def get_user_view_template_vars(session, actor, user, graph): # type: (Session, User, User, GroupGraph) -> Dict[str, Any] # TODO(cbguder): get around circular dependencies from grouper.fe.handlers.user_disable import UserDisable from grouper.fe.handlers.user_enable import UserEnable ret = {} # type: Dict[str, Any] if user.is_service_account: ret["can_control"] = can_manage_service_account( session, user.service_account, actor ) or user_is_user_admin(session, actor) ret["can_disable"] = ret["can_control"] ret["can_enable"] = user_is_user_admin(session, actor) ret["can_enable_preserving_membership"] = user_is_user_admin(session, actor) ret["account"] = user.service_account else: ret["can_control"] = user.name == actor.name or user_is_user_admin(session, actor) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable_preserving_membership"] = UserEnable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access_without_membership(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate(session, actor).count() _, ret["num_pending_perm_requests"] = get_requests( session, status="pending", limit=1, offset=0, owner=actor ) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell_metadata = get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) ret["shell"] = shell_metadata.data_value if shell_metadata else "No shell configured" github_username = get_user_metadata_by_key(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY) ret["github_username"] = github_username.data_value if github_username else "(Unset)" ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [ {"name": g.name, "type": "Group", "role": ge._role} for g, ge in group_edge_list ] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens if user.is_service_account: service_account = user.service_account ret["permissions"] = service_account_permissions(session, service_account) else: ret["permissions"] = user_md.get("permissions", []) for permission in ret["permissions"]: permission["granted_on"] = datetime.fromtimestamp(permission["granted_on"]) return ret
def post(self, request_id): # check for request existence request = permissions.get_request_by_id(self.session, request_id) if not request: return self.notfound() # check that this user should be actioning this request user_requests, total = permissions.get_requests( self.session, status="pending", limit=None, offset=0, owner=self.current_user) user_request_ids = [ur.id for ur in user_requests.requests] if request.id not in user_request_ids: return self.forbidden() form = PermissionRequestUpdateForm(self.request.arguments) form.status.choices = self._get_choices(request.status) if not form.validate(): change_comment_list = [ (sc, user_requests.comment_by_status_change_id[sc.id]) for sc in user_requests.status_change_by_request_id[request.id] ] return self.render( "permission-request-update.html", form=form, request=request, change_comment_list=change_comment_list, statuses=REQUEST_STATUS_CHOICES, alerts=self.get_form_alerts(form.errors), ) try: permissions.update_request(self.session, request, self.current_user, form.status.data, form.reason.data) except UserNotAuditor as e: alerts = [Alert("danger", str(e))] change_comment_list = [ (sc, user_requests.comment_by_status_change_id[sc.id]) for sc in user_requests.status_change_by_request_id[request.id] ] return self.render( "permission-request-update.html", form=form, request=request, change_comment_list=change_comment_list, statuses=REQUEST_STATUS_CHOICES, alerts=alerts, ) return self.redirect("/permissions/requests?status=pending")
def post(self, request_id): # check for request existence request = permissions.get_request_by_id(self.session, request_id) if not request: return self.notfound() # check that this user should be actioning this request user_requests, total = permissions.get_requests( self.session, status="pending", limit=None, offset=0, owner=self.current_user ) user_request_ids = [ur.id for ur in user_requests.requests] if request.id not in user_request_ids: return self.forbidden() form = PermissionRequestUpdateForm(self.request.arguments) form.status.choices = self._get_choices(request.status) if not form.validate(): change_comment_list = [ (sc, user_requests.comment_by_status_change_id[sc.id]) for sc in user_requests.status_change_by_request_id[request.id] ] return self.render( "permission-request-update.html", form=form, request=request, change_comment_list=change_comment_list, statuses=REQUEST_STATUS_CHOICES, alerts=self.get_form_alerts(form.errors), ) try: permissions.update_request( self.session, request, self.current_user, form.status.data, form.reason.data ) except UserNotAuditor as e: alerts = [Alert("danger", str(e))] change_comment_list = [ (sc, user_requests.comment_by_status_change_id[sc.id]) for sc in user_requests.status_change_by_request_id[request.id] ] return self.render( "permission-request-update.html", form=form, request=request, change_comment_list=change_comment_list, statuses=REQUEST_STATUS_CHOICES, alerts=alerts, ) return self.redirect("/permissions/requests?status=pending")
def get(self): form = PermissionRequestsForm(self.request.arguments) form.status.choices = [("", "")] + [(k, k) for k in REQUEST_STATUS_CHOICES] if not form.validate(): alerts = self.get_form_alerts(form.errors) request_tuple = None total = 0 granters_by_arg_by_perm = None else: alerts = [] owners_by_arg_by_perm = permissions.get_owners_by_grantable_permission( self.session) if form.direction.data == "Waiting my approval": owner = self.current_user requester = None else: # "Requested by me" owner = None requester = self.current_user request_tuple, total = permissions.get_requests( self.session, status=form.status.data, limit=form.limit.data, offset=form.offset.data, owner=owner, requester=requester, owners_by_arg_by_perm=owners_by_arg_by_perm, ) granters_by_arg_by_perm = defaultdict(dict) for request in request_tuple.requests: owners = permissions.get_owner_arg_list( self.session, request.permission, request.argument, owners_by_arg_by_perm=owners_by_arg_by_perm, ) granters = [owner_pair[0].name for owner_pair in owners] granters_by_arg_by_perm[request.permission.name][ request.argument] = granters return self.render( "permission-requests.html", form=form, request_tuple=request_tuple, granters=granters_by_arg_by_perm, alerts=alerts, total=total, statuses=REQUEST_STATUS_CHOICES, )
def get(self): form = PermissionRequestsForm(self.request.arguments) form.status.choices = [("", "")] + [(k, k) for k in REQUEST_STATUS_CHOICES] if not form.validate(): alerts = self.get_form_alerts(form.errors) request_tuple = None total = 0 granters_by_arg_by_perm = None else: alerts = [] owners_by_arg_by_perm = permissions.get_owners_by_grantable_permission(self.session) if form.direction.data == "Waiting my approval": owner = self.current_user requester = None else: # "Requested by me" owner = None requester = self.current_user request_tuple, total = permissions.get_requests( self.session, status=form.status.data, limit=form.limit.data, offset=form.offset.data, owner=owner, requester=requester, owners_by_arg_by_perm=owners_by_arg_by_perm, ) granters_by_arg_by_perm = defaultdict(dict) for request in request_tuple.requests: owners = permissions.get_owner_arg_list( self.session, request.permission, request.argument, owners_by_arg_by_perm=owners_by_arg_by_perm, ) granters = [owner_pair[0].name for owner_pair in owners] granters_by_arg_by_perm[request.permission.name][request.argument] = granters return self.render( "permission-requests.html", form=form, request_tuple=request_tuple, granters=granters_by_arg_by_perm, alerts=alerts, total=total, statuses=REQUEST_STATUS_CHOICES, )
def test_permission_request_flow( session, standard_graph, groups, grantable_permissions, http_client, base_url # noqa: F811 ): """Test that a permission request gets into the system correctly and notifications are sent correctly.""" perm_grant, _, perm1, perm2 = grantable_permissions grant_permission(groups["all-teams"], perm_grant, argument="grantable.*") grant_permission(groups["security-team"], perm_grant, argument="grantable.one") grant_permission(groups["tech-ops"], perm_grant, argument="grantable.two") # REQUEST: permission with an invalid argument groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission_name": "grantable.one", "argument": "some argument?", "reason": "blah blah black sheep", "argument_type": "text", }), headers={"X-Grouper-User": username}, ) assert resp.code == 200 assert b"Field must match" in resp.body emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 0, "no emails queued" # REQUEST: 'grantable.one', 'some argument' for 'serving-team' resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission_name": "grantable.one", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text", }), headers={"X-Grouper-User": username}, ) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert_same_recipients(emails, ["*****@*****.**", "*****@*****.**"]) perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 1 assert "grantable.one" not in perms, "requested permission shouldn't be granted immediately" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len( request_tuple.requests) == 0, "random user shouldn't have a request" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len(request_tuple.requests ) == 1, "user in group with grant should have a request" # APPROVE grant: have '*****@*****.**' action this request as owner of # 'all-teams' which has the grant permission for the requested permission request_id = request_tuple.requests[0].id fe_url = url(base_url, "/permissions/requests/{}".format(request_id)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "status": "actioned", "reason": "lgtm" }), headers={"X-Grouper-User": user.name}, ) assert resp.code == 200 perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 2 assert "grantable.one" in perms, "requested permission shouldn't be granted immediately" emails = _get_unsent_and_mark_as_sent_emails(session) assert_same_recipients(emails, ["*****@*****.**"]) # (re)REQUEST: 'grantable.one', 'some argument' for 'serving-team' groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission_name": "grantable.one", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text", }), headers={"X-Grouper-User": username}, ) assert resp.code == 200 user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len( request_tuple.requests) == 0, "request for existing perm should fail" # REQUEST: 'grantable.two', 'some argument' for 'serving-team' groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission_name": "grantable.two", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text", }), headers={"X-Grouper-User": username}, ) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) # because tech-ops team doesn't have an email, all of its members should get emailed instead assert_same_recipients( emails, ["*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**"]) perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 2 assert "grantable.two" not in perms, "requested permission shouldn't be granted immediately" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len( request_tuple.requests) == 0, "random user shouldn't have a request" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len(request_tuple.requests ) == 1, "user in group with grant should have a request" # CANCEL request: have '*****@*****.**' cancel this request request_id = request_tuple.requests[0].id fe_url = url(base_url, "/permissions/requests/{}".format(request_id)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "status": "cancelled", "reason": "heck no" }), headers={"X-Grouper-User": user.name}, ) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert_same_recipients(emails, ["*****@*****.**"]) perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 2 assert "grantable.two" not in perms, "no new permissions should be granted for this"
def get_user_view_template_vars(session, actor, user, graph): # type: (Session, User, User, GroupGraph) -> Dict[str, Any] # TODO(cbguder): get around circular dependencies from grouper.fe.handlers.user_disable import UserDisable from grouper.fe.handlers.user_enable import UserEnable ret = {} # type: Dict[str, Any] if user.is_service_account: ret["can_control"] = can_manage_service_account( session, user.service_account, actor ) or user_is_user_admin(session, actor) ret["can_disable"] = ret["can_control"] ret["can_enable"] = user_is_user_admin(session, actor) ret["can_enable_preserving_membership"] = user_is_user_admin(session, actor) ret["account"] = user.service_account else: ret["can_control"] = user.name == actor.name or user_is_user_admin(session, actor) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable_preserving_membership"] = UserEnable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access_without_membership(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate(session, actor).count() _, ret["num_pending_perm_requests"] = get_requests( session, status="pending", limit=1, offset=0, owner=actor ) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell = ( get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY).data_value if get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) else "No shell configured" ) ret["shell"] = shell github_username = get_user_metadata_by_key(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY) ret["github_username"] = github_username.data_value if github_username else "(Unset)" ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [ {"name": g.name, "type": "Group", "role": ge._role} for g, ge in group_edge_list ] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens if user.is_service_account: service_account = user.service_account ret["permissions"] = service_account_permissions(session, service_account) else: ret["permissions"] = user_md.get("permissions", []) for permission in ret["permissions"]: permission["granted_on"] = datetime.fromtimestamp(permission["granted_on"]) return ret
def test_permission_request_flow( session, standard_graph, groups, grantable_permissions, http_client, base_url # noqa: F811 ): """Test that a permission request gets into the system correctly and notifications are sent correctly.""" perm_grant, _, perm1, perm2 = grantable_permissions grant_permission(groups["all-teams"], perm_grant, argument="grantable.*") grant_permission(groups["security-team"], perm_grant, argument="grantable.one") grant_permission(groups["tech-ops"], perm_grant, argument="grantable.two") # REQUEST: permission with an invalid argument groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode( { "permission_name": "grantable.one", "argument": "some argument?", "reason": "blah blah black sheep", "argument_type": "text", } ), headers={"X-Grouper-User": username}, ) assert resp.code == 200 assert b"Field must match" in resp.body emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 0, "no emails queued" # REQUEST: 'grantable.one', 'some argument' for 'serving-team' resp = yield http_client.fetch( fe_url, method="POST", body=urlencode( { "permission_name": "grantable.one", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text", } ), headers={"X-Grouper-User": username}, ) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert_same_recipients(emails, [u"*****@*****.**", u"*****@*****.**"]) perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 1 assert "grantable.one" not in perms, "requested permission shouldn't be granted immediately" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len(request_tuple.requests) == 0, "random user shouldn't have a request" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len(request_tuple.requests) == 1, "user in group with grant should have a request" # APPROVE grant: have '*****@*****.**' action this request as owner of # 'all-teams' which has the grant permission for the requested permission request_id = request_tuple.requests[0].id fe_url = url(base_url, "/permissions/requests/{}".format(request_id)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"status": "actioned", "reason": "lgtm"}), headers={"X-Grouper-User": user.name}, ) assert resp.code == 200 perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 2 assert "grantable.one" in perms, "requested permission shouldn't be granted immediately" emails = _get_unsent_and_mark_as_sent_emails(session) assert_same_recipients(emails, [u"*****@*****.**"]) # (re)REQUEST: 'grantable.one', 'some argument' for 'serving-team' groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode( { "permission_name": "grantable.one", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text", } ), headers={"X-Grouper-User": username}, ) assert resp.code == 200 user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len(request_tuple.requests) == 0, "request for existing perm should fail" # REQUEST: 'grantable.two', 'some argument' for 'serving-team' groupname = "serving-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode( { "permission_name": "grantable.two", "argument": "some argument", "reason": "blah blah black sheep", "argument_type": "text", } ), headers={"X-Grouper-User": username}, ) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) # because tech-ops team doesn't have an email, all of its members should get emailed instead assert_same_recipients( emails, [u"*****@*****.**", u"*****@*****.**", u"*****@*****.**", u"*****@*****.**"] ) perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 2 assert "grantable.two" not in perms, "requested permission shouldn't be granted immediately" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len(request_tuple.requests) == 0, "random user shouldn't have a request" user = User.get(session, name="*****@*****.**") request_tuple, total = get_requests(session, "pending", 10, 0, owner=user) assert len(request_tuple.requests) == 1, "user in group with grant should have a request" # CANCEL request: have '*****@*****.**' cancel this request request_id = request_tuple.requests[0].id fe_url = url(base_url, "/permissions/requests/{}".format(request_id)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"status": "cancelled", "reason": "heck no"}), headers={"X-Grouper-User": user.name}, ) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert_same_recipients(emails, [u"*****@*****.**"]) perms = _load_permissions_by_group_name(session, "serving-team") assert len(perms) == 2 assert "grantable.two" not in perms, "no new permissions should be granted for this"
def get_user_view_template_vars(session, actor, user, graph): # TODO(cbguder): get around circular dependencies from grouper.fe.handlers.user_disable import UserDisable from grouper.fe.handlers.user_enable import UserEnable ret = {} if user.is_service_account: ret["can_control"] = can_manage_service_account( session, user.service_account, actor ) or user_is_user_admin(session, actor) ret["can_disable"] = ret["can_control"] ret["can_enable"] = user_is_user_admin(session, actor) ret["can_enable_preserving_membership"] = user_is_user_admin(session, actor) ret["account"] = user.service_account else: ret["can_control"] = user.name == actor.name or user_is_user_admin(session, actor) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable_preserving_membership"] = UserEnable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access_without_membership(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate(session, actor).count() _, ret["num_pending_perm_requests"] = get_requests( session, status="pending", limit=1, offset=0, owner=actor ) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell = ( get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY).data_value if get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) else "No shell configured" ) ret["shell"] = shell ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [ {"name": g.name, "type": "Group", "role": ge._role} for g, ge in group_edge_list ] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) for key in ret["public_keys"]: key.tags = get_public_key_tags(session, key) key.pretty_permissions = [ "{} ({})".format(perm.name, perm.argument if perm.argument else "unargumented") for perm in get_public_key_permissions(session, key) ] ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens if user.is_service_account: service_account = user.service_account ret["permissions"] = service_account_permissions(session, service_account) else: ret["permissions"] = user_md.get("permissions", []) return ret