def test_add_domain(setup): # type: (SetupTest) -> None with setup.transaction(): setup.add_user_to_group("*****@*****.**", "some-group") mock_ui = MagicMock() usecase = setup.usecase_factory.create_create_service_account_usecase("*****@*****.**", mock_ui) usecase.create_service_account("service", "some-group", "machine-set", "description") service = ServiceAccount.get(setup.session, name="*****@*****.**") assert service is not None assert service.machine_set == "machine-set" assert service.description == "description" assert ServiceAccount.get(setup.session, name="service") is None
def test_create(setup): # type: (SetupTest) -> None with setup.transaction(): setup.add_user_to_group("*****@*****.**", "some-group") setup.add_user_to_group("*****@*****.**", "other-group") run_ctl( setup, "service_account", "--actor", "*****@*****.**", "create", "*****@*****.**", "some-group", "foo +bar -(org)", "this is a service account.\n\n it is for testing", ) service_account = ServiceAccount.get(setup.session, name="*****@*****.**") assert service_account is not None assert service_account.user.name == "*****@*****.**" assert service_account.machine_set == "foo +bar -(org)" assert service_account.description == "this is a service account.\n\n it is for testing" group = Group.get(setup.session, name="some-group") assert group assert get_service_accounts(setup.session, group) == [service_account] # If the account already exists, creating it again returns an error and does nothing. with pytest.raises(SystemExit): run_ctl( setup, "service_account", "--actor", "*****@*****.**", "create", "*****@*****.**", "other-group", "foo", "another test", ) service_account = ServiceAccount.get(setup.session, name="*****@*****.**") assert service_account is not None assert service_account.machine_set == "foo +bar -(org)" assert service_account.description == "this is a service account.\n\n it is for testing" group = Group.get(setup.session, name="some-group") assert group assert get_service_accounts(setup.session, group) == [service_account]
def test_service_accounts(session, standard_graph, users, http_client, base_url): # noqa: F811 api_url = url(base_url, "/service_accounts") resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" assert sorted(body["data"]["service_accounts"]) == sorted( [u.name for u in itervalues(users) if u.role_user] + ["*****@*****.**"] ) # Retrieve a single service account and check its metadata. api_url = url(base_url, "/service_accounts/[email protected]") resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" data = body["data"]["user"] assert "service_account" in data assert data["service_account"]["description"] == "some service account" assert data["service_account"]["machine_set"] == "some machines" assert data["service_account"]["owner"] == "team-sre" assert body["data"]["permissions"] == [] # Delegate a permission to the service account and check for it. service_account = ServiceAccount.get(session, name="*****@*****.**") permission = get_permission(session, "team-sre") grant_permission_to_service_account(session, service_account, permission, "*") standard_graph.update_from_db(session) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" perms = body["data"]["permissions"] assert perms[0]["permission"] == "team-sre" assert perms[0]["argument"] == "*"
def post(self, group_id=None, name=None, account_id=None, accountname=None, mapping_id=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() mapping = ServiceAccountPermissionMap.get(self.session, mapping_id) if not mapping: return self.notfound() permission = mapping.permission argument = mapping.argument mapping.delete(self.session) Counter.incr(self.session, "updates") self.session.commit() AuditLog.log(self.session, self.current_user.id, "revoke_permission", "Revoked permission with argument: {}".format(argument), on_permission_id=permission.id, on_group_id=group.id, on_user_id=service_account.user.id) return self.redirect("/groups/{}/service/{}?refresh=yes".format( group.name, service_account.user.username))
def test_success(setup): # type: (SetupTest) -> None with setup.transaction(): setup.grant_permission_to_group(USER_ADMIN, "", "admins") setup.add_user_to_group("*****@*****.**", "admins") setup.create_user("*****@*****.**") setup.create_group("some-group") mock_ui = MagicMock() usecase = setup.usecase_factory.create_convert_user_to_service_account_usecase( "*****@*****.**", mock_ui) usecase.convert_user_to_service_account("*****@*****.**", "some-group") assert mock_ui.mock_calls == [ call.converted_user_to_service_account("*****@*****.**", "some-group") ] # Check the User after the conversion service_account_user = User.get(setup.session, name="*****@*****.**") assert service_account_user assert service_account_user.is_service_account assert service_account_user.enabled # Check the ServiceAccount that should have been created service_account = ServiceAccount.get(setup.session, name="*****@*****.**") assert service_account assert service_account.description == "" assert service_account.machine_set == "" assert service_account.user_id == service_account_user.id # Check that the ServiceAccount is owned by the correct Group group = Group.get(setup.session, name="some-group") group_service_account = GroupServiceAccount.get( setup.session, service_account_id=service_account.id) assert group assert group_service_account assert group_service_account.group_id == group.id
def test_success(setup): # type: (SetupTest) -> None with setup.transaction(): setup.add_user_to_group("*****@*****.**", "some-group") mock_ui = MagicMock() usecase = setup.usecase_factory.create_create_service_account_usecase("*****@*****.**", mock_ui) usecase.create_service_account( "*****@*****.**", "some-group", "machine-set", "description" ) assert mock_ui.mock_calls == [ call.created_service_account("*****@*****.**", "some-group") ] # Check the User and ServiceAccount that were created. user = User.get(setup.session, name="*****@*****.**") assert user is not None assert user.is_service_account assert user.enabled service = ServiceAccount.get(setup.session, name="*****@*****.**") assert service is not None assert service.machine_set == "machine-set" assert service.description == "description" # Check that the ServiceAccount is owned by the correct Group. group = Group.get(setup.session, name="some-group") assert group is not None linkage = GroupServiceAccount.get(setup.session, service_account_id=service.id) assert linkage is not None assert linkage.group_id == group.id
def post(self, user_id=None, name=None): service_account = ServiceAccount.get(self.session, user_id, name) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() form = self.get_form() if not form.validate(): return self.render("service-account-enable.html", form=form, user=service_account.user, alerts=self.get_form_alerts(form.errors)) owner = Group.get(self.session, name=form.data["owner"]) if owner is None: form.owner.errors.append("Group not found.") return self.render("service-account-enable.html", form=form, user=service_account.user, alerts=self.get_form_alerts(form.errors)) enable_service_account(self.session, self.current_user, service_account, owner) return self.redirect("/groups/{}/service/{}?refresh=yes".format( owner.name, service_account.user.username))
def enable_service_account(self, name): # type: (str) -> None service_account = SQLServiceAccount.get(self.session, name=name) if not service_account: raise ServiceAccountNotFoundException(name) service_account.user.enabled = True
def post(self, group_id=None, name=None, account_id=None, accountname=None, mapping_id=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() mapping = ServiceAccountPermissionMap.get(self.session, mapping_id) if not mapping: return self.notfound() permission = mapping.permission argument = mapping.argument mapping.delete(self.session) Counter.incr(self.session, "updates") self.session.commit() AuditLog.log( self.session, self.current_user.id, "revoke_permission", "Revoked permission with argument: {}".format(argument), on_permission_id=permission.id, on_group_id=group.id, on_user_id=service_account.user.id, ) return self.redirect( "/groups/{}/service/{}?refresh=yes".format(group.name, service_account.user.username) )
def get(self, *args: Any, **kwargs: Any) -> None: name = self.get_path_argument("name") accountname = self.get_path_argument("accountname") self.handle_refresh() group = Group.get(self.session, name=name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, name=accountname) if not service_account: return self.notfound() # We don't need the group to be valid to find the service account, but ensure that the # group is the owner of the service account so that we don't generate confusing URLs and # broken information on the view page. if service_account.owner.group_id != group.id: return self.notfound() user = service_account.user self.render( "service-account.html", service_account=service_account, group=group, user=user, **get_user_view_template_vars(self.session, self.current_user, user, self.graph) )
def test_create_as_service_account(setup): """Test that a service account can create another service account.""" with setup.transaction(): setup.create_group("some-group") setup.create_service_account("*****@*****.**", "another-group") setup.grant_permission_to_service_account(USER_ADMIN, "", "*****@*****.**") run_ctl( setup, "service_account", "--actor", "*****@*****.**", "create", "*****@*****.**", "some-group", "foo +bar -(org)", "this is a service account.\n\n it is for testing", ) service_account = ServiceAccount.get(setup.session, name="*****@*****.**") assert service_account is not None assert service_account.user.name == "*****@*****.**" assert service_account.machine_set == "foo +bar -(org)" assert service_account.description == "this is a service account.\n\n it is for testing" group = Group.get(setup.session, name="some-group") assert get_service_accounts(setup.session, group) == [service_account]
def post(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() if not can_manage_service_account(self.session, service_account, self.current_user): return self.forbidden() form = ServiceAccountEditForm(self.request.arguments, obj=service_account) if not form.validate(): return self.render( "service-account-edit.html", service_account=service_account, group=group, form=form, alerts=self.get_form_alerts(form.errors) ) try: edit_service_account(self.session, self.current_user, service_account, form.data["description"], form.data["machine_set"]) except BadMachineSet as e: form.machine_set.errors.append(str(e)) return self.render( "service-account-edit.html", service_account=service_account, group=group, form=form, alerts=self.get_form_alerts(form.errors) ) return self.redirect("/groups/{}/service/{}".format( group.name, service_account.user.username))
def post(self, user_id=None, name=None): service_account = ServiceAccount.get(self.session, user_id, name) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() form = self.get_form() if not form.validate(): return self.render( "service-account-enable.html", form=form, user=service_account.user, alerts=self.get_form_alerts(form.errors), ) owner = Group.get(self.session, name=form.data["owner"]) if owner is None: form.owner.errors.append("Group not found.") return self.render( "service-account-enable.html", form=form, user=service_account.user, alerts=self.get_form_alerts(form.errors), ) enable_service_account(self.session, self.current_user, service_account, owner) return self.redirect( "/groups/{}/service/{}?refresh=yes".format(owner.name, service_account.user.username) )
def enable_service_account(self, name): # type: (str) -> None service_account = SQLServiceAccount.get(self.session, name=name) if not service_account: raise ServiceAccountNotFoundException(name) service_account.user.enabled = True
def service_account_command(args, settings, session_factory): # type: (Namespace, CtlSettings, SessionFactory) -> None session = session_factory.create_session() actor_user = User.get(session, name=args.actor_name) if not actor_user: logging.fatal('Actor user "{}" is not a valid Grouper user'.format( args.actor_name)) return if args.subcommand == "create": name = args.name if ServiceAccount.get(session, name=name): logging.info("{}: Already exists. Doing nothing.".format(name)) return owner_group = Group.get(session, name=args.owner_group) if not owner_group: logging.fatal('Owner group "{}" does not exist.'.format( args.owner_group)) return logging.info("{}: No such service account, creating...".format(name)) description = args.description machine_set = args.machine_set create_service_account(session, actor_user, name, description, machine_set, owner_group) return
def test_grant_permission(session, standard_graph, groups, permissions): grant_permission(groups["sad-team"], permissions["ssh"], argument="host +other-host") with pytest.raises(AssertionError): grant_permission(groups["sad-team"], permissions["ssh"], argument="question?") account = ServiceAccount.get(session, name="*****@*****.**") grant_permission_to_service_account(session, account, permissions["ssh"], argument="*") with pytest.raises(AssertionError): grant_permission_to_service_account( session, account, permissions["ssh"], argument="question?")
def get(self, user_id=None, name=None): service_account = ServiceAccount.get(self.session, user_id, name) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() form = self.get_form() return self.render("service-account-enable.html", form=form, user=service_account.user)
def test_grant_permission(session, standard_graph, groups, permissions): # noqa: F811 grant_permission(groups["sad-team"], permissions["ssh"], argument="host +other-host") with pytest.raises(AssertionError): grant_permission(groups["sad-team"], permissions["ssh"], argument="question?") account = ServiceAccount.get(session, name="*****@*****.**") grant_permission_to_service_account(session, account, permissions["ssh"], argument="*") with pytest.raises(AssertionError): grant_permission_to_service_account( session, account, permissions["ssh"], argument="question?" )
def post(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() grantable = group.my_permissions() form = self.get_form(grantable) if not form.validate(): return self.render( "service-account-permission-grant.html", form=form, user=user, group=group, alerts=self.get_form_alerts(form.errors) ) permission = Permission.get(self.session, form.data["permission"]) if not permission: return self.notfound() allowed = False for perm in grantable: if perm[1] == permission.name: if matches_glob(perm[3], form.data["argument"]): allowed = True break if not allowed: form.argument.errors.append( "The group {} does not have that permission".format(group.name)) return self.render( "service-account-permission-grant.html", form=form, user=user, group=group, alerts=self.get_form_alerts(form.errors) ) try: grant_permission_to_service_account( self.session, service_account, permission, form.data["argument"]) except IntegrityError: self.session.rollback() return self.render( "service-account-permission-grant.html", form=form, user=user, alerts=self.get_form_alerts(form.errors) ) AuditLog.log(self.session, self.current_user.id, "grant_permission", "Granted permission with argument: {}".format(form.data["argument"]), on_permission_id=permission.id, on_group_id=group.id, on_user_id=service_account.user.id) return self.redirect("/groups/{}/service/{}?refresh=yes".format( group.name, service_account.user.username))
def disable_service_account(self, service_account): # type: (str) -> None service_obj = ServiceAccount.get(self.session, name=service_account) assert service_obj service_obj.user.enabled = False service_obj.owner.delete(self.session) permissions = self.session.query( ServiceAccountPermissionMap).filter_by( service_account_id=service_obj.id) for permission in permissions: permission.delete(self.session)
def disable_service_account(self, service_account): # type: (str) -> None service_obj = ServiceAccount.get(self.session, name=service_account) assert service_obj service_obj.user.enabled = False service_obj.owner.delete(self.session) permissions = self.session.query(ServiceAccountPermissionMap).filter_by( service_account_id=service_obj.id ) for permission in permissions: permission.delete(self.session)
def get(self, user_id=None, name=None): service_account = ServiceAccount.get(self.session, user_id, name) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() form = self.get_form() return self.render("service-account-enable.html", form=form, user=service_account.user)
def get(self, group_id=None, name=None, account_id=None, accountname=None): self.handle_refresh() group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user self.render( "service-account.html", service_account=service_account, group=group, user=user, **get_user_view_template_vars(self.session, self.current_user, user, self.graph) )
def post(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() disable_service_account(self.session, self.current_user, service_account) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def grant_permission_to_service_account(self, permission, argument, service): # type: (str, str, str) -> None sql_service = ServiceAccount.get(self.session, name=service) if not sql_service or not sql_service.user.enabled: raise ServiceAccountNotFoundException(service) sql_permission = Permission.get(self.session, name=permission) if not sql_permission: raise PermissionNotFoundException(permission) mapping = ServiceAccountPermissionMap( permission_id=sql_permission.id, service_account_id=sql_service.id, argument=argument) mapping.add(self.session)
def get(self, *args: Any, **kwargs: Any) -> None: name = self.get_path_argument("name") service_account = ServiceAccount.get(self.session, name=name) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() form = self.get_form() return self.render("service-account-enable.html", form=form, user=service_account.user)
def post(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() if not can_manage_service_account(self.session, service_account, self.current_user): return self.forbidden() form = ServiceAccountEditForm(self.request.arguments, obj=service_account) if not form.validate(): return self.render( "service-account-edit.html", service_account=service_account, group=group, form=form, alerts=self.get_form_alerts(form.errors), ) try: edit_service_account( self.session, self.current_user, service_account, form.data["description"], form.data["machine_set"], ) except BadMachineSet as e: form.machine_set.errors.append(str(e)) return self.render( "service-account-edit.html", service_account=service_account, group=group, form=form, alerts=self.get_form_alerts(form.errors), ) return self.redirect("/groups/{}/service/{}".format( group.name, service_account.user.username))
def get(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() if not can_manage_service_account(self.session, service_account, self.current_user): return self.forbidden() form = ServiceAccountEditForm(obj=service_account) self.render( "service-account-edit.html", service_account=service_account, group=group, form=form )
def get(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() form = self.get_form(group.my_permissions()) return self.render( "service-account-permission-grant.html", form=form, user=user, group=group )
def test_admin_can_create(setup): # type: (SetupTest) -> None with setup.transaction(): setup.create_group("some-group") setup.add_user_to_group("*****@*****.**", "admins") setup.grant_permission_to_group(USER_ADMIN, "", "admins") mock_ui = MagicMock() usecase = setup.usecase_factory.create_create_service_account_usecase("*****@*****.**", mock_ui) usecase.create_service_account("*****@*****.**", "some-group", "", "") assert mock_ui.mock_calls == [ call.created_service_account("*****@*****.**", "some-group") ] service = ServiceAccount.get(setup.session, name="*****@*****.**") assert service is not None assert service.machine_set == "" assert service.description == ""
def get(self, group_id=None, name=None, account_id=None, accountname=None): self.handle_refresh() group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user self.render("service-account.html", service_account=service_account, group=group, user=user, **get_user_view_template_vars(self.session, self.current_user, user, self.graph))
def assign_service_account_to_group(self, name, groupname): # type: (str, str) -> None service_account = SQLServiceAccount.get(self.session, name=name) if not service_account: raise ServiceAccountNotFoundException(name) group = Group.get(self.session, name=groupname) if not group: raise GroupNotFoundException(groupname) existing_relationship = GroupServiceAccount.get( self.session, service_account_id=service_account.id) if existing_relationship: existing_relationship.group_id = group.id else: group_service_account = GroupServiceAccount( group_id=group.id, service_account_id=service_account.id) group_service_account.add(self.session)
def post(self, *args: Any, **kwargs: Any) -> None: name = self.get_path_argument("name") accountname = self.get_path_argument("accountname") group = Group.get(self.session, name=name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, name=accountname) if not service_account: return self.notfound() if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() disable_service_account(self.session, self.current_user, service_account) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def get(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user if not self.check_access(self.session, self.current_user, service_account): return self.forbidden() form = self.get_form(group.my_permissions()) return self.render("service-account-permission-grant.html", form=form, user=user, group=group)
def get(self, *args: Any, **kwargs: Any) -> None: name = self.get_path_argument("name") accountname = self.get_path_argument("accountname") group = Group.get(self.session, name=name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, name=accountname) if not service_account: return self.notfound() if not can_manage_service_account(self.session, service_account, self.current_user): return self.forbidden() form = ServiceAccountEditForm(obj=service_account) self.render( "service-account-edit.html", service_account=service_account, group=group, form=form )
def get(self, group_id=None, name=None, account_id=None, accountname=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() if not can_manage_service_account(self.session, service_account, self.current_user): return self.forbidden() form = ServiceAccountEditForm(obj=service_account) self.render("service-account-edit.html", service_account=service_account, group=group, form=form)
def assign_service_account_to_group(self, name, groupname): # type: (str, str) -> None service_account = SQLServiceAccount.get(self.session, name=name) if not service_account: raise ServiceAccountNotFoundException(name) group = Group.get(self.session, name=groupname) if not group: raise GroupNotFoundException(groupname) existing_relationship = GroupServiceAccount.get( self.session, service_account_id=service_account.id ) if existing_relationship: existing_relationship.group_id = group.id else: group_service_account = GroupServiceAccount( group_id=group.id, service_account_id=service_account.id ) group_service_account.add(self.session)
def test_service_accounts(session, standard_graph, users, http_client, base_url): graph = standard_graph service_accounts = sorted([u.name for u in users.values() if u.role_user] + ["*****@*****.**"]) api_url = url(base_url, "/service_accounts") resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" assert sorted(body["data"]["service_accounts"]) == service_accounts # TODO: test cutoff # Retrieve a single service account and check its metadata. api_url = url(base_url, "/service_accounts/[email protected]") resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" data = body["data"]["user"] assert "service_account" in data assert data["service_account"]["description"] == "some service account" assert data["service_account"]["machine_set"] == "some machines" assert data["service_account"]["owner"] == "team-sre" assert body["data"]["permissions"] == [] # Delegate a permission to the service account and check for it. service_account = ServiceAccount.get(session, name="*****@*****.**") permission = Permission.get(session, name="team-sre") grant_permission_to_service_account(session, service_account, permission, "*") graph.update_from_db(session) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" permissions = body["data"]["permissions"] assert permissions[0]["permission"] == "team-sre" assert permissions[0]["argument"] == "*"
def test_create_invalid_actor(setup): # type: (SetupTest) -> None with setup.transaction(): setup.create_group("some-group") with pytest.raises(SystemExit): run_ctl( setup, "service_account", "--actor", "*****@*****.**", "create", "*****@*****.**", "some-group", "foo", "another test", ) assert ServiceAccount.get(setup.session, name="*****@*****.**") is None group = Group.get(setup.session, name="some-group") assert get_service_accounts(setup.session, group) == []
def service_account_command(args, settings, session_factory): # type: (Namespace, CtlSettings, SessionFactory) -> None session = session_factory.create_session() actor_user = User.get(session, name=args.actor_name) if not actor_user: logging.fatal('Actor user "{}" is not a valid Grouper user'.format(args.actor_name)) return if args.subcommand == "create": name = args.name if ServiceAccount.get(session, name=name): logging.info("{}: Already exists. Doing nothing.".format(name)) return owner_group = Group.get(session, name=args.owner_group) if not owner_group: logging.fatal('Owner group "{}" does not exist.'.format(args.owner_group)) return logging.info("{}: No such service account, creating...".format(name)) description = args.description machine_set = args.machine_set create_service_account(session, actor_user, name, description, machine_set, owner_group) return
def get(self, *args, **kwargs): # type: (*Any, **Any) -> None group_id = kwargs.get("group_id") # type: Optional[int] name = kwargs.get("name") # type: Optional[str] account_id = kwargs.get("account_id") # type: Optional[int] accountname = kwargs.get("accountname") # type: Optional[str] self.handle_refresh() group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user self.render( "service-account.html", service_account=service_account, group=group, user=user, **get_user_view_template_vars(self.session, self.current_user, user, self.graph) )
def test_success(setup): # type: (SetupTest) -> None with setup.transaction(): setup.grant_permission_to_group(USER_ADMIN, "", "admins") setup.add_user_to_group("*****@*****.**", "admins") setup.create_user("*****@*****.**") setup.create_group("some-group") mock_ui = MagicMock() usecase = setup.usecase_factory.create_convert_user_to_service_account_usecase( "*****@*****.**", mock_ui ) usecase.convert_user_to_service_account("*****@*****.**", "some-group") assert mock_ui.mock_calls == [ call.converted_user_to_service_account("*****@*****.**", "some-group") ] # Check the User after the conversion service_account_user = User.get(setup.session, name="*****@*****.**") assert service_account_user assert service_account_user.is_service_account assert service_account_user.enabled # Check the ServiceAccount that should have been created service_account = ServiceAccount.get(setup.session, name="*****@*****.**") assert service_account assert service_account.description == "" assert service_account.machine_set == "" assert service_account.user_id == service_account_user.id # Check that the ServiceAccount is owned by the correct Group group = Group.get(setup.session, name="some-group") group_service_account = GroupServiceAccount.get( setup.session, service_account_id=service_account.id ) assert group assert group_service_account assert group_service_account.group_id == group.id
def get(self, *args, **kwargs): # type: (*Any, **Any) -> None group_id = kwargs.get("group_id") # type: Optional[int] name = kwargs.get("name") # type: Optional[str] account_id = kwargs.get("account_id") # type: Optional[int] accountname = kwargs.get("accountname") # type: Optional[str] self.handle_refresh() group = Group.get(self.session, group_id, name) if not group: return self.notfound() service_account = ServiceAccount.get(self.session, account_id, accountname) if not service_account: return self.notfound() user = service_account.user self.render("service-account.html", service_account=service_account, group=group, user=user, **get_user_view_template_vars(self.session, self.current_user, user, self.graph))
def service_account_exists(self, name): # type: (str) -> bool return SQLServiceAccount.get(self.session, name=name) is not None
def test_service_account_create(groups, service_accounts, session, tmpdir, users): # noqa: F811 machine_set = "foo +bar -(org)" description = "this is a service account.\n\n it is for testing" security_team_group = Group.get(session, name="security-team") good_actor_username = "******" good_service_account_name = "*****@*****.**" assert ServiceAccount.get(session, name=good_service_account_name) is None assert get_service_accounts(session, security_team_group) == [] # no-op if non-existing actor call_main( session, tmpdir, "service_account", "--actor", "*****@*****.**", "create", good_service_account_name, security_team_group.groupname, machine_set, description, ) # ... or if bad account name call_main( session, tmpdir, "service_account", "--actor", good_actor_username, "create", "bad-service-account-name", security_team_group.groupname, machine_set, description, ) # ... or non-existing owner group call_main( session, tmpdir, "service_account", "--actor", good_actor_username, "create", good_service_account_name, "non-such-owner-group", machine_set, description, ) # make sure no change was made assert ServiceAccount.get(session, name=good_service_account_name) is None assert get_service_accounts(session, security_team_group) == [] # now it works call_main( session, tmpdir, "service_account", "--actor", good_actor_username, "create", good_service_account_name, security_team_group.groupname, machine_set, description, ) service_account = ServiceAccount.get(session, name=good_service_account_name) assert service_account, "non-existing account should be created" assert service_account.user.name == good_service_account_name assert service_account.machine_set == machine_set assert service_account.description == description assert get_service_accounts(session, security_team_group) == [service_account] # no-op if account name already exists call_main( session, tmpdir, "service_account", "--actor", good_actor_username, "create", good_service_account_name, security_team_group.groupname, machine_set, description, ) service_account = ServiceAccount.get(session, name=good_service_account_name) assert service_account, "non-account should be created" assert service_account.user.name == good_service_account_name assert service_account.machine_set == machine_set assert service_account.description == description assert get_service_accounts(session, security_team_group) == [service_account] # actor can be a service account as well call_main( session, tmpdir, "service_account", "--actor", "*****@*****.**", "create", "*****@*****.**", security_team_group.groupname, machine_set + "2", description + "2", ) service_account_2 = ServiceAccount.get(session, name="*****@*****.**") assert service_account_2, "non-existing account should be created" assert service_account_2.user.name == "*****@*****.**" assert service_account_2.machine_set == (machine_set + "2") assert service_account_2.description == (description + "2") assert set(get_service_accounts(session, security_team_group)) == set( [service_account, service_account_2] )
def test_service_accounts( session, standard_graph, graph, users, groups, permissions # noqa: F811 ): # Create a service account. service_account = ServiceAccount.get(session, name="*****@*****.**") assert service_account.description == "some service account" assert service_account.machine_set == "some machines" assert service_account.user.name == "*****@*****.**" assert service_account.user.enabled == True assert service_account.user.is_service_account == True accounts = get_service_accounts(session, groups["team-sre"]) assert len(accounts) == 1 assert accounts[0].user.name == "*****@*****.**" assert is_service_account(session, service_account.user) # Duplicates should raise an exception. with pytest.raises(DuplicateServiceAccount): create_service_account(session, users["*****@*****.**"], "*****@*****.**", "dup", "dup", groups["team-sre"]) # zorkian should be able to manage the account, as should gary, but oliver (not a member of the # group) should not. assert can_manage_service_account(session, service_account, users["*****@*****.**"]) assert can_manage_service_account(session, service_account, users["*****@*****.**"]) assert not can_manage_service_account(session, service_account, users["*****@*****.**"]) # Check that the user appears in the graph. graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["enabled"] assert metadata["service_account"]["description"] == "some service account" assert metadata["service_account"]["machine_set"] == "some machines" assert metadata["service_account"]["owner"] == "team-sre" group_details = graph.get_group_details("team-sre") assert group_details["service_accounts"] == ["*****@*****.**"] # Grant a permission to the service account and check it in the graph. grant_permission_to_service_account(session, service_account, permissions["team-sre"], "*") graph.update_from_db(session) user_details = graph.get_user_details("*****@*****.**") assert user_details["permissions"][0]["permission"] == "team-sre" assert user_details["permissions"][0]["argument"] == "*" # Diabling the service account should remove the link to the group. disable_service_account(session, users["*****@*****.**"], service_account) assert service_account.user.enabled == False assert get_service_accounts(session, groups["team-sre"]) == [] # The user should also be gone from the graph and have its permissions removed. graph.update_from_db(session) group_details = graph.get_group_details("team-sre") assert "service_accounts" not in group_details metadata = graph.user_metadata["*****@*****.**"] assert not metadata["enabled"] assert "owner" not in metadata["service_account"] user_details = graph.get_user_details("*****@*****.**") assert user_details["permissions"] == [] # We can re-enable and attach to a different group. new_group = groups["security-team"] enable_service_account(session, users["*****@*****.**"], service_account, new_group) assert service_account.user.enabled == True assert get_service_accounts(session, groups["team-sre"]) == [] accounts = get_service_accounts(session, new_group) assert len(accounts) == 1 assert accounts[0].user.name == "*****@*****.**" # Check that this is reflected in the graph and the user has no permissions. graph.update_from_db(session) group_details = graph.get_group_details("security-team") assert group_details["service_accounts"] == ["*****@*****.**"] metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["owner"] == "security-team" user_details = graph.get_user_details("*****@*****.**") assert user_details["permissions"] == []
def test_service_account_fe_perms( session, standard_graph, graph, http_client, base_url # noqa: F811 ): admin = "*****@*****.**" owner = "*****@*****.**" plebe = "*****@*****.**" # Unrelated people cannot create a service account fe_url = url(base_url, "/groups/team-sre/service/create") with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({ "name": "service_account", "description": "*", "machine_set": "*" }), ) # But group members can create service accounts resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({ "name": "service_account", "description": "*", "machine_set": "*" }), ) assert resp.code == 200 # Unrelated people cannot grant a permission. fe_url = url(base_url, "/groups/team-sre/service/[email protected]/grant") with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({ "permission": "team-sre", "argument": "*" }), ) # Even group owners cannot grant an unrelated permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({ "permission": "other-perm", "argument": "*" }), ) assert resp.code == 200 graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") assert metadata["permissions"] == [] # Group owners can delegate a team permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({ "permission": "team-sre", "argument": "*" }), ) assert resp.code == 200 # Global user admins still cannot grant an unrelated permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({ "permission": "other-perm", "argument": "*" }), ) assert resp.code == 200 graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") assert len(metadata["permissions"]) == 1 # But can delegate a team permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({ "permission": "ssh", "argument": "*" }), ) assert resp.code == 200 # Check that the permissions are reflected in the graph. graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") perms = [(p["permission"], p["argument"]) for p in metadata["permissions"]] assert sorted(perms) == [("ssh", "*"), ("team-sre", "*")] # Find the mapping IDs of the two permissions. service_account = ServiceAccount.get(session, name="*****@*****.**") perms = service_account_permissions(session, service_account) # Unrelated people cannot revoke a permission. fe_url = url( base_url, "/groups/team-sre/service/[email protected]/revoke/{}".format( perms[0].mapping_id)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({})) # But the group owner and a global admin can. resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({})) assert resp.code == 200 fe_url = url( base_url, "/groups/team-sre/service/[email protected]/revoke/{}".format( perms[1].mapping_id)) resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({})) assert resp.code == 200 # This should have removed all the permissions. graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") assert metadata["permissions"] == []