Merge branch 'master' of bitbucket.org:yackob03/quay

This commit is contained in:
Jake Moshenko 2014-09-15 15:59:18 -04:00
commit 2b59a0cbe1
11 changed files with 89 additions and 5 deletions

View file

@ -135,8 +135,15 @@ def process_token(auth):
logger.warning('Invalid token format: %s' % auth) logger.warning('Invalid token format: %s' % auth)
abort(401, message='Invalid token format: %(auth)s', issue='invalid-auth-token', auth=auth) abort(401, message='Invalid token format: %(auth)s', issue='invalid-auth-token', auth=auth)
token_vals = {val[0]: val[1] for val in def safe_get(lst, index, default_value):
try:
return lst[index]
except IndexError:
return default_value
token_vals = {val[0]: safe_get(val, 1, '') for val in
(detail.split('=') for detail in token_details)} (detail.split('=') for detail in token_details)}
if 'signature' not in token_vals: if 'signature' not in token_vals:
logger.warning('Token does not contain signature: %s' % auth) logger.warning('Token does not contain signature: %s' % auth)
abort(401, message='Token does not contain a valid signature: %(auth)s', abort(401, message='Token does not contain a valid signature: %(auth)s',

View file

@ -1830,6 +1830,16 @@ def get_active_users():
def get_active_user_count(): def get_active_user_count():
return get_active_users().count() return get_active_users().count()
def detach_external_login(user, service_name):
try:
service = LoginService.get(name = service_name)
except LoginService.DoesNotExist:
return
FederatedLogin.delete().where(FederatedLogin.user == user,
FederatedLogin.service == service).execute()
def delete_user(user): def delete_user(user):
user.delete_instance(recursive=True, delete_nullable=True) user.delete_instance(recursive=True, delete_nullable=True)

View file

@ -46,7 +46,7 @@ class DatabaseAuthorizationProvider(AuthorizationProvider):
def validate_redirect_uri(self, client_id, redirect_uri): def validate_redirect_uri(self, client_id, redirect_uri):
try: try:
app = OAuthApplication.get(client_id=client_id) app = OAuthApplication.get(client_id=client_id)
if app.redirect_uri and redirect_uri.startswith(app.redirect_uri): if app.redirect_uri and redirect_uri and redirect_uri.startswith(app.redirect_uri):
return True return True
return False return False
except OAuthApplication.DoesNotExist: except OAuthApplication.DoesNotExist:

View file

@ -408,6 +408,19 @@ class Signout(ApiResource):
return {'success': True} return {'success': True}
@resource('/v1/detachexternal/<servicename>')
@internal_only
class DetachExternal(ApiResource):
""" Resource for detaching an external login. """
@require_user_admin
@nickname('detachExternalLogin')
def post(self, servicename):
""" Request that the current user be detached from the external login service. """
model.detach_external_login(get_authenticated_user(), servicename)
return {'success': True}
@resource("/v1/recovery") @resource("/v1/recovery")
@internal_only @internal_only
class Recovery(ApiResource): class Recovery(ApiResource):

View file

@ -66,6 +66,9 @@ def generate_headers(role='read'):
@index.route('/users/', methods=['POST']) @index.route('/users/', methods=['POST'])
def create_user(): def create_user():
user_data = request.get_json() user_data = request.get_json()
if not 'username' in user_data:
abort(400, 'Missing username')
username = user_data['username'] username = user_data['username']
password = user_data.get('password', '') password = user_data.get('password', '')

View file

@ -556,7 +556,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
// If an error occurred, report it and done. // If an error occurred, report it and done.
if (ping < 0) { if (ping < 0) {
cached['pings'] = [-1]; cached['pings'] = [-1];
invokeCallback($scope, pings, callback); invokeCallback($scope, [-1], callback);
return; return;
} }
@ -1518,7 +1518,12 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
}; };
notificationService.getPage = function(notification) { notificationService.getPage = function(notification) {
var page = notificationKinds[notification['kind']]['page']; var kindInfo = notificationKinds[notification['kind']];
if (!kindInfo) {
return null;
}
var page = kindInfo['page'];
if (typeof page != 'string') { if (typeof page != 'string') {
page = page(notification['metadata']); page = page(notification['metadata']);
} }

View file

@ -1781,6 +1781,18 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
UIService.showFormError('#changePasswordForm', result); UIService.showFormError('#changePasswordForm', result);
}); });
}; };
$scope.detachExternalLogin = function(kind) {
var params = {
'servicename': kind
};
ApiService.detachExternalLogin(null, params).then(function() {
$scope.hasGithubLogin = false;
$scope.hasGoogleLogin = false;
UserService.load();
}, ApiService.errorDisplay('Count not detach service'));
};
} }
function ImageViewCtrl($scope, $routeParams, $rootScope, $timeout, ApiService, ImageMetadataService) { function ImageViewCtrl($scope, $routeParams, $rootScope, $timeout, ApiService, ImageMetadataService) {

View file

@ -177,10 +177,14 @@
<div ng-show="hasGithubLogin && githubLogin" class="lead col-md-8"> <div ng-show="hasGithubLogin && githubLogin" class="lead col-md-8">
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i> <i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
<b><a href="https://github.com/{{githubLogin}}" target="_blank">{{githubLogin}}</a></b> <b><a href="https://github.com/{{githubLogin}}" target="_blank">{{githubLogin}}</a></b>
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('github')"></span>
</div> </div>
<div ng-show="hasGithubLogin && !githubLogin" class="lead col-md-8"> <div ng-show="hasGithubLogin && !githubLogin" class="lead col-md-8">
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i> <i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
Account attached to Github Account Account attached to Github Account
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('github')"></span>
</div> </div>
<div ng-show="!hasGithubLogin" class="col-md-4"> <div ng-show="!hasGithubLogin" class="col-md-4">
<span class="external-login-button" provider="github" action="attach"></span> <span class="external-login-button" provider="github" action="attach"></span>
@ -197,10 +201,14 @@
<div ng-show="hasGoogleLogin && googleLogin" class="lead col-md-8"> <div ng-show="hasGoogleLogin && googleLogin" class="lead col-md-8">
<i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i> <i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i>
<b>{{ googleLogin }}</b> <b>{{ googleLogin }}</b>
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('google')"></span>
</div> </div>
<div ng-show="hasGoogleLogin && !googleLogin" class="lead col-md-8"> <div ng-show="hasGoogleLogin && !googleLogin" class="lead col-md-8">
<i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i> <i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i>
Account attached to Google Account Account attached to Google Account
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('google')"></span>
</div> </div>
<div ng-show="!hasGoogleLogin" class="col-md-4"> <div ng-show="!hasGoogleLogin" class="col-md-4">
<span class="external-login-button" provider="google" action="attach"></span> <span class="external-login-button" provider="google" action="attach"></span>

View file

@ -205,6 +205,9 @@ class _CloudStorage(BaseStorage):
path = self._init_path(path) path = self._init_path(path)
key = self._key_class(self._cloud_bucket, path) key = self._key_class(self._cloud_bucket, path)
k = self._cloud_bucket.lookup(key) k = self._cloud_bucket.lookup(key)
if k is None:
raise IOError('No such key: \'{0}\''.format(path))
return k.etag[1:-1][:7] return k.etag[1:-1][:7]

View file

@ -24,7 +24,7 @@ from endpoints.api.repoemail import RepositoryAuthorizedEmail
from endpoints.api.repositorynotification import RepositoryNotification, RepositoryNotificationList from endpoints.api.repositorynotification import RepositoryNotification, RepositoryNotificationList
from endpoints.api.user import (PrivateRepositories, ConvertToOrganization, Recovery, Signout, from endpoints.api.user import (PrivateRepositories, ConvertToOrganization, Recovery, Signout,
Signin, User, UserAuthorizationList, UserAuthorization, UserNotification, Signin, User, UserAuthorizationList, UserAuthorization, UserNotification,
VerifyUser) VerifyUser, DetachExternal)
from endpoints.api.repotoken import RepositoryToken, RepositoryTokenList from endpoints.api.repotoken import RepositoryToken, RepositoryTokenList
from endpoints.api.prototype import PermissionPrototype, PermissionPrototypeList from endpoints.api.prototype import PermissionPrototype, PermissionPrototypeList
from endpoints.api.logs import UserLogs, OrgLogs, RepositoryLogs from endpoints.api.logs import UserLogs, OrgLogs, RepositoryLogs
@ -435,6 +435,24 @@ class TestSignin(ApiTestCase):
self._run_test('POST', 403, 'devtable', {u'username': 'E9RY', u'password': 'LQ0N'}) self._run_test('POST', 403, 'devtable', {u'username': 'E9RY', u'password': 'LQ0N'})
class TestDetachExternal(ApiTestCase):
def setUp(self):
ApiTestCase.setUp(self)
self._set_url(DetachExternal, servicename='someservice')
def test_post_anonymous(self):
self._run_test('POST', 401, None, {})
def test_post_freshuser(self):
self._run_test('POST', 200, 'freshuser', {})
def test_post_reader(self):
self._run_test('POST', 200, 'reader', {})
def test_post_devtable(self):
self._run_test('POST', 200, 'devtable', {})
class TestVerifyUser(ApiTestCase): class TestVerifyUser(ApiTestCase):
def setUp(self): def setUp(self):
ApiTestCase.setUp(self) ApiTestCase.setUp(self)

View file

@ -15,4 +15,9 @@ for index, command in reversed(list(enumerate(parsed_dockerfile.commands))):
parsed_dockerfile.commands.insert(new_command_index, env_command) parsed_dockerfile.commands.insert(new_command_index, env_command)
break break
image_and_tag_tuple = parsed_dockerfile.get_image_and_tag()
print image_and_tag_tuple
if image_and_tag_tuple is None or image_and_tag_tuple[0] is None:
raise Exception('Missing FROM command in Dockerfile')
print serialize_dockerfile(parsed_dockerfile) print serialize_dockerfile(parsed_dockerfile)