Merge resistanceisfutile into master
This commit is contained in:
commit
85d6500daa
7 changed files with 91 additions and 3 deletions
|
@ -157,7 +157,10 @@ def github_oauth_callback():
|
||||||
if error:
|
if error:
|
||||||
return render_ologin_error('GitHub', error)
|
return render_ologin_error('GitHub', error)
|
||||||
|
|
||||||
|
# Exchange the OAuth code.
|
||||||
token = exchange_code_for_token(request.args.get('code'), github_login)
|
token = exchange_code_for_token(request.args.get('code'), github_login)
|
||||||
|
|
||||||
|
# Retrieve the user's information.
|
||||||
user_data = get_user(github_login, token)
|
user_data = get_user(github_login, token)
|
||||||
if not user_data or not 'login' in user_data:
|
if not user_data or not 'login' in user_data:
|
||||||
return render_ologin_error('GitHub')
|
return render_ologin_error('GitHub')
|
||||||
|
@ -172,16 +175,35 @@ def github_oauth_callback():
|
||||||
token_param = {
|
token_param = {
|
||||||
'access_token': token,
|
'access_token': token,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Retrieve the user's orgnizations (if organization filtering is turned on)
|
||||||
|
if github_login.allowed_organizations() is not None:
|
||||||
|
get_orgs = client.get(github_login.orgs_endpoint(), params=token_param,
|
||||||
|
headers={'Accept': 'application/vnd.github.moondragon+json'})
|
||||||
|
|
||||||
|
organizations = set([org.get('login') for org in get_orgs.json()])
|
||||||
|
if not (organizations & set(github_login.allowed_organizations())):
|
||||||
|
err = """You are not a member of an allowed GitHub organization.
|
||||||
|
Please contact your system administrator if you believe this is in error."""
|
||||||
|
return render_ologin_error('GitHub', err)
|
||||||
|
|
||||||
|
# Find the e-mail address for the user: we will accept any email, but we prefer the primary
|
||||||
get_email = client.get(github_login.email_endpoint(), params=token_param,
|
get_email = client.get(github_login.email_endpoint(), params=token_param,
|
||||||
headers=v3_media_type)
|
headers=v3_media_type)
|
||||||
|
|
||||||
# We will accept any email, but we prefer the primary
|
|
||||||
found_email = None
|
found_email = None
|
||||||
for user_email in get_email.json():
|
for user_email in get_email.json():
|
||||||
|
if not user_email['verified']:
|
||||||
|
continue
|
||||||
|
|
||||||
found_email = user_email['email']
|
found_email = user_email['email']
|
||||||
if user_email['primary']:
|
if user_email['primary']:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if found_email is None:
|
||||||
|
err = 'There is no verified e-mail address attached to the GitHub account.'
|
||||||
|
return render_ologin_error('GitHub', err)
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
'service_username': username
|
'service_username': username
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,6 +278,15 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.config-list-field-element input {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-list-field-element .item-delete {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.config-list-field-element input {
|
.config-list-field-element input {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,6 +406,28 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Organization Filtering:</td>
|
||||||
|
<td>
|
||||||
|
<div class="co-checkbox">
|
||||||
|
<input id="ftghlof" type="checkbox"
|
||||||
|
ng-model="config.GITHUB_LOGIN_CONFIG.ORG_RESTRICT">
|
||||||
|
<label for="ftghlof">Restrict By Organization Membership</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-text" style="margin-bottom: 20px;">
|
||||||
|
If enabled, only members of specified GitHub
|
||||||
|
<span ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">Enterprise</span> organizations will be allowed to login via GitHub
|
||||||
|
<span ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">Enterprise</span>.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="config-list-field"
|
||||||
|
item-title="Organization ID"
|
||||||
|
binding="config.GITHUB_LOGIN_CONFIG.ALLOWED_ORGANIZATIONS"
|
||||||
|
ng-if="config.GITHUB_LOGIN_CONFIG.ORG_RESTRICT">
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- /GitHub Authentication -->
|
</div> <!-- /GitHub Authentication -->
|
||||||
|
|
|
@ -140,7 +140,7 @@
|
||||||
$scope.showSuperuserPanel = function() {
|
$scope.showSuperuserPanel = function() {
|
||||||
$('#setupModal').modal('hide');
|
$('#setupModal').modal('hide');
|
||||||
var prefix = $scope.hasSSL ? 'https' : 'http';
|
var prefix = $scope.hasSSL ? 'https' : 'http';
|
||||||
var hostname = $scope.hostname;
|
var hostname = $scope.hostname || document.location.hostname;
|
||||||
window.location = prefix + '://' + hostname + '/superuser';
|
window.location = prefix + '://' + hostname + '/superuser';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,10 @@ angular.module('quay').factory('KeyService', ['$location', 'Config', function($l
|
||||||
keyService['githubTriggerAuthorizeUrl'] = oauth['GITHUB_TRIGGER_CONFIG']['AUTHORIZE_ENDPOINT'];
|
keyService['githubTriggerAuthorizeUrl'] = oauth['GITHUB_TRIGGER_CONFIG']['AUTHORIZE_ENDPOINT'];
|
||||||
|
|
||||||
keyService['githubLoginScope'] = 'user:email';
|
keyService['githubLoginScope'] = 'user:email';
|
||||||
|
if (oauth['GITHUB_LOGIN_CONFIG']['ORG_RESTRICT']) {
|
||||||
|
keyService['githubLoginScope'] += ',read:org';
|
||||||
|
}
|
||||||
|
|
||||||
keyService['googleLoginScope'] = 'openid email';
|
keyService['googleLoginScope'] = 'openid email';
|
||||||
|
|
||||||
keyService.isEnterprise = function(service) {
|
keyService.isEnterprise = function(service) {
|
||||||
|
|
|
@ -122,12 +122,20 @@ def _validate_github_with_key(config_key, config):
|
||||||
if not github_config.get('CLIENT_SECRET'):
|
if not github_config.get('CLIENT_SECRET'):
|
||||||
raise Exception('Missing Client Secret')
|
raise Exception('Missing Client Secret')
|
||||||
|
|
||||||
|
if github_config.get('ORG_RESTRICT') and not github_config.get('ALLOWED_ORGANIZATIONS'):
|
||||||
|
raise Exception('Organization restriction must have at least one allowed organization')
|
||||||
|
|
||||||
client = app.config['HTTPCLIENT']
|
client = app.config['HTTPCLIENT']
|
||||||
oauth = GithubOAuthConfig(config, config_key)
|
oauth = GithubOAuthConfig(config, config_key)
|
||||||
result = oauth.validate_client_id_and_secret(client)
|
result = oauth.validate_client_id_and_secret(client)
|
||||||
if not result:
|
if not result:
|
||||||
raise Exception('Invalid client id or client secret')
|
raise Exception('Invalid client id or client secret')
|
||||||
|
|
||||||
|
if github_config.get('ALLOWED_ORGANIZATIONS'):
|
||||||
|
for org_id in github_config.get('ALLOWED_ORGANIZATIONS'):
|
||||||
|
if not oauth.validate_organization(org_id, client):
|
||||||
|
raise Exception('Invalid organization: %s' % org_id)
|
||||||
|
|
||||||
|
|
||||||
def _validate_google_login(config):
|
def _validate_google_login(config):
|
||||||
""" Validates the Google Login client ID and secret. """
|
""" Validates the Google Login client ID and secret. """
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import urlparse
|
import urlparse
|
||||||
|
import github
|
||||||
|
|
||||||
class OAuthConfig(object):
|
class OAuthConfig(object):
|
||||||
def __init__(self, config, key_name):
|
def __init__(self, config, key_name):
|
||||||
|
@ -40,6 +41,12 @@ class GithubOAuthConfig(OAuthConfig):
|
||||||
def service_name(self):
|
def service_name(self):
|
||||||
return 'GitHub'
|
return 'GitHub'
|
||||||
|
|
||||||
|
def allowed_organizations(self):
|
||||||
|
if not self.config.get('ORG_RESTRICT', False):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.config.get('ALLOWED_ORGANIZATIONS', None)
|
||||||
|
|
||||||
def _endpoint(self):
|
def _endpoint(self):
|
||||||
endpoint = self.config.get('GITHUB_ENDPOINT', 'https://github.com')
|
endpoint = self.config.get('GITHUB_ENDPOINT', 'https://github.com')
|
||||||
if not endpoint.endswith('/'):
|
if not endpoint.endswith('/'):
|
||||||
|
@ -66,6 +73,10 @@ class GithubOAuthConfig(OAuthConfig):
|
||||||
api_endpoint = self._api_endpoint()
|
api_endpoint = self._api_endpoint()
|
||||||
return self._get_url(api_endpoint, 'user/emails')
|
return self._get_url(api_endpoint, 'user/emails')
|
||||||
|
|
||||||
|
def orgs_endpoint(self):
|
||||||
|
api_endpoint = self._api_endpoint()
|
||||||
|
return self._get_url(api_endpoint, 'user/orgs')
|
||||||
|
|
||||||
def validate_client_id_and_secret(self, http_client):
|
def validate_client_id_and_secret(self, http_client):
|
||||||
# First: Verify that the github endpoint is actually Github by checking for the
|
# First: Verify that the github endpoint is actually Github by checking for the
|
||||||
# X-GitHub-Request-Id here.
|
# X-GitHub-Request-Id here.
|
||||||
|
@ -91,11 +102,23 @@ class GithubOAuthConfig(OAuthConfig):
|
||||||
timeout=5)
|
timeout=5)
|
||||||
return result.status_code == 404
|
return result.status_code == 404
|
||||||
|
|
||||||
|
def validate_organization(self, organization_id, http_client):
|
||||||
|
api_endpoint = self._api_endpoint()
|
||||||
|
org_endpoint = self._get_url(api_endpoint, 'orgs/%s' % organization_id)
|
||||||
|
|
||||||
|
result = http_client.get(org_endpoint,
|
||||||
|
headers={'Accept': 'application/vnd.github.moondragon+json'},
|
||||||
|
timeout=5)
|
||||||
|
|
||||||
|
return result.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def get_public_config(self):
|
def get_public_config(self):
|
||||||
return {
|
return {
|
||||||
'CLIENT_ID': self.client_id(),
|
'CLIENT_ID': self.client_id(),
|
||||||
'AUTHORIZE_ENDPOINT': self.authorize_endpoint(),
|
'AUTHORIZE_ENDPOINT': self.authorize_endpoint(),
|
||||||
'GITHUB_ENDPOINT': self._endpoint()
|
'GITHUB_ENDPOINT': self._endpoint(),
|
||||||
|
'ORG_RESTRICT': self.config.get('ORG_RESTRICT', False)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in a new issue