def test_grant_permission_to_tag(users, http_client, base_url, session): # noqa: F811 user = session.query(User).filter_by(username="******").scalar() perm = create_permission(session, TAG_EDIT) session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, TAG_EDIT), "*", ) fe_url = url(base_url, "/tags") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"tagname": "tyler_was_here", "description": "Test Tag Please Ignore"}), headers={"X-Grouper-User": user.username}, ) tag = PublicKeyTag.get(session, name="tyler_was_here") user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, "/permissions/grant_tag/{}".format(tag.name)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"permission": TAG_EDIT, "argument": "*"}), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") perm = get_permission(session, TAG_EDIT) assert ( len(get_public_key_tag_permissions(session, tag)) == 1 ), "The tag should have exactly 1 permission" assert ( get_public_key_tag_permissions(session, tag)[0].name == perm.name ), "The tag's permission should be the one we added" assert ( get_public_key_tag_permissions(session, tag)[0].argument == "*" ), "The tag's permission should be the one we added" # Make sure trying to add a permission to a tag doesn't fail horribly if it's already there user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, "/permissions/grant_tag/{}".format(tag.name)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"permission": TAG_EDIT, "argument": "*"}), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200
def get(self): self.render( "help.html", how_to_get_help=settings.how_to_get_help, site_docs=settings.site_docs, grant_perm=get_permission(self.session, PERMISSION_GRANT), create_perm=get_permission(self.session, PERMISSION_CREATE), audit_perm=get_permission(self.session, PERMISSION_AUDITOR), tag_edit=get_permission(self.session, TAG_EDIT), )
def get(self, *args, **kwargs): # type: (*Any, **Any) -> None self.render( "help.html", how_to_get_help=settings().how_to_get_help, site_docs=settings().site_docs, grant_perm=get_permission(self.session, PERMISSION_GRANT), create_perm=get_permission(self.session, PERMISSION_CREATE), audit_perm=get_permission(self.session, PERMISSION_AUDITOR), )
def test_revoke_permission_from_tag(users, http_client, base_url, session): # noqa: F811 user = session.query(User).filter_by(username="******").scalar() create_permission(session, TAG_EDIT) session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, TAG_EDIT), "*", ) fe_url = url(base_url, "/tags") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"tagname": "tyler_was_here", "description": "Test Tag Please Ignore"}), headers={"X-Grouper-User": user.username}, ) tag = PublicKeyTag.get(session, name="tyler_was_here") user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, "/permissions/grant_tag/{}".format(tag.name)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"permission": TAG_EDIT, "argument": "*"}), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") get_permission(session, TAG_EDIT) assert ( len(get_public_key_tag_permissions(session, tag)) == 1 ), "The tag should have exactly 1 permission" user = session.query(User).filter_by(username="******").scalar() mapping = get_public_key_tag_permissions(session, tag)[0] fe_url = url(base_url, "/permissions/{}/revoke_tag/{}".format(TAG_EDIT, mapping.mapping_id)) resp = yield http_client.fetch( fe_url, method="POST", body="", headers={"X-Grouper-User": user.username} ) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") assert ( len(get_public_key_tag_permissions(session, tag)) == 0 ), "The tag should have no permissions"
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, name=None): tag = PublicKeyTag.get(self.session, None, name) if not tag: return self.notfound() if not user_has_permission(self.session, self.current_user, TAG_EDIT, tag.name): return self.forbidden() form = PermissionGrantTagForm(self.request.arguments) form.permission.choices = [["", "(select one)"]] for perm in get_all_permissions(self.session): form.permission.choices.append( [perm.name, "{} (*)".format(perm.name)]) if not form.validate(): return self.render( "permission-grant-tag.html", form=form, tag=tag, alerts=self.get_form_alerts(form.errors), ) permission = get_permission(self.session, form.data["permission"]) if not permission: return self.notfound() # Shouldn't happen. success = grant_permission_to_tag(self.session, tag.id, permission.id, argument=form.data["argument"]) if not success: form.argument.errors.append( "Permission and Argument already mapped to this tag.") return self.render( "permission-grant-tag.html", form=form, tag=tag, alerts=self.get_form_alerts(form.errors), ) AuditLog.log( self.session, self.current_user.id, "grant_permission_tag", "Granted permission with argument: {}".format( form.data["argument"]), on_permission_id=permission.id, on_tag_id=tag.id, ) return self.redirect("/tags/{}?refresh=yes".format(tag.name))
def test_permission_disable(setup): # type: (SetupTest) -> None with setup.transaction(): setup.grant_permission_to_group(PERMISSION_ADMIN, "", "admins") setup.add_user_to_group("*****@*****.**", "admins") setup.create_permission("some-permission") run_ctl(setup, "permission", "-a", "*****@*****.**", "disable", "some-permission") permission = get_permission(setup.session, "some-permission") assert permission assert not permission.enabled
def test_permission_exclude_inactive_groups(session, standard_graph): # noqa: F811 """Ensure disabled groups are excluded from permission data.""" group = Group.get(session, name="team-sre") permission = get_permission(session, "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 test_edit_tag(users, http_client, base_url, session): # noqa: F811 user = session.query(User).filter_by(username="******").scalar() create_permission(session, TAG_EDIT) session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, TAG_EDIT), "*", ) fe_url = url(base_url, "/tags") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "tagname": "tyler_was_here", "description": "Test Tag Please Ignore" }), headers={"X-Grouper-User": user.username}, ) tag = PublicKeyTag.get(session, name="tyler_was_here") assert (tag.description == "Test Tag Please Ignore" ), "The description should match what we created it with" user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, "/tags/{}/edit".format(tag.id)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"description": "Don't tag me bro"}), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 tag = PublicKeyTag.get(session, name="tyler_was_here") assert tag.description == "Don't tag me bro", "The description should have been updated"
def get(self, name=None): # TODO: use cached data instead, add refresh to appropriate redirects. permission = get_permission(self.session, name) if not permission: return self.notfound() can_change_audit_status = user_is_permission_admin( self.session, self.current_user) can_disable = user_is_permission_admin(self.session, self.current_user) mapped_groups = get_groups_by_permission(self.session, permission) log_entries = get_log_entries_by_permission(self.session, permission) self.render( "permission.html", permission=permission, can_disable=can_disable, mapped_groups=mapped_groups, log_entries=log_entries, can_change_audit_status=can_change_audit_status, )
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] + ["*****@*****.**"]) # 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 = 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, 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 = get_permission(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))
def post(self, *args, **kwargs): # type: (*Any, **Any) -> None group_id = kwargs.get("group_id") # type: Optional[int] name = kwargs.get("name") # type: Optional[str] 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].decode() == "text": form = text_form elif argument_type and argument_type[0].decode() == "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 = get_permission(self.session, form.permission_name.data) assert permission is not None, "our prefilled permission should exist or we have problems" # save off request try: request = 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.", ) ] except UserNotAuditor as e: alerts = [Alert("danger", str(e))] else: alerts = [] 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("/permissions/requests/{}".format(request.id))
def test_permissions(users, http_client, base_url, session): # noqa: F811 user = session.query(User).filter_by(username="******").scalar() create_permission(session, TAG_EDIT) session.commit() create_permission(session, "it.literally.does.not.matter") session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, TAG_EDIT), "*", ) grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, "it.literally.does.not.matter"), "*", ) fe_url = url(base_url, "/tags") yield http_client.fetch( fe_url, method="POST", body=urlencode({"tagname": "tyler_was_here", "description": "Test Tag Please Ignore"}), headers={"X-Grouper-User": user.username}, ) tag = PublicKeyTag.get(session, name="tyler_was_here") user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, "/permissions/grant_tag/{}".format(tag.name)) yield http_client.fetch( fe_url, method="POST", body=urlencode({"permission": TAG_EDIT, "argument": "prod"}), headers={"X-Grouper-User": user.username}, ) user = session.query(User).filter_by(username="******").scalar() # add SSH key fe_url = url(base_url, "/users/{}/public-key/add".format(user.username)) yield http_client.fetch( fe_url, method="POST", body=urlencode({"public_key": SSH_KEY_1}), headers={"X-Grouper-User": user.username}, ) key = session.query(PublicKey).filter_by(user_id=user.id).scalar() user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, "/users/{}/public-key/{}/tag".format(user.username, key.id)) yield http_client.fetch( fe_url, method="POST", body=urlencode({"tagname": "tyler_was_here"}), headers={"X-Grouper-User": user.username}, ) user = session.query(User).filter_by(username="******").scalar() key = session.query(PublicKey).filter_by(user_id=user.id).scalar() assert ( len(get_public_key_permissions(session, key)) == 1 ), "The SSH Key should have only 1 permission" assert ( get_public_key_permissions(session, key)[0].name == TAG_EDIT ), "The SSH key's permission should be TAG_EDIT" assert ( get_public_key_permissions(session, key)[0].argument == "prod" ), "The SSH key's permission argument should be restricted to the tag's argument" assert len(user_permissions(session, user)) > 1, "The user should have more than 1 permission"
def test_grant_and_revoke( session, standard_graph, graph, groups, permissions, http_client, base_url # noqa: F811 ): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): # type: (GroupGraph) -> bool return any( [ g["permission"] == permission_name and g["distance"] == 0 for g in graph.get_group_details(group_name)["permissions"] ] ) # make some permission admins grant_permission(groups["security-team"], permissions[PERMISSION_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 = get_permission(session, 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): 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].decode() == "text": form = text_form elif argument_type and argument_type[0].decode() == "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 = get_permission(self.session, form.permission_name.data) assert permission is not None, "our prefilled permission should exist or we have problems" # save off request try: request = 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.", ) ] except UserNotAuditor as e: alerts = [Alert("danger", str(e))] 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("/permissions/requests/{}".format(request.id))
def test_exclude_disabled_permissions( session, standard_graph, graph, users, groups, permissions # noqa: F811 ): """ Ensure that disabled permissions are excluded from various functions/methods that return data from the models. """ perm_ssh = get_permission(session, "ssh") perm_grant = create_permission(session, PERMISSION_GRANT) session.commit() # this user has grouper.permission.grant with argument "ssh/*" grant_permission(groups["group-admins"], perm_grant, argument="ssh/*") graph.update_from_db(session) grant_perms = [ x for x in user_permissions(session, users["*****@*****.**"]) if x.name == PERMISSION_GRANT ] assert "ssh" == filter_grantable_permissions(session, grant_perms)[0][0].name assert "ssh" in (p.name for p in get_all_permissions(session)) assert "ssh" in (p.name for p in get_all_permissions(session, include_disabled=False)) assert "ssh" in (p.name for p in get_all_permissions(session, include_disabled=True)) assert "ssh" in get_grantable_permissions(session, []) assert "team-sre" in [g[0] for g in get_groups_by_permission(session, perm_ssh)] assert get_owner_arg_list(session, perm_ssh, "*") assert "ssh" in get_owners_by_grantable_permission(session) assert "ssh" in (x[0].name for x in user_grantable_permissions(session, users["*****@*****.**"])) assert user_has_permission(session, users["*****@*****.**"], "ssh") assert "ssh" in (p.name for p in user_permissions(session, users["*****@*****.**"])) assert "ssh" in (p["permission"] for p in graph.get_group_details("team-sre")["permissions"]) assert "ssh" in (pt.name for pt in graph.get_permissions()) assert "team-sre" in graph.get_permission_details("ssh")["groups"] assert "ssh" in (p["permission"] for p in graph.get_user_details("*****@*****.**")["permissions"]) # now disable the ssh permission disable_permission(session, "ssh", users["*****@*****.**"].id) graph.update_from_db(session) grant_perms = [ x for x in user_permissions(session, users["*****@*****.**"]) if x.name == PERMISSION_GRANT ] assert not filter_grantable_permissions(session, grant_perms) assert "ssh" not in (p.name for p in get_all_permissions(session)) assert "ssh" not in (p.name for p in get_all_permissions(session, include_disabled=False)) assert "ssh" in (p.name for p in get_all_permissions(session, include_disabled=True)) assert "ssh" not in get_grantable_permissions(session, []) assert not get_groups_by_permission(session, perm_ssh) assert not get_owner_arg_list(session, perm_ssh, "*") assert "ssh" not in get_owners_by_grantable_permission(session) assert "ssh" not in ( x[0].name for x in user_grantable_permissions(session, users["*****@*****.**"]) ) assert not user_has_permission(session, users["*****@*****.**"], "ssh") assert "ssh" not in (p.name for p in user_permissions(session, users["*****@*****.**"])) assert "ssh" not in ( p["permission"] for p in graph.get_group_details("team-sre")["permissions"] ) assert "ssh" not in (pt.name for pt in graph.get_permissions()) assert not graph.get_permission_details("ssh")["groups"] assert "ssh" not in ( p["permission"] for p in graph.get_user_details("*****@*****.**")["permissions"] )
def post(self, *args: Any, **kwargs: Any) -> None: name = self.get_path_argument("name") grantable = user_grantable_permissions(self.session, self.current_user) if not grantable: return self.forbidden() group = Group.get(self.session, name=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 = get_permission(self.session, form.data["permission"]) if not permission: return self.notfound() # Shouldn't happen. argument = form.argument.data.strip() allowed = False for perm in grantable: if perm[0].name == permission.name: if matches_glob(perm[1], 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: try: assert_controllers_are_auditors(group) except UserNotAuditor as e: form.permission.errors.append(str(e)) return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) try: self.plugins.check_permission_argument(permission.name, argument) grant_permission(self.session, group.id, permission.id, argument=argument) except PluginRejectedPermissionArgument as e: self.session.rollback() form.argument.errors.append(f"Rejected by plugin: {e}") return self.render( "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors), ) 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))
def post(self, *args, **kwargs): # type: (*Any, **Any) -> None form, args_by_perm = self._build_form(self.request.arguments) if not form.validate(): return self.render( "permission-request.html", args_by_perm_json=json.dumps(args_by_perm), form=form, alerts=self.get_form_alerts(form.errors), ) group = Group.get(self.session, name=form.group_name.data) if group is None: raise HTTPError(status_code=400, reason="that group does not exist") permission = get_permission(self.session, form.permission_name.data) if permission is None: raise HTTPError(status_code=400, reason="that permission does not exist") if permission.name not in args_by_perm: raise HTTPError(status_code=400, reason="that permission was not in the form") # save off request try: request = 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.", ) ] except UserNotAuditor as e: alerts = [Alert("danger", str(e))] else: alerts = [] if alerts: return self.render( "permission-request.html", args_by_perm_json=json.dumps(args_by_perm), form=form, alerts=alerts, ) else: return self.redirect("/permissions/requests/{}".format(request.id))
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 = get_permission(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 test_tags(session, http_client, base_url, graph): # noqa: F811 perm = create_permission(session, TAG_EDIT) session.commit() create_permission(session, "it.literally.does.not.matter") session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, TAG_EDIT), "*", ) grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, "it.literally.does.not.matter"), "*", ) tag = PublicKeyTag(name="tyler_was_here") tag.add(session) session.commit() tag = PublicKeyTag.get(session, name="tyler_was_here") grant_permission_to_tag(session, tag.id, perm.id, "prod") with pytest.raises(AssertionError): grant_permission_to_tag(session, tag.id, perm.id, "question?") user = session.query(User).filter_by(username="******").scalar() add_public_key(session, user, SSH_KEY_1) key = session.query(PublicKey).filter_by(user_id=user.id).scalar() add_tag_to_public_key(session, key, tag) user = session.query(User).filter_by(username="******").scalar() key = session.query(PublicKey).filter_by(user_id=user.id).scalar() assert ( len(get_public_key_permissions(session, key)) == 1 ), "The SSH Key should have only 1 permission" assert ( get_public_key_permissions(session, key)[0].name == TAG_EDIT ), "The SSH key's permission should be TAG_EDIT" assert ( get_public_key_permissions(session, key)[0].argument == "prod" ), "The SSH key's permission argument should be restricted to the tag's argument" assert len(user_permissions(session, user)) > 1, "The user should have more than 1 permission" graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) pub_key = body["data"]["user"]["public_keys"][0] assert len(pub_key["tags"]) == 1, "The public key should only have 1 tag" assert pub_key["fingerprint"] == "e9:ae:c5:8f:39:9b:3a:9c:6a:b8:33:6b:cb:6f:ba:35" assert pub_key["fingerprint_sha256"] == "MP9uWaujW96EWxbjDtPdPWheoMDu6BZ8FZj0+CBkVWU" assert pub_key["tags"][0] == "tyler_was_here", "The public key should have the tag we gave it"
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 = get_permission(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))
def test_grant_and_revoke( session, standard_graph, graph, groups, permissions, http_client, base_url # noqa: F811 ): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): # type: (GroupGraph) -> bool return any([ g["permission"] == permission_name and g["distance"] == 0 for g in graph.get_group_details(group_name)["permissions"] ]) # make some permission admins grant_permission(groups["security-team"], permissions[PERMISSION_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 = get_permission(session, 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 sync_db_command(args): # Models not implicitly or explictly imported above are explicitly imported here from grouper.models.perf_profile import PerfProfile # noqa: F401 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 = get_permission(session, name) if test: continue try: create_permission(session, name, description) 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 = get_permission(session, permission_name) assert permission, "Permission should have been created earlier!" grant_permission(session, admin_group.id, permission.id) session.commit() auditors_group_name = get_auditors_group_name(settings) auditors_group = Group.get(session, name=auditors_group_name) if not auditors_group: auditors_group = Group( groupname=auditors_group_name, description= "Group for auditors, who can be owners of audited groups.", canjoin="canjoin", ) try: auditors_group.add(session) session.flush() except IntegrityError: session.rollback() raise Exception( "Failed to create group: {}".format(auditors_group_name)) permission = get_permission(session, PERMISSION_AUDITOR) assert permission, "Permission should have been created earlier!" grant_permission(session, auditors_group.id, permission.id) session.commit()
def test_disabling_permission( session, groups, standard_graph, graph, http_client, base_url # noqa: F811 ): """ This tests disabling via the front-end route, including checking that the user is authorized to disable permissions. """ perm_name = "sudo" nonpriv_user_name = "*****@*****.**" # user without PERMISSION_ADMIN nonpriv_headers = {"X-Grouper-User": nonpriv_user_name} priv_user_name = "*****@*****.**" # user with PERMISSION_ADMIN priv_headers = {"X-Grouper-User": priv_user_name} disable_url = url(base_url, "/permissions/{}/disable".format(perm_name)) disable_url_non_exist_perm = url(base_url, "/permissions/no.exists/disable") assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") # attempt to disable the permission -> should fail cuz actor # doesn't have PERMISSION_ADMIN with pytest.raises(HTTPError) as exc: yield http_client.fetch(disable_url, method="POST", headers=nonpriv_headers, body="") assert exc.value.code == 403 # check that no change assert get_permission(session, perm_name).enabled graph.update_from_db(session) assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") # an actor with PERMISSION_ADMIN is allowed to disable the # permission resp = yield http_client.fetch(disable_url, method="POST", headers=priv_headers, body="") assert resp.code == 200 assert not get_permission(session, perm_name).enabled graph.update_from_db(session) assert "sudo:shell" not in get_user_permissions(graph, "*****@*****.**") assert "sudo:shell" not in get_user_permissions(graph, "*****@*****.**") with pytest.raises(HTTPError) as exc: yield http_client.fetch( disable_url_non_exist_perm, method="POST", headers=priv_headers, body="" ) assert exc.value.code == 404 # # make sure that when disabling the permission, all mappings of # it, i.e., with different arguments, are disabled # # the standard_graph grants 'ssh' with args '*' and 'shell' to two # different groups assert "ssh:*" in get_group_permissions(graph, "team-sre") assert "ssh:shell" in get_group_permissions(graph, "tech-ops") # disable the perm disable_url_ssh_pem = url(base_url, "/permissions/ssh/disable") resp = yield http_client.fetch( disable_url_ssh_pem, method="POST", headers=priv_headers, body="" ) assert resp.code == 200 assert not get_permission(session, "ssh").enabled graph.update_from_db(session) assert "ssh:*" not in get_group_permissions(graph, "team-sre") assert "ssh:shell" not in get_group_permissions(graph, "tech-ops")
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 = get_permission(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) )