def test_user_status_changes(make_user_session, make_group_session, session, users, groups): make_user_session.return_value = session make_group_session.return_value = session username = '******' groupname = 'team-sre' # add user to a group call_main('group', 'add_member', '--member', groupname, username) # disable the account call_main('user', 'disable', username) assert not User.get(session, name=username).enabled # double disabling is a no-op call_main('user', 'disable', username) assert not User.get(session, name=username).enabled # re-enable the account, preserving memberships call_main('user', 'enable', '--preserve-membership', username) assert User.get(session, name=username).enabled assert (u'User', username) in groups[groupname].my_members() # enabling an active account is a no-op call_main('user', 'enable', username) assert User.get(session, name=username).enabled # disable and re-enable without the --preserve-membership flag call_main('user', 'disable', username) call_main('user', 'enable', username) assert User.get(session, name=username).enabled assert (u'User', username) not in groups[groupname].my_members()
def test_user_public_key(make_session, session, users): make_session.return_value = session # good key username = '******' good_key = ('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDCUQeasspT/etEJR2WUoR+h2sMOQYbJgr0Q' 'E+J8p97gEhmz107KWZ+3mbOwyIFzfWBcJZCEg9wy5Paj+YxbGONqbpXAhPdVQ2TLgxr41bNXvbcR' 'AxZC+Q12UZywR4Klb2kungKz4qkcmSZzouaKK12UxzGB3xQ0N+3osKFj3xA1+B6HqrVreU19XdVo' 'AJh0xLZwhw17/NDM+dAcEdMZ9V89KyjwjraXtOVfFhQF0EDF0ame8d6UkayGrAiXC2He0P2Cja+J' '371P27AlNLHFJij8WGxvcGGSeAxMLoVSDOOllLCYH5UieV8mNpX1kNe2LeA58ciZb0AXHaipSmCH' 'gh/ some-comment') call_main('user', 'add_public_key', username, good_key) user = User.get(session, name=username) keys = user.my_public_keys() assert len(keys) == 1 assert keys[0].public_key == good_key # bad key username = '******' bad_key = 'ssh-rsa AAAblahblahkey some-comment' call_main('user', 'add_public_key', username, good_key) user = User.get(session, name=username) keys = user.my_public_keys() assert len(keys) == 1 assert keys[0].public_key == good_key
def post(self, user_id=None, name=None, key_id=None, tag_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() try: key = get_public_key(self.session, user.id, key_id) except KeyNotFound: return self.notfound() tag = PublicKeyTag.get(self.session, id=tag_id) if not tag: return self.notfound() try: remove_tag_from_public_key(self.session, key, tag) except TagNotOnKey: return self.redirect("/users/{}?refresh=yes".format(user.name)) AuditLog.log(self.session, self.current_user.id, 'untag_public_key', 'Untagged public key: {}'.format(key.fingerprint), on_tag_id=tag.id, on_user_id=user.id) return self.redirect("/users/{}?refresh=yes".format(user.name))
def post(self): supplied_token = self.get_body_argument("token") match = TokenValidate.validator.match(supplied_token) if not match: return self.error(((1, "Token format not recognized"),)) sess = Session() token_name = match.group("token_name") token_secret = match.group("token_secret") owner = User.get(sess, name=match.group("name")) token = UserToken.get(sess, owner, token_name) if token is None: return self.error(((2, "Token specified does not exist"),)) if not token.enabled: return self.error(((3, "Token is disabled"),)) if not token.check_secret(token_secret): return self.error(((4, "Token secret mismatch"),)) return self.success({ "owner": owner.username, "identity": str(token), "act_as_owner": True, "valid": True, })
def post(self, user_id=None, name=None, key_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if (user.name != self.current_user.name) and not self.current_user.user_admin: return self.forbidden() try: key = get_public_key(self.session, user.id, key_id) delete_public_key(self.session, user.id, key_id) except KeyNotFound: return self.notfound() AuditLog.log(self.session, self.current_user.id, 'delete_public_key', 'Deleted public key: {}'.format(key.fingerprint), on_user_id=user.id) email_context = { "actioner": self.current_user.name, "changed_user": user.name, "action": "removed", } send_email(self.session, [user.name], 'Public SSH key removed', 'ssh_keys_changed', settings, email_context) return self.redirect("/users/{}?refresh=yes".format(user.name))
def post(self, user_id=None, name=None, key_id=None, tag_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() try: key = get_public_key(self.session, user.id, key_id) except KeyNotFound: return self.notfound() tag = PublicKeyTag.get(self.session, id=tag_id) if not tag: return self.notfound() try: remove_tag_from_public_key(self.session, key, tag) except TagNotOnKey: return self.redirect("/users/{}?refresh=yes".format(user.name)) AuditLog.log(self.session, self.current_user.id, 'untag_public_key', 'Untagged public key: {}'.format(key.fingerprint), on_tag_id=tag.id, on_user_id=user.id) return self.redirect("/users/{}?refresh=yes".format(user.name))
def post(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() form = UserPasswordForm(self.request.arguments) if not form.validate(): return self.render("user-password-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors)) pass_name = form.data["name"] password = form.data["password"] try: add_new_user_password(self.session, pass_name, password, user.id) except PasswordAlreadyExists: self.session.rollback() form.name.errors.append("Name already in use.") return self.render("user-password-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors)) AuditLog.log( self.session, self.current_user.id, "add_password", "Added password: {}".format(pass_name), on_user_id=user.id, ) email_context = {"actioner": self.current_user.name, "changed_user": user.name, "pass_name": pass_name} send_email(self.session, [user.name], "User password created", "user_password_created", settings, email_context) return self.redirect("/users/{}?refresh=yes".format(user.name))
def get(self, user_id=None, name=None, pass_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() password = UserPassword.get(self.session, user=user, id=pass_id) return self.render("user-password-delete.html", user=user, password=password)
def get(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if (user.name != self.current_user.name) and not self.current_user.user_admin: return self.forbidden() self.render("public-key-add.html", form=PublicKeyForm(), user=user)
def get(self, user_id=None, name=None, token_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if (user.name != self.current_user.name) and not self.current_user.user_admin: return self.forbidden() token = UserToken.get(self.session, user=user, id=token_id) return self.render("user-token-disable.html", user=user, token=token)
def get(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() self.render("user-password-add.html", form=UserPasswordForm(), user=user)
def get(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() self.render("user-password-add.html", form=UserPasswordForm(), user=user)
def get(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if user.name != self.current_user.name and not ( self.current_user.has_permission(USER_ADMIN) and user.role_user ): return self.forbidden() self.render("user-token-add.html", form=UserTokenForm(), user=user)
def post(self, user_id=None, name=None, key_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() try: key = get_public_key(self.session, user.id, key_id) except KeyNotFound: return self.notfound() form = PublicKeyAddTagForm(self.request.arguments) form.tagname.choices = [] for tag in self.session.query(PublicKeyTag).filter_by( enabled=True).all(): form.tagname.choices.append([tag.name, tag.name]) if not form.validate(): return self.render("public-key-add-tag.html", form=form, user=user, key=key, alerts=self.get_form_alerts(form.errors)) tag = PublicKeyTag.get(self.session, name=form.data["tagname"]) if not tag: form.tagname.errors.append("Unknown tag name {}".format( form.data["tagname"])) return self.render("public-key-add-tag.html", form=form, user=user, key=key, alerts=self.get_form_alerts(form.errors)) try: add_tag_to_public_key(self.session, key, tag) except DuplicateTag: return self.render("public-key-add-tag.html", form=form, user=user, key=key, alerts=["This key already has that tag!"]) AuditLog.log(self.session, self.current_user.id, 'tag_public_key', 'Tagged public key: {}'.format(key.fingerprint), on_tag_id=tag.id, on_user_id=user.id) return self.redirect("/users/{}?refresh=yes".format(user.name))
def get(self, user_id=None, name=None): self.handle_refresh() user = User.get(self.session, user_id, name) if user_id is not None: user = self.session.query(User).filter_by(id=user_id).scalar() else: user = self.session.query(User).filter_by(username=name).scalar() if not user: return self.notfound() can_control = user.name == self.current_user.name or self.current_user.user_admin can_disable = UserDisable.check_access(self.current_user, user) can_enable = UserEnable.check_access(self.current_user, user) if user.id == self.current_user.id: num_pending_group_requests = self.current_user.my_requests_aggregate().count() _, num_pending_perm_requests = get_requests_by_owner(self.session, self.current_user, status='pending', limit=1, offset=0) else: num_pending_group_requests = None num_pending_perm_requests = None try: user_md = self.graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} open_audits = user.my_open_audits() group_edge_list = group_biz.get_groups_by_user(self.session, user) if user.enabled else [] groups = [{'name': g.name, 'type': 'Group', 'role': ge._role} for g, ge in group_edge_list] public_keys = user.my_public_keys() permissions = user_md.get('permissions', []) log_entries = user.my_log_entries() self.render("user.html", user=user, groups=groups, public_keys=public_keys, can_control=can_control, permissions=permissions, can_disable=can_disable, can_enable=can_enable, user_tokens=user.tokens, log_entries=log_entries, num_pending_group_requests=num_pending_group_requests, num_pending_perm_requests=num_pending_perm_requests, open_audits=open_audits, )
def test_user_create(make_session, session, users): make_session.return_value = session # simple username = '******' call_main('user', 'create', username) assert User.get(session, name=username), 'non-existent user should be created' # check username bad_username = '******' call_main('user', 'create', bad_username) assert not User.get(session, name=bad_username), 'bad user should not be created' # bulk usernames = ['*****@*****.**', '*****@*****.**', '*****@*****.**'] call_main('user', 'create', *usernames) users = [User.get(session, name=u) for u in usernames] assert all(users), 'all users created' usernames_with_one_bad = ['*****@*****.**', '*****@*****.**', 'not_valid_user'] call_main('user', 'create', *usernames_with_one_bad) users = [User.get(session, name=u) for u in usernames_with_one_bad] assert not any(users), 'one bad seed means no users created'
def get(self, user_id=None, name=None, key_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if (user.name != self.current_user.name) and not self.current_user.user_admin: return self.forbidden() try: key = get_public_key(self.session, user.id, key_id) except KeyNotFound: return self.notfound() self.render("public-key-delete.html", user=user, key=key)
def post(self, user_id=None, name=None, token_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if (user.name != self.current_user.name) and not self.current_user.user_admin: return self.forbidden() token = UserToken.get(self.session, user=user, id=token_id) disable_user_token(self.session, token) AuditLog.log(self.session, self.current_user.id, 'disable_token', 'Disabled token: {}'.format(token.name), on_user_id=user.id) self.session.commit() return self.render("user-token-disabled.html", token=token)
def post(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.current_user, user): return self.forbidden() user.disable() self.session.commit() AuditLog.log(self.session, self.current_user.id, 'disable_user', 'Disabled user.', on_user_id=user.id) return self.redirect("/users/{}?refresh=yes".format(user.name))
def post(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if (user.name != self.current_user.name) and not self.current_user.user_admin: return self.forbidden() form = PublicKeyForm(self.request.arguments) if not form.validate(): return self.render( "public-key-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors), ) try: pubkey = public_key.add_public_key(self.session, user, form.data["public_key"]) except public_key.PublicKeyParseError: form.public_key.errors.append( "Key failed to parse and is invalid." ) return self.render( "public-key-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors), ) except public_key.DuplicateKey: form.public_key.errors.append( "Key already in use. Public keys must be unique." ) return self.render( "public-key-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors), ) AuditLog.log(self.session, self.current_user.id, 'add_public_key', 'Added public key: {}'.format(pubkey.fingerprint), on_user_id=user.id) email_context = { "actioner": self.current_user.name, "changed_user": user.name, "action": "added", } send_email(self.session, [user.name], 'Public SSH key added', 'ssh_keys_changed', settings, email_context) return self.redirect("/users/{}?refresh=yes".format(user.name))
def post(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() form = UserPasswordForm(self.request.arguments) if not form.validate(): return self.render( "user-password-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors), ) pass_name = form.data["name"] password = form.data["password"] try: add_new_user_password(self.session, pass_name, password, user.id) except PasswordAlreadyExists: self.session.rollback() form.name.errors.append("Name already in use.") return self.render( "user-password-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors), ) AuditLog.log(self.session, self.current_user.id, 'add_password', 'Added password: {}'.format(pass_name), on_user_id=user.id) email_context = { "actioner": self.current_user.name, "changed_user": user.name, "pass_name": pass_name, } send_email(self.session, [user.name], 'User password created', 'user_password_created', settings, email_context) return self.redirect("/users/{}?refresh=yes".format(user.name))
def test_oneoff(mock_make_session, mock_annex, mock_load_plugins, session): mock_make_session.return_value = session username = '******' other_username = '******' groupname = 'fake_group' class FakeOneOff(object): def configure(self, service_name): pass def run(self, session, **kwargs): if kwargs.get('group'): Group.get_or_create(session, groupname=groupname) session.commit() elif kwargs.get('key') == 'valuewith=': User.get_or_create(session, username=other_username) session.commit() else: User.get_or_create(session, username=username) session.commit() mock_annex.return_value = [FakeOneOff()] # dry_run call_main('oneoff', 'run', 'FakeOneOff') assert User.get(session, name=username) is None, 'default dry_run means no writes' assert User.get(session, name=other_username) is None, '"valuewith= not in arg' assert Group.get(session, name=groupname) is None, '"group" not in arg so no group created' # not dry_run, create a user call_main('oneoff', 'run', '--no-dry_run', 'FakeOneOff') assert User.get(session, name=username) is not None, 'dry_run off means writes' assert User.get(session, name=other_username) is None, '"valuewith= not in arg' assert Group.get(session, name=groupname) is None, '"group" not in arg so no group created' # not dry_run, use kwarg to create a group call_main('oneoff', 'run', '--no-dry_run', 'FakeOneOff', 'group=1') assert User.get(session, name=username) is not None, 'dry_run off means writes' assert User.get(session, name=other_username) is None, '"valuewith= not in arg' assert Group.get(session, name=groupname) is not None, '"group" in arg so group created' # invalid format for argument should result in premature system exit with pytest.raises(SystemExit): call_main('oneoff', 'run', '--no-dry_run', 'FakeOneOff', 'bad_arg') call_main('oneoff', 'run', '--no-dry_run', 'FakeOneOff', 'key=valuewith=') assert User.get(session, name=other_username) is not None, '"valuewith= in arg, create user2'
def post(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if user.name != self.current_user.name and not ( self.current_user.has_permission(USER_ADMIN) and user.role_user ): return self.forbidden() form = UserTokenForm(self.request.arguments) if not form.validate(): return self.render( "user-token-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors), ) try: token, secret = add_new_user_token(self.session, UserToken(name=form.data["name"], user=user)) self.session.commit() except IntegrityError: self.session.rollback() form.name.errors.append( "Name already in use." ) return self.render( "user-token-add.html", form=form, user=user, alerts=self.get_form_alerts(form.errors), ) AuditLog.log(self.session, self.current_user.id, 'add_token', 'Added token: {}'.format(token.name), on_user_id=user.id) email_context = { "actioner": self.current_user.name, "changed_user": user.name, "action": "added", } send_email(self.session, [user.name], 'User token created', 'user_tokens_changed', settings, email_context) return self.render("user-token-created.html", token=token, secret=secret)
def post(self, user_id=None, name=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.current_user, user): return self.forbidden() form = UserEnableForm(self.request.arguments) if not form.validate(): # TODO: add error message return self.redirect("/users/{}?refresh=yes".format(user.name)) user.enable(self.current_user, preserve_membership=form.preserve_membership.data) self.session.commit() AuditLog.log(self.session, self.current_user.id, 'enable_user', 'Enabled user.', on_user_id=user.id) return self.redirect("/users/{}?refresh=yes".format(user.name))
def post(self, user_id=None, name=None, pass_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() password = UserPassword.get(self.session, user=user, id=pass_id) try: delete_user_password(self.session, password.name, user.id) except PasswordDoesNotExist: # if the password doesn't exist, we can pretend like it did and that we deleted it return self.redirect("/users/{}?refresh=yes".format(user.id)) AuditLog.log(self.session, self.current_user.id, 'delete_password', 'Deleted password: {}'.format(password.name), on_user_id=user.id) self.session.commit() return self.redirect("/users/{}?refresh=yes".format(user.id))
def get(self, user_id=None, name=None, key_id=None): user = User.get(self.session, user_id, name) if not user: return self.notfound() if not self.check_access(self.session, self.current_user, user): return self.forbidden() try: key = get_public_key(self.session, user.id, key_id) except KeyNotFound: return self.notfound() form = PublicKeyAddTagForm() form.tagname.choices = [] for tag in self.session.query(PublicKeyTag).filter_by( enabled=True).all(): form.tagname.choices.append([tag.name, tag.name]) self.render("public-key-add-tag.html", user=user, key=key, form=form)
def group_command(args): session = make_session() group = session.query(Group).filter_by(groupname=args.groupname).scalar() if not group: logging.error("No such group %s".format(args.groupname)) return for username in args.username: user = User.get(session, name=username) if not user: logging.error("no such user '{}'".format(username)) return if args.subcommand == "add_member": if args.member: role = 'member' elif args.owner: role = 'owner' elif args.np_owner: role = 'np-owner' elif args.manager: role = 'manager' assert role logging.info("Adding {} as {} to group {}".format(username, role, args.groupname)) group.add_member(user, user, "grouper-ctl join", status="actioned", role=role) AuditLog.log( session, user.id, 'join_group', '{} manually joined via grouper-ctl'.format(username), on_group_id=group.id) session.commit() elif args.subcommand == "remove_member": logging.info("Removing {} from group {}".format(username, args.groupname)) group.revoke_member(user, user, "grouper-ctl remove") AuditLog.log( session, user.id, 'leave_group', '{} manually left via grouper-ctl'.format(username), on_group_id=group.id) session.commit()
def notify_edge_expiration(settings, session, edge): """Send notification that an edge has expired. Handles email notification and audit logging. Args: settings (Settings): Grouper Settings object for current run. session (Session): Object for db session. edge (GroupEdge): The expiring edge. """ # TODO(herb): get around circular depdendencies; long term remove call to # send_async_email() from grouper.models from grouper.model_soup import Group, OBJ_TYPES_IDX, User # TODO(rra): Arbitrarily use the first listed owner of the group from which membership expired # as the actor, since we have to provide an actor and we didn't record who set the expiration on # the edge originally. actor_id = next(edge.group.my_owners().itervalues()).id # Pull data about the edge and the affected user or group. group_name = edge.group.name if OBJ_TYPES_IDX[edge.member_type] == "User": user = User.get(session, pk=edge.member_pk) member_name = user.username recipients = [member_name] member_is_user = True else: subgroup = Group.get(session, pk=edge.member_pk) member_name = subgroup.groupname recipients = subgroup.my_owners_as_strings() member_is_user = False # Log to the audit log. How depends on whether a user's membership has expired or a group's # membership has expired. audit_data = { "action": "expired_from_group", "actor_id": actor_id, "description": "{} expired out of the group".format(member_name), } if member_is_user: AuditLog.log(session, on_user_id=user.id, on_group_id=edge.group_id, **audit_data) else: # Make an audit log entry for both the subgroup and the parent group so that it will show up # in the FE view for both groups. AuditLog.log(session, on_group_id=edge.group_id, **audit_data) AuditLog.log(session, on_group_id=subgroup.id, **audit_data) # Send email notification to the affected people. email_context = { "group_name": group_name, "member_name": member_name, "member_is_user": member_is_user, } send_email( session=session, recipients=recipients, subject="Membership in {} expired".format(group_name), template="expiration", settings=settings, context=email_context, )
def user_command(args): session = make_session() if args.subcommand == "create": for username in args.username: user = User.get(session, name=username) if not user: logging.info("{}: No such user, creating...".format(username)) user = User.get_or_create(session, username=username, role_user=args.role_user) session.commit() else: logging.info("{}: Already exists. Doing nothing.".format(username)) return elif args.subcommand == "disable": for username in args.username: user = User.get(session, name=username) if not user: logging.info("{}: No such user. Doing nothing.".format(username)) elif not user.enabled: logging.info("{}: User already disabled. Doing nothing.".format(username)) else: logging.info("{}: User found, disabling...".format(username)) user.disable() session.commit() return elif args.subcommand == "enable": for username in args.username: user = User.get(session, name=username) if not user: logging.info("{}: No such user. Doing nothing.".format(username)) elif user.enabled: logging.info("{}: User not disabled. Doing nothing.".format(username)) else: logging.info("{}: User found, enabling...".format(username)) user.enable(user, preserve_membership=args.preserve_membership) session.commit() return # "add_public_key" and "set_metadata" user = User.get(session, name=args.username) if not user: logging.error("{}: No such user. Doing nothing.".format(args.username)) return # User must exist at this point. if args.subcommand == "set_metadata": print "Setting %s metadata: %s=%s" % (args.username, args.metadata_key, args.metadata_value) if args.metadata_value == "": args.metadata_value = None user.set_metadata(args.metadata_key, args.metadata_value) session.commit() elif args.subcommand == "add_public_key": print "Adding public key for user..." try: pubkey = public_key.add_public_key(session, user, args.public_key) except public_key.DuplicateKey: print "Key already in use." return except public_key.PublicKeyParseError: print "Public key appears to be invalid." return AuditLog.log(session, user.id, 'add_public_key', '(Administrative) Added public key: {}'.format(pubkey.fingerprint), on_user_id=user.id)
def post(self, group_id=None, name=None, name2=None, member_type=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if self.current_user.name == name2: return self.forbidden() members = group.my_members() my_role = self.current_user.my_role(members) if my_role not in ("manager", "owner", "np-owner"): return self.forbidden() member = members.get((member_type.capitalize(), name2), None) if not member: return self.notfound() if member.type == "Group": user_or_group = Group.get(self.session, member.id) else: user_or_group = User.get(self.session, member.id) if not user_or_group: return self.notfound() edge = GroupEdge.get( self.session, group_id=group.id, member_type=OBJ_TYPES[member.type], member_pk=member.id, ) if not edge: return self.notfound() form = GroupEditMemberForm(self.request.arguments) form.role.choices = [["member", "Member"]] if my_role in ("owner", "np-owner"): form.role.choices.append(["manager", "Manager"]) form.role.choices.append(["owner", "Owner"]) form.role.choices.append(["np-owner", "No-Permissions Owner"]) if not form.validate(): return self.render( "group-edit-member.html", group=group, member=member, edge=edge, form=form, alerts=self.get_form_alerts(form.errors), ) fail_message = 'This join is denied with this role at this time.' try: user_can_join = assert_can_join(group, user_or_group, role=form.data["role"]) except UserNotAuditor as e: user_can_join = False fail_message = e if not user_can_join: return self.render( "group-edit-member.html", form=form, group=group, member=member, edge=edge, alerts=[ Alert('danger', fail_message, 'Audit Policy Enforcement') ] ) expiration = None if form.data["expiration"]: expiration = datetime.strptime(form.data["expiration"], "%m/%d/%Y") group.edit_member(self.current_user, user_or_group, form.data["reason"], role=form.data["role"], expiration=expiration) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def test_permission_request_flow(session, standard_graph, groups, grantable_permissions, http_client, base_url): """Test that a permission request gets into the system correctly and notifications are sent correctly.""" perm_grant, _, perm1, perm2 = grantable_permissions grant_permission(groups["all-teams"], perm_grant, argument="grantable.*") # REQUEST: 'grantable.one', 'some argument' for 'serving-team' groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"permission_name": "grantable.one", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text"}), headers={'X-Grouper-User': username}) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 1, "only one user (and no group) should receive notification for request" perms = _load_permissions_by_group_name(session, 'serving-team') assert len(perms) == 1 assert "grantable.one" not in perms, "requested permission shouldn't be granted immediately" user = User.get(session, name='*****@*****.**') request_tuple, total = get_requests_by_owner(session, user, "pending", 10, 0) assert len(request_tuple.requests) == 0, "random user shouldn't have a request" user = User.get(session, name='*****@*****.**') request_tuple, total = get_requests_by_owner(session, user, "pending", 10, 0) assert len(request_tuple.requests) == 1, "user in group with grant should have a request" # APPROVE grant: have '*****@*****.**' action this request as owner of # 'all-teams' which has the grant permission for the requested permission request_id = request_tuple.requests[0].id fe_url = url(base_url, "/permissions/requests/{}".format(request_id)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"status": "actioned", "reason": "lgtm"}), headers={'X-Grouper-User': user.name}) assert resp.code == 200 perms = _load_permissions_by_group_name(session, 'serving-team') assert len(perms) == 2 assert "grantable.one" in perms, "requested permission shouldn't be granted immediately" emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 1, "requester should receive email as well" # (re)REQUEST: 'grantable.one', 'some argument' for 'serving-team' groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"permission_name": "grantable.one", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text"}), headers={'X-Grouper-User': username}) assert resp.code == 200 user = User.get(session, name='*****@*****.**') request_tuple, total = get_requests_by_owner(session, user, "pending", 10, 0) assert len(request_tuple.requests) == 0, "request for existing perm should fail" # REQUEST: 'grantable.two', 'some argument' for 'serving-team' groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"permission_name": "grantable.two", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text"}), headers={'X-Grouper-User': username}) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 1, "only one user (and no group) should receive notification for request" perms = _load_permissions_by_group_name(session, 'serving-team') assert len(perms) == 2 assert "grantable.two" not in perms, "requested permission shouldn't be granted immediately" user = User.get(session, name='*****@*****.**') request_tuple, total = get_requests_by_owner(session, user, "pending", 10, 0) assert len(request_tuple.requests) == 0, "random user shouldn't have a request" user = User.get(session, name='*****@*****.**') request_tuple, total = get_requests_by_owner(session, user, "pending", 10, 0) assert len(request_tuple.requests) == 1, "user in group with grant should have a request" # CANCEL request: have '*****@*****.**' cancel this request request_id = request_tuple.requests[0].id fe_url = url(base_url, "/permissions/requests/{}".format(request_id)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"status": "cancelled", "reason": "heck no"}), headers={'X-Grouper-User': user.name}) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 1, "rejection email should be sent" perms = _load_permissions_by_group_name(session, 'serving-team') assert len(perms) == 2 assert "grantable.two" not in perms, "no new permissions should be granted for this"