From 913b3e472f71ddabcdc78629198b0b06c1522e42 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 15 Sep 2014 12:01:02 -0400 Subject: [PATCH] Add ability to detach external login services --- data/model/legacy.py | 10 ++++++++++ endpoints/api/user.py | 13 +++++++++++++ static/js/controllers.js | 12 ++++++++++++ static/partials/user-admin.html | 8 ++++++++ test/test_api_security.py | 20 +++++++++++++++++++- 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/data/model/legacy.py b/data/model/legacy.py index 92a130dca..d9b2079d8 100644 --- a/data/model/legacy.py +++ b/data/model/legacy.py @@ -1830,6 +1830,16 @@ def get_active_users(): def get_active_user_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): user.delete_instance(recursive=True, delete_nullable=True) diff --git a/endpoints/api/user.py b/endpoints/api/user.py index ddf05aafa..43a08508a 100644 --- a/endpoints/api/user.py +++ b/endpoints/api/user.py @@ -408,6 +408,19 @@ class Signout(ApiResource): return {'success': True} + +@resource('/v1/detachexternal/') +@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") @internal_only class Recovery(ApiResource): diff --git a/static/js/controllers.js b/static/js/controllers.js index f259ead68..7010dc4eb 100644 --- a/static/js/controllers.js +++ b/static/js/controllers.js @@ -1781,6 +1781,18 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use 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) { diff --git a/static/partials/user-admin.html b/static/partials/user-admin.html index c4d3b94a0..7d6dd8dfb 100644 --- a/static/partials/user-admin.html +++ b/static/partials/user-admin.html @@ -177,10 +177,14 @@
Account attached to Github Account +
@@ -197,10 +201,14 @@
{{ googleLogin }} +
Account attached to Google Account +
diff --git a/test/test_api_security.py b/test/test_api_security.py index 3c33ad712..1f4a18a84 100644 --- a/test/test_api_security.py +++ b/test/test_api_security.py @@ -24,7 +24,7 @@ from endpoints.api.repoemail import RepositoryAuthorizedEmail from endpoints.api.repositorynotification import RepositoryNotification, RepositoryNotificationList from endpoints.api.user import (PrivateRepositories, ConvertToOrganization, Recovery, Signout, Signin, User, UserAuthorizationList, UserAuthorization, UserNotification, - VerifyUser) + VerifyUser, DetachExternal) from endpoints.api.repotoken import RepositoryToken, RepositoryTokenList from endpoints.api.prototype import PermissionPrototype, PermissionPrototypeList 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'}) +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): def setUp(self): ApiTestCase.setUp(self)