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 test_add_service_account(session, users, http_client, base_url): user = users['*****@*****.**'] # Add account fe_url = url(base_url, '/service/create') resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': '*****@*****.**', "description": "Hi", "canjoin": "canjoin"}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 assert User.get(session, name="*****@*****.**") is None assert Group.get(session, name="*****@*****.**") is None # Add account fe_url = url(base_url, '/service/create') resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': '*****@*****.**', "description": "Hi", "canjoin": "canjoin"}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 u = User.get(session, name="*****@*****.**") g = Group.get(session, name="*****@*****.**") assert u is not None assert g is not None assert is_service_account(session, user=u) assert is_service_account(session, group=g) assert get_service_account(session, user=u).group.id == g.id assert get_service_account(session, group=g).user.id == u.id assert not is_service_account(session, user=user) assert not is_service_account(session, group=Group.get(session, name="team-sre"))
def test_permission_exclude_inactive(session, standard_graph): """Ensure disabled groups are excluded from permission data.""" group = Group.get(session, name="team-sre") permission = Permission.get(session, name="ssh") assert "team-sre" in [g[0] for g in get_groups_by_permission(session, permission)] group.disable() assert "team-sre" not in [g[0] for g in get_groups_by_permission(session, permission)]
def get(self, request_id, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() my_role = user_role(self.current_user, members) if my_role not in ("manager", "owner", "np-owner"): return self.forbidden() request = self.session.query(Request).filter_by(id=request_id).scalar() if not request: return self.notfound() form = GroupRequestModifyForm(self.request.arguments) form.status.choices = self._get_choices(request.status) updates = request.my_status_updates() self.render("group-request-update.html", group=group, request=request, members=members, form=form, statuses=REQUEST_STATUS_CHOICES, updates=updates)
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() if not user_role(self.current_user, members) in ("owner", "np-owner"): return self.forbidden() # Enabling and disabling service accounts via the group endpoints is forbidden # because we need the preserve_membership data that is only available via the # UserEnable form. if is_service_account(self.session, group=group): return self.forbidden() group.enable() self.session.commit() AuditLog.log(self.session, self.current_user.id, 'enable_group', 'Enabled group.', on_group_id=group.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def get_service_account(session, user=None, group=None): # type: (Session, User, Group) -> ServiceAccount """ Takes in a User or a Group and returns a dictionary that contains all of the service account components for the service account that the user/group is part of. Args: session: the database session user: a User object to check group: a Group object to check Throws: ServiceAccountNotFound: if the user or group is not part of a service account Returns: a dictionary with all components of the service account of the user or group passed in """ if not is_service_account(session, user, group): raise ServiceAccountNotFound() name = user.name if user else group.name return ServiceAccount(User.get(session, name=name), Group.get(session, name=name))
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not self.current_user.can_manage(group): return self.forbidden() form = GroupRemoveForm(self.request.arguments) if not form.validate(): return self.send_error(status_code=400) member_type, member_name = form.data["member_type"], form.data["member"] members = group.my_members() if not members.get((member_type.capitalize(), member_name), None): return self.notfound() removed_member = get_user_or_group(self.session, member_name, user_or_group=member_type) if self.current_user == removed_member: return self.send_error( status_code=400, reason="Can't remove yourself. Leave group instead." ) group.revoke_member(self.current_user, removed_member, "Removed by owner/np-owner/manager") AuditLog.log(self.session, self.current_user.id, 'remove_from_group', '{} was removed from the group.'.format(removed_member.name), on_group_id=group.id, on_user_id=removed_member.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() status = self.get_argument("status", None) offset = int(self.get_argument("offset", 0)) limit = int(self.get_argument("limit", 100)) if limit > 9000: limit = 9000 requests = group.my_requests(status).order_by( Request.requested_at.desc() ) members = group.my_members() total = requests.count() requests = requests.offset(offset).limit(limit) current_user_role = { 'is_owner': user_role_index(self.current_user, members) in OWNER_ROLE_INDICES, 'is_approver': user_role_index(self.current_user, members) in APPROVER_ROLE_INDICIES, 'is_manager': user_role(self.current_user, members) == "manager", 'role': user_role(self.current_user, members), } self.render( "group-requests.html", group=group, requests=requests, members=members, status=status, statuses=REQUEST_STATUS_CHOICES, offset=offset, limit=limit, total=total, current_user_role=current_user_role, )
def test_oneoff(mock_make_session, mock_annex, 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 test_grant_and_revoke(session, standard_graph, graph, groups, permissions, http_client, base_url): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): return any(map(lambda x: x.permission == permission_name, graph.permission_metadata[group_name])) # make some permission admins perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) # grant attempt by non-permission admin fe_url = url(base_url, "/permissions/grant/{}".format(group_name)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({"permission": permission_name, "argument": "specific_arg"}), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert not _check_graph_for_perm(graph), "no permissions granted" # grant by permission admin resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"permission": permission_name, "argument": "specific_arg"}), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions granted, successfully" # figure out mapping_id of grant permission_id = Permission.get(session, name=permission_name).id group_id = Group.get(session, name=group_name).id mapping = session.query(PermissionMap).filter( PermissionMap.permission_id == permission_id, PermissionMap.group_id == group_id).first() # revoke permission by non-admin fe_url = url(base_url, "/permissions/{}/revoke/{}".format(permission_name, mapping.id)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions not revoked" # revoke permission for realz resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert not _check_graph_for_perm(graph), "permissions revoked successfully"
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() group_md = self.graph.get_group_details(group.name) form = GroupJoinForm() form.member.choices = self._get_choices(group) return self.render("group-join.html", form=form, group=group, audited=group_md["audited"])
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() if not user_role(self.current_user, members): return self.forbidden() return self.render("group-leave.html", group=group)
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not user_can_manage_group(self.session, group, self.current_user): return self.forbidden() form = GroupEditForm(obj=group) self.render("group-edit.html", group=group, form=form)
def test_sync_db_default_group(make_session, session, users, groups): make_session.return_value = session call_main('sync_db') admin_group = Group.get(session, name="grouper-administrators") assert admin_group, "Group should have been autocreated" admin_group_permission_names = [perm[1] for perm in admin_group.my_permissions()] for permission in (GROUP_ADMIN, PERMISSION_ADMIN, USER_ADMIN): assert permission in admin_group_permission_names, \ "Expected permission missing: %s" % permission
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not self.current_user.can_manage(group): return self.forbidden() form = GroupEditForm(obj=group) self.render("group-edit.html", group=group, form=form)
def test_permission_exclude_inactive(session, standard_graph): """Ensure disabled groups are excluded from permission data.""" group = Group.get(session, name="team-sre") permission = Permission.get(session, name="ssh") assert "team-sre" in [ g[0] for g in get_groups_by_permission(session, permission) ] group.disable() assert "team-sre" not in [ g[0] for g in get_groups_by_permission(session, permission) ]
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() if not self.current_user.my_role(members): return self.forbidden() return self.render( "group-leave.html", group=group )
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not self.current_user.can_manage(group): return self.forbidden() members = group.my_members() my_role = self.current_user.my_role(members) return self.render( "group-add.html", form=self.get_form(role=my_role), group=group )
def get(self, group_id=None, name=None): self.handle_refresh() group = Group.get(self.session, group_id, name) if not group: return self.notfound() if is_service_account(self.session, group=group): return self.redirect("/service/{}".format(group.groupname)) self.render( "group.html", group=group, **get_group_view_template_vars(self.session, self.current_user, group, self.graph) )
def test_sync_db_default_group(make_session, session, users, groups): make_session.return_value = session call_main('sync_db') admin_group = Group.get(session, name="grouper-administrators") assert admin_group, "Group should have been autocreated" admin_group_permission_names = [ perm[1] for perm in admin_group.my_permissions() ] for permission in (GROUP_ADMIN, PERMISSION_ADMIN, USER_ADMIN): assert permission in admin_group_permission_names, \ "Expected permission missing: %s" % permission
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() args_by_perm = get_grantable_permissions(self.session, settings.restricted_ownership_permissions) dropdown_form, text_form = GroupPermissionRequest._get_forms(args_by_perm, None) self.render("group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), dropdown_help=settings.permission_request_dropdown_help, text_help=settings.permission_request_text_help)
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not user_can_manage_group(self.session, group, self.current_user): return self.forbidden() members = group.my_members() my_role = user_role(self.current_user, members) return self.render( "group-add.html", form=self.get_form(role=my_role), group=group )
def get(self, group_id=None, name=None): self.handle_refresh() group = Group.get(self.session, group_id, name) if not group: return self.notfound() if is_service_account(self.session, group=group): return self.redirect("/service/{}".format(group.groupname)) self.render("group.html", group=group, **get_group_view_template_vars(self.session, self.current_user, group, self.graph))
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not user_can_manage_group(self.session, group, self.current_user): return self.forbidden() form = GroupEditForm(self.request.arguments, obj=group) if not form.validate(): return self.render("group-edit.html", group=group, form=form, alerts=self.get_form_alerts(form.errors)) if (group.groupname != form.data["groupname"] and is_service_account(self.session, group=group)): form.groupname.errors.append( "You cannot change the name of service account groups") return self.render("group-edit.html", group=group, form=form, alerts=self.get_form_alerts(form.errors)) group.groupname = form.data["groupname"] group.email_address = form.data["email_address"] group.description = form.data["description"] group.canjoin = form.data["canjoin"] group.auto_expire = form.data["auto_expire"] Counter.incr(self.session, "updates") try: self.session.commit() except IntegrityError: self.session.rollback() form.groupname.errors.append("{} already exists".format( form.data["groupname"])) return self.render("group-edit.html", group=group, form=form, alerts=self.get_form_alerts(form.errors)) AuditLog.log(self.session, self.current_user.id, 'edit_group', 'Edited group.', on_group_id=group.id) return self.redirect("/groups/{}".format(group.name))
def get(self, group_id=None, name=None): self.handle_refresh() group = Group.get(self.session, group_id, name) if not group: return self.notfound() grantable = self.current_user.my_grantable_permissions() try: group_md = self.graph.get_group_details(group.name) except NoSuchGroup: # Very new group with no metadata yet, or it has been disabled and # excluded from in-memory cache. group_md = {} members = group.my_members() groups = group.my_groups() permissions = group_md.get('permissions', []) permission_requests_pending = get_pending_request_by_group(self.session, group) audited = group_md.get('audited', False) log_entries = group.my_log_entries() num_pending = group.my_requests("pending").count() current_user_role = { 'is_owner': self.current_user.my_role_index(members) in OWNER_ROLE_INDICES, 'is_approver': self.current_user.my_role_index(members) in APPROVER_ROLE_INDICIES, 'is_manager': self.current_user.my_role(members) == "manager", } # Add mapping_id to permissions structure my_permissions = group.my_permissions() for perm_up in permissions: for perm_direct in my_permissions: if (perm_up['permission'] == perm_direct.name and perm_up['argument'] == perm_direct.argument): perm_up['mapping_id'] = perm_direct.mapping_id break alerts = [] self_pending = group.my_requests("pending", user=self.current_user).count() if self_pending: alerts.append(Alert('info', 'You have a pending request to join this group.', None)) self.render( "group.html", group=group, members=members, groups=groups, num_pending=num_pending, alerts=alerts, permissions=permissions, log_entries=log_entries, grantable=grantable, audited=audited, statuses=AUDIT_STATUS_CHOICES, current_user_role=current_user_role, permission_requests_pending=permission_requests_pending )
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() group_md = self.graph.get_group_details(group.name) form = GroupJoinForm() form.member.choices = self._get_choices(group) return self.render( "group-join.html", form=form, group=group, audited=group_md["audited"], )
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() if not self.current_user.my_role(members): return self.forbidden() group.revoke_member(self.current_user, self.current_user, "User self-revoked.") AuditLog.log(self.session, self.current_user.id, 'leave_group', '{} left the group.'.format(self.current_user.name), on_group_id=group.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() if not self.current_user.my_role(members) in ("owner", "np-owner"): return self.forbidden() group.enable() self.session.commit() AuditLog.log(self.session, self.current_user.id, 'enable_group', 'Enabled group.', on_group_id=group.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def get(self, user_id=None, name=None): self.handle_refresh() user = User.get(self.session, user_id, name) if not user or not user.role_user: return self.notfound() group = Group.get(self.session, name=name) actor = self.current_user graph = self.graph session = self.session self.render("service.html", user=user, group=group, **get_service_account_view_template_vars(session, actor, user, group, graph) )
def sync_db_command(args): # Models not implicitly or explictly imported above are explicitly imported # here: from grouper.models.perf_profile import PerfProfile # noqa db_engine = get_db_engine(get_database_url(settings)) Model.metadata.create_all(db_engine) # Add some basic database structures we know we will need if they don't exist. session = make_session() for name, description in SYSTEM_PERMISSIONS: test = Permission.get(session, name) if test: continue permission = Permission(name=name, description=description) try: permission.add(session) session.flush() except IntegrityError: session.rollback() raise Exception('Failed to create permission: %s' % (name, )) session.commit() # This group is needed to bootstrap a Grouper installation. admin_group = Group.get(session, name="grouper-administrators") if not admin_group: admin_group = Group( groupname="grouper-administrators", description="Administrators of the Grouper system.", canjoin="nobody", ) try: admin_group.add(session) session.flush() except IntegrityError: session.rollback() raise Exception('Failed to create group: grouper-administrators') for permission_name in (GROUP_ADMIN, PERMISSION_ADMIN, USER_ADMIN): permission = Permission.get(session, permission_name) assert permission, "Permission should have been created earlier!" grant_permission(session, admin_group.id, permission.id) session.commit()
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not user_can_manage_group(self.session, group, self.current_user): return self.forbidden() form = GroupEditForm(self.request.arguments, obj=group) if not form.validate(): return self.render( "group-edit.html", group=group, form=form, alerts=self.get_form_alerts(form.errors) ) if (group.groupname != form.data["groupname"] and is_service_account(self.session, group=group)): form.groupname.errors.append("You cannot change the name of service account groups") return self.render( "group-edit.html", group=group, form=form, alerts=self.get_form_alerts(form.errors) ) group.groupname = form.data["groupname"] group.description = form.data["description"] group.canjoin = form.data["canjoin"] group.auto_expire = form.data["auto_expire"] Counter.incr(self.session, "updates") try: self.session.commit() except IntegrityError: self.session.rollback() form.groupname.errors.append( "{} already exists".format(form.data["groupname"]) ) return self.render( "group-edit.html", group=group, form=form, alerts=self.get_form_alerts(form.errors) ) AuditLog.log(self.session, self.current_user.id, 'edit_group', 'Edited group.', on_group_id=group.id) return self.redirect("/groups/{}".format(group.name))
def get(self, name=None): grantable = self.current_user.my_grantable_permissions() if not grantable: return self.forbidden() group = Group.get(self.session, None, name) if not group: return self.notfound() form = PermissionGrantForm() form.permission.choices = [["", "(select one)"]] for perm in grantable: grantable = "{} ({})".format(perm[0].name, perm[1]) form.permission.choices.append([perm[0].name, grantable]) return self.render( "permission-grant.html", form=form, group=group, )
def get(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 = user_role(self.current_user, 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() 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"]) form.role.data = edge.role form.expiration.data = edge.expiration.strftime( "%m/%d/%Y") if edge.expiration else None self.render( "group-edit-member.html", group=group, member=member, edge=edge, form=form, )
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() if not user_role(self.current_user, members): return self.forbidden() group.revoke_member(self.current_user, self.current_user, "User self-revoked.") AuditLog.log(self.session, self.current_user.id, 'leave_group', '{} left the group.'.format(self.current_user.name), on_group_id=group.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def get(self, name=None): grantable = user_grantable_permissions(self.session, self.current_user) if not grantable: return self.forbidden() group = Group.get(self.session, None, name) if not group: return self.notfound() form = PermissionGrantForm() form.permission.choices = [["", "(select one)"]] for perm in grantable: grantable = "{} ({})".format(perm[0].name, perm[1]) form.permission.choices.append([perm[0].name, grantable]) return self.render( "permission-grant.html", form=form, group=group, )
def get(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() # Only members can request permissions if not self.current_user.is_member(group.my_members()): return self.forbidden() args_by_perm = get_grantable_permissions( self.session, settings.restricted_ownership_permissions) dropdown_form, text_form = GroupPermissionRequest._get_forms( args_by_perm, None) self.render("group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), dropdown_help=settings.permission_request_dropdown_help, text_help=settings.permission_request_text_help)
def test_group_add_remove_member(make_session, session, users, groups): make_session.return_value = session username = '******' groupname = 'team-sre' # add assert (u'User', username) not in groups[groupname].my_members() call_main('group', 'add_member', '--owner', groupname, username) all_members = Group.get(session, name=groupname).my_members() assert (u'User', username) in all_members _, _, _, role, _, _ = all_members[(u'User', username)] assert GROUP_EDGE_ROLES[role] == "owner" # remove call_main('group', 'remove_member', groupname, username) assert (u'User', username) not in Group.get(session, name=groupname).my_members() # bulk add usernames = {'*****@*****.**', '*****@*****.**', '*****@*****.**'} call_main('group', 'add_member', '--member', groupname, *usernames) members = { u for _, u in Group.get(session, name=groupname).my_members().keys() } assert usernames.issubset(members) # bulk remove call_main('group', 'remove_member', groupname, *usernames) members = { u for _, u in Group.get(session, name=groupname).my_members().keys() } assert not members.intersection(usernames) # check user/group name call_main('group', 'add_member', '--member', 'invalid group name', username) assert (u'User', username) not in Group.get(session, name=groupname).my_members() bad_username = '******' call_main('group', 'add_member', '--member', groupname, bad_username) assert (u'User', bad_username) not in Group.get(session, name=groupname).my_members()
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() if not user_role(self.current_user, members) in ("owner", "np-owner"): return self.forbidden() # Enabling and disabling service accounts via the group endpoints is forbidden # because we need the preserve_membership data that is only available via the # UserEnable form. if is_service_account(self.session, group=group): return self.forbidden() group.enable() self.session.commit() AuditLog.log(self.session, self.current_user.id, "enable_group", "Enabled group.", on_group_id=group.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def get(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() 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"]) form.role.data = edge.role form.expiration.data = edge.expiration.strftime("%m/%d/%Y") if edge.expiration else None self.render( "group-edit-member.html", group=group, member=member, edge=edge, form=form, )
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not user_can_manage_group(self.session, group, self.current_user): return self.forbidden() form = GroupRemoveForm(self.request.arguments) if not form.validate(): return self.send_error(status_code=400) member_type, member_name = form.data["member_type"], form.data["member"] members = group.my_members() if not members.get((member_type.capitalize(), member_name), None): return self.notfound() removed_member = get_user_or_group(self.session, member_name, user_or_group=member_type) if self.current_user == removed_member: return self.send_error( status_code=400, reason="Can't remove yourself. Leave group instead." ) if (is_service_account(self.session, group=group) and get_service_account(self.session, group=group).user.name == removed_member.name): return self.send_error( status_code=400, reason="Can't remove a service account user from the service account group." ) group.revoke_member(self.current_user, removed_member, "Removed by owner/np-owner/manager") AuditLog.log(self.session, self.current_user.id, 'remove_from_group', '{} was removed from the group.'.format(removed_member.name), on_group_id=group.id, on_user_id=removed_member.id) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def get(self, request_id, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() my_role = self.current_user.my_role(members) if my_role not in ("manager", "owner", "np-owner"): return self.forbidden() request = self.session.query(Request).filter_by(id=request_id).scalar() if not request: return self.notfound() form = GroupRequestModifyForm(self.request.arguments) form.status.choices = self._get_choices(request.status) updates = request.my_status_updates() self.render( "group-request-update.html", group=group, request=request, members=members, form=form, statuses=REQUEST_STATUS_CHOICES, updates=updates )
def test_group_add_remove_member(make_session, session, users, groups): make_session.return_value = session username = '******' groupname = 'team-sre' # add assert (u'User', username) not in groups[groupname].my_members() call_main('group', 'add_member', '--owner', groupname, username) all_members = Group.get(session, name=groupname).my_members() assert (u'User', username) in all_members _, _, _, role, _, _ = all_members[(u'User', username)] assert GROUP_EDGE_ROLES[role] == "owner" # remove call_main('group', 'remove_member', groupname, username) assert (u'User', username) not in Group.get(session, name=groupname).my_members() # bulk add usernames = {'*****@*****.**', '*****@*****.**', '*****@*****.**'} call_main('group', 'add_member', '--member', groupname, *usernames) members = {u for _, u in Group.get(session, name=groupname).my_members().keys()} assert usernames.issubset(members) # bulk remove call_main('group', 'remove_member', groupname, *usernames) members = {u for _, u in Group.get(session, name=groupname).my_members().keys()} assert not members.intersection(usernames) # check user/group name call_main('group', 'add_member', '--member', 'invalid group name', username) assert (u'User', username) not in Group.get(session, name=groupname).my_members() bad_username = '******' call_main('group', 'add_member', '--member', groupname, bad_username) assert (u'User', bad_username) not in Group.get(session, name=groupname).my_members()
def test_group_add_remove_member(make_session, session, users, groups): make_session.return_value = session username = "******" groupname = "team-sre" # add assert (u"User", username) not in groups[groupname].my_members() call_main("group", "add_member", "--owner", groupname, username) all_members = Group.get(session, name=groupname).my_members() assert (u"User", username) in all_members _, _, _, role, _, _ = all_members[(u"User", username)] assert GROUP_EDGE_ROLES[role] == "owner" # remove call_main("group", "remove_member", groupname, username) assert (u"User", username) not in Group.get(session, name=groupname).my_members() # bulk add usernames = {"*****@*****.**", "*****@*****.**", "*****@*****.**"} call_main("group", "add_member", "--member", groupname, *usernames) members = {u for _, u in Group.get(session, name=groupname).my_members().keys()} assert usernames.issubset(members) # bulk remove call_main("group", "remove_member", groupname, *usernames) members = {u for _, u in Group.get(session, name=groupname).my_members().keys()} assert not members.intersection(usernames) # check user/group name call_main("group", "add_member", "--member", "invalid group name", username) assert (u"User", username) not in Group.get(session, name=groupname).my_members() bad_username = "******" call_main("group", "add_member", "--member", groupname, bad_username) assert (u"User", bad_username) not in Group.get(session, name=groupname).my_members()
def test_grant_and_revoke(session, standard_graph, graph, groups, permissions, http_client, base_url): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): return any( map(lambda x: x.permission == permission_name, graph.permission_metadata[group_name])) # make some permission admins perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) # grant attempt by non-permission admin fe_url = url(base_url, "/permissions/grant/{}".format(group_name)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "specific_arg" }), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert not _check_graph_for_perm(graph), "no permissions granted" # grant by permission admin resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "specific_arg" }), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions granted, successfully" # figure out mapping_id of grant permission_id = Permission.get(session, name=permission_name).id group_id = Group.get(session, name=group_name).id mapping = session.query(PermissionMap).filter( PermissionMap.permission_id == permission_id, PermissionMap.group_id == group_id).first() # revoke permission by non-admin fe_url = url( base_url, "/permissions/{}/revoke/{}".format(permission_name, mapping.id)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': "******"}) graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions not revoked" # revoke permission for realz resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={'X-Grouper-User': user_name}) assert resp.code == 200 graph.update_from_db(session) assert not _check_graph_for_perm(graph), "permissions revoked successfully"
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 _load_permissions_by_group_name(session, group_name): group = Group.get(session, name=group_name) return [name for _, name, _, _, _ in group.my_permissions()]
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 from grouper.models.base.constants import OBJ_TYPES_IDX from grouper.models.user import 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 post(self, request_id, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() members = group.my_members() my_role = user_role(self.current_user, members) if my_role not in ("manager", "owner", "np-owner"): return self.forbidden() request = self.session.query(Request).filter_by(id=request_id).scalar() if not request: return self.notfound() form = GroupRequestModifyForm(self.request.arguments) form.status.choices = self._get_choices(request.status) updates = request.my_status_updates() if not form.validate(): return self.render("group-request-update.html", group=group, request=request, members=members, form=form, alerts=self.get_form_alerts(form.errors), statuses=REQUEST_STATUS_CHOICES, updates=updates) # We have to test this here, too, to ensure that someone can't sneak in with a pending # request that used to be allowed. if form.data["status"] != "cancelled": fail_message = 'This join is denied with this role at this time.' try: user_can_join = assert_can_join(request.requesting, request.get_on_behalf(), role=request.edge.role) except UserNotAuditor as e: user_can_join = False fail_message = e if not user_can_join: return self.render("group-request-update.html", group=group, request=request, members=members, form=form, statuses=REQUEST_STATUS_CHOICES, updates=updates, alerts=[ Alert('danger', fail_message, 'Audit Policy Enforcement') ]) request.update_status(self.current_user, form.data["status"], form.data["reason"]) self.session.commit() AuditLog.log(self.session, self.current_user.id, 'update_request', 'Updated request to status: {}'.format( form.data["status"]), on_group_id=group.id, on_user_id=request.requester.id) edge = self.session.query(GroupEdge).filter_by( id=request.edge_id).one() approver_mail_to = [ user.name for user in group.my_approver_users() if user.name != self.current_user.name and user.name != request.requester.username ] send_email( self.session, approver_mail_to, "Request to join {} by {} has been {}".format( group.groupname, request.requester.name, form.data['status']), "approver_request_updated", settings, { 'group_name': group.name, 'requester': request.requester.username, 'changed_by': self.current_user.name, 'status': form.data['status'], 'role': edge.role, 'reason': form.data['reason'], }, ) if form.data['status'] == 'actioned': send_email( self.session, [request.requester.name], 'Added to group: {}'.format(group.groupname), 'request_actioned', settings, { 'group_name': group.name, 'actioned_by': self.current_user.name, 'reason': form.data['reason'], 'expiration': edge.expiration, 'role': edge.role, }) elif form.data['status'] == 'cancelled': send_email( self.session, [request.requester.name], 'Request to join cancelled: {}'.format(group.groupname), 'request_cancelled', settings, { 'group_name': group.name, 'cancelled_by': self.current_user.name, 'reason': form.data['reason'], 'expiration': edge.expiration, 'role': edge.role, }) # No explicit refresh because handler queries SQL. if form.data['redirect_aggregate']: return self.redirect("/user/requests") else: return self.redirect("/groups/{}/requests".format(group.name))
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() form = GroupJoinForm(self.request.arguments) form.member.choices = self._get_choices(group) if not form.validate(): return self.render("group-join.html", form=form, group=group, alerts=self.get_form_alerts(form.errors)) member = self._get_member(form.data["member"]) fail_message = 'This join is denied with this role at this time.' try: user_can_join = assert_can_join(group, member, role=form.data["role"]) except UserNotAuditor as e: user_can_join = False fail_message = e if not user_can_join: return self.render("group-join.html", form=form, group=group, alerts=[ Alert('danger', fail_message, 'Audit Policy Enforcement') ]) if group.canjoin == "nobody": fail_message = 'This group cannot be joined at this time.' return self.render("group-join.html", form=form, group=group, alerts=[Alert('danger', fail_message)]) # We only use the default expiration time if no expiration time was given # This does mean that if a user wishes to join a group with no expiration # (even with an owner's permission) that has an auto expiration, they must # first be accepted to the group and then have the owner edit the user to # have no expiration. expiration = None if form.data["expiration"]: expiration = datetime.strptime(form.data["expiration"], "%m/%d/%Y") elif group.auto_expire: expiration = datetime.utcnow() + group.auto_expire request = group.add_member(requester=self.current_user, user_or_group=member, reason=form.data["reason"], status=GROUP_JOIN_CHOICES[group.canjoin], expiration=expiration, role=form.data["role"]) self.session.commit() if group.canjoin == 'canask': AuditLog.log(self.session, self.current_user.id, 'join_group', '{} requested to join with role: {}'.format( member.name, form.data["role"]), on_group_id=group.id) mail_to = [ user.name for user in group.my_users() if GROUP_EDGE_ROLES[user.role] in ('manager', 'owner', 'np-owner') ] email_context = { "requester": member.name, "requested_by": self.current_user.name, "request_id": request.id, "group_name": group.name, "reason": form.data["reason"], "expiration": expiration, "role": form.data["role"], "references_header": request.reference_id, } subj = self.render_template('email/pending_request_subj.tmpl', group=group.name, user=self.current_user.name) send_email(self.session, mail_to, subj, 'pending_request', settings, email_context) elif group.canjoin == 'canjoin': AuditLog.log(self.session, self.current_user.id, 'join_group', '{} auto-approved to join with role: {}'.format( member.name, form.data["role"]), on_group_id=group.id) else: raise Exception('Need to update the GroupJoin.post audit logging') return self.redirect("/groups/{}?refresh=yes".format(group.name))
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not self.current_user.can_manage(group): return self.forbidden() members = group.my_members() my_role = self.current_user.my_role(members) form = self.get_form(role=my_role) if not form.validate(): return self.render( "group-add.html", form=form, group=group, alerts=self.get_form_alerts(form.errors) ) member = get_user_or_group(self.session, form.data["member"]) if not member: form.member.errors.append("User or group not found.") elif (member.type, member.name) in group.my_members(): form.member.errors.append("User or group is already a member of this group.") elif group.name == member.name: form.member.errors.append("By definition, this group is a member of itself already.") # Ensure this doesn't violate auditing constraints fail_message = 'This join is denied with this role at this time.' try: user_can_join = assert_can_join(group, member, role=form.data["role"]) except UserNotAuditor as e: user_can_join = False fail_message = e if not user_can_join: form.member.errors.append(fail_message) if form.member.errors: return self.render( "group-add.html", form=form, group=group, alerts=self.get_form_alerts(form.errors) ) expiration = None if form.data["expiration"]: expiration = datetime.strptime(form.data["expiration"], "%m/%d/%Y") group.add_member( requester=self.current_user, user_or_group=member, reason=form.data["reason"], status='actioned', expiration=expiration, role=form.data["role"] ) self.session.commit() AuditLog.log(self.session, self.current_user.id, 'join_group', '{} added to group with role: {}'.format( member.name, form.data["role"]), on_group_id=group.id) if member.type == "User": send_email( self.session, [member.name], 'Added to group: {}'.format(group.name), 'request_actioned', settings, { 'group': group.name, 'actioned_by': self.current_user.name, 'reason': form.data['reason'], 'expiration': expiration, 'role': form.data['role'], } ) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() # Only members can request permissions if not self.current_user.is_member(group.my_members()): return self.forbidden() # check inputs args_by_perm = get_grantable_permissions( self.session, settings.restricted_ownership_permissions) dropdown_form, text_form = GroupPermissionRequest._get_forms( args_by_perm, self.request.arguments) argument_type = self.request.arguments.get("argument_type") if argument_type and argument_type[0] == "text": form = text_form elif argument_type and argument_type[0] == "dropdown": form = dropdown_form form.argument.choices = [ (a, a) for a in args_by_perm[form.permission_name.data] ] else: # someone messing with the form self.log_message("unknown argument type", group_name=group.name, argument_type=argument_type) return self.forbidden() if not form.validate(): return self.render( "group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), alerts=self.get_form_alerts(form.errors), dropdown_help=settings.permission_request_dropdown_help, text_help=settings.permission_request_text_help, ) permission = Permission.get(self.session, form.permission_name.data) assert permission is not None, "our prefilled permission should exist or we have problems" # save off request try: permissions.create_request(self.session, self.current_user, group, permission, form.argument.data, form.reason.data) except permissions.RequestAlreadyGranted: alerts = [ Alert("danger", "This group already has this permission and argument.") ] except permissions.RequestAlreadyExists: alerts = [ Alert( "danger", "Request for permission and argument already exists, please wait patiently." ) ] except permissions.NoOwnersAvailable: self.log_message("prefilled perm+arg have no owner", group_name=group.name, permission_name=permission.name, argument=form.argument.data) alerts = [ Alert( "danger", "No owners available for requested permission and argument." " If this error persists please contact an adminstrator.") ] else: alerts = None if alerts: return self.render( "group-permission-request.html", dropdown_form=dropdown_form, text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm), alerts=alerts, ) else: return self.redirect("/groups/{}".format(group.name))
def post(self, group_id=None, name=None): group = Group.get(self.session, group_id, name) if not group: return self.notfound() if not user_can_manage_group(self.session, group, self.current_user): return self.forbidden() members = group.my_members() my_role = user_role(self.current_user, members) form = self.get_form(role=my_role) if not form.validate(): return self.render( "group-add.html", form=form, group=group, alerts=self.get_form_alerts(form.errors) ) member = get_user_or_group(self.session, form.data["member"]) if member.type == "User" and is_service_account(self.session, member): # For service accounts, we want to always add the group to other groups, not the user member = get_service_account(self.session, user=member).group if not member: form.member.errors.append("User or group not found.") elif (member.type, member.name) in group.my_members(): form.member.errors.append("User or group is already a member of this group.") elif group.name == member.name: form.member.errors.append("By definition, this group is a member of itself already.") # Ensure this doesn't violate auditing constraints fail_message = 'This join is denied with this role at this time.' try: user_can_join = assert_can_join(group, member, role=form.data["role"]) except UserNotAuditor as e: user_can_join = False fail_message = e if not user_can_join: form.member.errors.append(fail_message) if form.member.errors: return self.render( "group-add.html", form=form, group=group, alerts=self.get_form_alerts(form.errors) ) expiration = None if form.data["expiration"]: expiration = datetime.strptime(form.data["expiration"], "%m/%d/%Y") try: group.add_member( requester=self.current_user, user_or_group=member, reason=form.data["reason"], status='actioned', expiration=expiration, role=form.data["role"] ) except InvalidRoleForMember as e: return self.render( "group-add.html", form=form, group=group, alerts=[ Alert('danger', e.message) ] ) self.session.commit() AuditLog.log(self.session, self.current_user.id, 'join_group', '{} added to group with role: {}'.format( member.name, form.data["role"]), on_group_id=group.id) if member.type == "User": send_email( self.session, [member.name], 'Added to group: {}'.format(group.name), 'request_actioned', settings, { 'group_name': group.name, 'actioned_by': self.current_user.name, 'reason': form.data['reason'], 'expiration': expiration, 'role': form.data['role'], } ) return self.redirect("/groups/{}?refresh=yes".format(group.name))
def post(self, name=None): grantable = user_grantable_permissions(self.session, self.current_user) if not grantable: return self.forbidden() group = Group.get(self.session, None, name) if not group: return self.notfound() form = PermissionGrantForm(self.request.arguments) form.permission.choices = [["", "(select one)"]] for perm in grantable: grantable_str = "{} ({})".format(perm[0].name, perm[1]) form.permission.choices.append([perm[0].name, grantable_str]) if not form.validate(): return self.render("permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors)) permission = Permission.get(self.session, form.data["permission"]) if not permission: return self.notfound() # Shouldn't happen. allowed = False for perm in grantable: if perm[0].name == permission.name: if matches_glob(perm[1], form.data["argument"]): allowed = True break if not allowed: form.argument.errors.append( "You do not have grant authority over that permission/argument combination." ) return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) # If the permission is audited, then see if the subtree meets auditing requirements. if permission.audited: fail_message = ( "Permission is audited and this group (or a subgroup) contains " + "owners, np-owners, or managers who have not received audit training." ) try: permission_ok = assert_controllers_are_auditors(group) except UserNotAuditor as e: permission_ok = False fail_message = e if not permission_ok: form.permission.errors.append(fail_message) return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) try: grant_permission(self.session, group.id, permission.id, argument=form.data["argument"]) except IntegrityError: self.session.rollback() form.argument.errors.append( "Permission and Argument already mapped to this group.") return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) self.session.commit() 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) return self.redirect("/groups/{}?refresh=yes".format(group.name))