def test_edit_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") 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 test_group_logdump(make_session, session, users, groups, tmpdir): make_session.return_value = session groupname = 'team-sre' group_id = groups[groupname].id yesterday = date.today() - timedelta(days=1) fn = tmpdir.join('out.csv').strpath call_main('group', 'log_dump', groupname, yesterday.isoformat(), '--outfile', fn) with open(fn, 'r') as fh: out = fh.read() assert not out, 'nothing yet' AuditLog.log(session, users['*****@*****.**'].id, 'make_noise', 'making some noise', on_group_id=group_id) session.commit() call_main('group', 'log_dump', groupname, yesterday.isoformat(), '--outfile', fn) with open(fn, 'r') as fh: entries = [x for x in csv.reader(fh)] assert len(entries) == 1, 'should capture our new audit log entry' log_time, actor, description, action, extra = entries[0] assert groupname in extra
def test_limited_permissions_global_approvers(session, standard_graph, groups, grantable_permissions, http_client, base_url): """Test that notifications are not sent to global approvers.""" perm_grant, _, perm1, _ = grantable_permissions perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() # one circuit-breaking admin grant, one wildcard grant grant_permission(groups["sad-team"], perm_admin, argument="") grant_permission(groups["security-team"], perm_grant, argument="grantable.*") security_team_members = {name for (t, name) in groups['security-team'].my_members().keys() if t == 'User'} # SPECIFIC REQUEST: 'grantable.one', 'specific_arg' for 'sad-team' groupname = "sad-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": perm1.name, "argument": "specific_arg", "reason": "blah blah black sheep", "argument_type": "text"}), headers={'X-Grouper-User': username}) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 2, "email only sent to security-team" assert not security_team_members.difference(e.email for e in emails), \ "only security-team members get notification"
def test_graph_desc_to_ances(session, graph, users, groups): # noqa """ Test adding members where all descendants already exist.""" setup_desc_to_ances(session, users, groups) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
def test_group_logdump(make_session, session, users, groups, tmpdir): make_session.return_value = session groupname = "team-sre" group_id = groups[groupname].id yesterday = date.today() - timedelta(days=1) fn = tmpdir.join("out.csv").strpath call_main("group", "log_dump", groupname, yesterday.isoformat(), "--outfile", fn) with open(fn, "r") as fh: out = fh.read() assert not out, "nothing yet" AuditLog.log(session, users["*****@*****.**"].id, "make_noise", "making some noise", on_group_id=group_id) session.commit() call_main("group", "log_dump", groupname, yesterday.isoformat(), "--outfile", fn) with open(fn, "r") as fh: entries = [x for x in csv.reader(fh)] assert len(entries) == 1, "should capture our new audit log entry" log_time, actor, description, action, extra = entries[0] assert groupname in extra
def test_basic_request(graph, groups, permissions, session, standard_graph, users): group_sre = groups["team-sre"] group_not_sre = [g for name, g in groups.items() if name != "team-sre"] assert not any([ get_requests_by_group(session, group, status="pending").all() for group in groups.values() ]), "no group should start with pending requests" group_sre.add_member(users["*****@*****.**"], users["*****@*****.**"], reason="for the lulz") session.commit() request_not_sre = [ get_requests_by_group(session, group, status="pending").all() for group in group_not_sre ] assert not any( request_not_sre), "only affected group should show pending requests" request_sre = get_requests_by_group(session, group_sre, status="pending").all() assert len(request_sre) == 1, "affected group should have request" request = session.query(Request).filter_by(id=request_sre[0].id).scalar() request.update_status(users["*****@*****.**"], "actioned", "for being a good person") session.commit() assert not any([ get_requests_by_group(session, group, status="pending").all() for group in groups.values() ]), "no group should have requests after being actioned"
def user_admin_perm_to_auditors(session, groups): """Adds a USER_ADMIN permission to the "auditors" group""" user_admin_perm, is_new = Permission.get_or_create(session, name=USER_ADMIN, description="grouper.admin.users permission") session.commit() grant_permission(groups["auditors"], user_admin_perm)
def test_graph_desc_to_ances(session, graph, users, groups): # noqa """ Test adding members where all descendants already exist.""" setup_desc_to_ances(session, users, groups) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set( ["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
def user_admin_perm_to_auditors(session, groups): """Adds a USER_ADMIN permission to the "auditors" group""" user_admin_perm, is_new = Permission.get_or_create( session, name=USER_ADMIN, description="grouper.admin.users permission") session.commit() grant_permission(groups["auditors"], user_admin_perm)
def test_request_emails_reference(session, groups, permissions, users, base_url, http_client): tech = groups["tech-ops"] tech.canjoin = "canask" tech.add(session) session.commit() # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/groups/{}/join'.format(tech.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name) }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 zay_emails = _get_unsent_and_mark_as_sent_emails_with_username( session, "*****@*****.**") assert any(["References: " in email.body for email in zay_emails])
def test_remove_last_owner_via_audit(async_server, browser, users, groups, session): future = datetime.utcnow() + timedelta(1) add_member(groups["auditors"], users["*****@*****.**"], role="owner") add_member(groups["audited-team"], users["*****@*****.**"], role="owner", expiration=future) session.commit() fe_url = url(async_server, "/audits/create") browser.get(fe_url) page = AuditsCreatePage(browser) page.set_end_date(future.strftime("%m/%d/%Y")) page.submit() fe_url = url(async_server, "/groups/audited-team") browser.get(fe_url) page = GroupViewPage(browser) audit_modal = page.get_audit_modal() audit_modal.find_member_row("*****@*****.**").set_audit_status("remove") audit_modal.confirm() assert page.current_url.endswith("/groups/audited-team") assert page.has_text(group_ownership_policy.EXCEPTION_MESSAGE)
def grantable_permissions(session, standard_graph): perm_grant, _ = Permission.get_or_create(session, name=PERMISSION_GRANT, description="") perm0, _ = Permission.get_or_create(session, name="grantable", description="") perm1, _ = Permission.get_or_create(session, name="grantable.one", description="") perm2, _ = Permission.get_or_create(session, name="grantable.two", description="") session.commit() return perm_grant, perm0, perm1, perm2
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_unsent_and_mark_as_sent_emails(session): """Helper to count unsent emails and then mark them as sent.""" emails = session.query(AsyncNotification).filter(AsyncNotification.sent == False).all() for email in emails: email.sent = True session.commit() return emails
def _get_unsent_and_mark_as_sent_emails_with_username(session, username): """Helper to count unsent emails and then mark them as sent.""" emails = session.query(AsyncNotification).filter_by(sent=False, email=username).all() for email in emails: email.sent = True session.commit() return emails
def _get_unsent_emails_and_send(session): """Helper to count unsent emails and then mark them as sent.""" emails = session.query(AsyncNotification).filter_by(sent=False).all() for email in emails: email.sent = True session.commit() return emails
def test_revoke_permission_from_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") 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 = Permission.get(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_permission_grant_to_owners(session, standard_graph, groups, grantable_permissions): """Test we're getting correct owners according to granted 'grouper.permission.grant' permissions.""" perm_grant, _, perm1, perm2 = grantable_permissions assert not get_owners_by_grantable_permission(session), "nothing to begin with" # grant a grant on a non-existent permission grant_permission(groups["auditors"], perm_grant, argument="notgrantable.one") assert not get_owners_by_grantable_permission(session), "ignore grants for non-existent perms" # grant a wildcard grant -- make sure all permissions are represented and # the grant isn't inherited grant_permission(groups["all-teams"], perm_grant, argument="grantable.*") owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups["all-teams"]] assert owners_by_arg_by_perm[perm1.name]["*"] == expected, "grants are not inherited" assert len(owners_by_arg_by_perm) == 2 assert len(owners_by_arg_by_perm[perm1.name]) == 1 assert len(owners_by_arg_by_perm[perm2.name]) == 1 # grant on argument substring grant_permission(groups["team-sre"], perm_grant, argument="{}/somesubstring*".format(perm1.name)) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups["all-teams"]] assert owners_by_arg_by_perm[perm1.name]["*"] == expected expected = [groups["team-sre"]] assert owners_by_arg_by_perm[perm1.name]["somesubstring*"] == expected # make sure get_owner() respect substrings res = [ o for o, a in get_owner_arg_list(session, perm1, "somesubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert ( sorted(res) == sorted([groups["all-teams"], groups["team-sre"]]), "should include substring wildcard matches", ) res = [ o for o, a in get_owner_arg_list(session, perm1, "othersubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == [groups["all-teams"]], "negative test of substring wildcard matches" # permission admins have all the power perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) all_permissions = Permission.get_all(session) for perm in all_permissions: assert perm.name in owners_by_arg_by_perm, "all permission should be represented" assert ( groups["security-team"] in owners_by_arg_by_perm[perm.name]["*"] ), "permission admin should be wildcard owners"
def test_merge(session, initial_items, items_to_merge): items = { 1: {'id': 1, 'change': '1', 'a': 'a', 'b': 'b'}, } session.execute(insert(Foo, initial_items.values())) session.execute(Merge(Foo, items.values())) session.commit() foos = { foo.id: foo._asdict() for foo in session.query(Foo.id, Foo.change, Foo.a, Foo.b).all() } assert dict(initial_items, **items) == foos
def test_groups_email(groups, session, graph, http_client, base_url): expected_address = "*****@*****.**" sad = groups['sad-team'] sad.email_address = expected_address session.commit() Counter.incr(session, "updates") graph.update_from_db(session) api_url = url(base_url, '/groups/{}'.format(sad.name)) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert body["data"]["group"]["contacts"]["email"] == expected_address
def test_permissions(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() perm = Permission(name="it.literally.does.not.matter", description="Why is this not nullable?") perm.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name="it.literally.does.not.matter").scalar(), "*") 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": "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)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'public_key': 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)) resp = 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_regress_permreq_global_approvers(session, standard_graph, groups, grantable_permissions, http_client, base_url): """Validates that we can render a permission request form where a global approver exists""" perm_grant, _, perm1, _ = grantable_permissions perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) groupname = "sad-team" username = "******" fe_url = url(base_url, "/groups/{}/permission/request".format(groupname)) resp = yield http_client.fetch(fe_url, method="GET", headers={'X-Grouper-User': username}) assert resp.code == 200
def test_aggregate_request(graph, groups, permissions, session, standard_graph, users): gary = users["*****@*****.**"] testuser = users["*****@*****.**"] not_involved = [user for name,user in users.items() if name not in ("*****@*****.**", "*****@*****.**")] assert not any([user_requests_aggregate(session, u).all() for u in users.values()]), \ "should have no pending requests to begin with" # one request to one team groups["team-sre"].add_member(users["*****@*****.**"], users["*****@*****.**"], reason="for the lulz") session.commit() assert len(user_requests_aggregate(session, gary).all()) == 1, "one pending request for owner" assert not any([user_requests_aggregate(session, u).all() for u in not_involved]), \ "no pending requests if you're not the owner" # two request to two teams, same owner groups["team-infra"].add_member(users["*****@*****.**"], users["*****@*****.**"], reason="for the lulz") session.commit() request_gary = user_requests_aggregate(session, gary).all() assert len(request_gary) == 2, "two pending request for owner" assert not any([user_requests_aggregate(session, u).all() for u in not_involved]), \ "no pending requests if you're not the owner" # resolving one request should reflect request = session.query(Request).filter_by(id=request_gary[0].id).scalar() request.update_status(users["*****@*****.**"], "actioned", "for being a good person") session.commit() assert len(user_requests_aggregate(session, gary).all()) == 1, "one pending request for owner" assert not any([user_requests_aggregate(session, u).all() for u in not_involved]), \ "no pending requests if you're not the owner" # requests to dependent teams should reflect apprpriately groups["security-team"].add_member(users["*****@*****.**"], users["*****@*****.**"], reason="for the lulz") session.commit() assert len(user_requests_aggregate(session, gary).all()) == 1, "super owner should not get request" assert len(user_requests_aggregate(session, users["*****@*****.**"]).all()) == 1, "owner should get request" user_not_gary_oliver = [u for n,u in users.items() if n not in ("*****@*****.**","*****@*****.**")] assert not any([user_requests_aggregate(session, u).all() for u in user_not_gary_oliver]) # manager and np-owner should get requests figurehead = users["*****@*****.**"] add_member(groups["audited-team"], figurehead, role="manager") assert len(user_requests_aggregate(session, figurehead).all()) == 0, "no request for np-owner at first" groups["tech-ops"].add_member(users["*****@*****.**"], users["*****@*****.**"], reason="for the lulz") assert len(user_requests_aggregate(session, figurehead).all()) == 1, "request for np-owner" groups["audited-team"].add_member(users["*****@*****.**"], users["*****@*****.**"], reason="for the lulz") assert len(user_requests_aggregate(session, figurehead).all()) == 2, "request for np-owner and manager"
def test_graph_cycle_indirect(session, graph, users, groups): # noqa """ Test adding a member that will create a cycle. gary zay testuser | | | sre <----- tech-ops <----- team-infra <-- | | | | --------> all-teams -------------------- """ add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) add_member(groups["team-infra"], users["*****@*****.**"]) add_member(groups["team-sre"], groups["tech-ops"]) add_member(groups["tech-ops"], groups["team-infra"]) add_member(groups["team-infra"], groups["all-teams"]) add_member(groups["all-teams"], groups["team-sre"]) session.commit() graph.update_from_db(session) all_users = set(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) all_groups = set(["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_users(graph, "team-sre") == all_users assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "tech-ops") == all_users assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "team-infra") == all_users assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == all_users assert get_users(graph, "all-teams", cutoff=1) == set([]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-infra"])
def expired_graph(session, graph, groups, users): now = datetime.utcnow() # expired user membership add_member(groups["team-sre"], users["*****@*****.**"], role="owner") add_member(groups["team-sre"], users["*****@*****.**"], expiration=now) # expired group membership add_member(groups["serving-team"], users["*****@*****.**"], role="owner") add_member(groups["serving-team"], groups["team-sre"], expiration=now) # expired user membership in disabled group add_member(groups["sad-team"], users["*****@*****.**"], expiration=now) groups["sad-team"].disable() session.commit() return graph
def test_merge(session, initial_items, items_to_merge): items = { 1: { 'id': 1, 'change': '1', 'a': 'a', 'b': 'b' }, } session.execute(insert(Foo, initial_items.values())) session.execute(Merge(Foo, items.values())) session.commit() foos = { foo.id: foo._asdict() for foo in session.query(Foo.id, Foo.change, Foo.a, Foo.b).all() } assert dict(initial_items, **items) == foos
def test_cant_revoke_last_npowner(get_plugin_proxy, session, groups, users): get_plugin_proxy.return_value = PluginProxy([GroupOwnershipPolicyPlugin()]) group = groups["team-infra"] first_owner = users["*****@*****.**"] second_owner = users["*****@*****.**"] add_member(group, first_owner, role="np-owner") add_member(group, second_owner, role="np-owner") # Revoking the first owner does not raise an exception revoke_member(group, first_owner) session.commit() with pytest.raises(PluginRejectedGroupMembershipUpdate): revoke_member(group, second_owner)
def test_request_emails_reference(session, groups, permissions, users, base_url, http_client): tech = groups["tech-ops"] tech.canjoin = "canask" tech.add(session) session.commit() # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/groups/{}/join'.format(tech.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({"reason": "Test Request Please Ignore", "member": "User: {}".format(user.name)}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 zay_emails = _get_unsent_and_mark_as_sent_emails_with_username(session, "*****@*****.**") assert any( ["References: " in email.body for email in zay_emails] )
def test_graph_add_member_existing(session, graph, users, groups): # noqa """ Test adding members to an existing relationship.""" add_member(groups["team-sre"], users["*****@*****.**"], role="owner") add_member(groups["tech-ops"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], groups["team-sre"]) add_member(groups["team-infra"], groups["tech-ops"]) add_member(groups["all-teams"], users["*****@*****.**"], role="owner") add_member(groups["all-teams"], groups["team-infra"]) add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set( ["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
def test_basic_request(graph, groups, permissions, session, standard_graph, users): group_sre = groups["team-sre"] group_not_sre = [g for name,g in groups.items() if name != "team-sre"] assert not any([group.my_requests(status="pending").all() for group in groups.values()]), \ "no group should start with pending requests" group_sre.add_member(users["*****@*****.**"], users["*****@*****.**"], reason="for the lulz") session.commit() request_not_sre = [group.my_requests(status="pending").all() for group in group_not_sre] assert not any(request_not_sre), "only affected group should show pending requests" request_sre = group_sre.my_requests(status="pending").all() assert len(request_sre) == 1, "affected group should have request" request = session.query(Request).filter_by(id=request_sre[0].id).scalar() request.update_status(users["*****@*****.**"], "actioned", "for being a good person") session.commit() assert not any([group.my_requests(status="pending").all() for group in groups.values()]), \ "no group should have requests after being actioned"
def test_grant_permission_to_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") 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 = Permission.get(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 test_limited_permissions_global_approvers(session, standard_graph, groups, grantable_permissions, http_client, base_url): """Test that notifications are not sent to global approvers.""" perm_grant, _, perm1, _ = grantable_permissions perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() # one circuit-breaking admin grant, one wildcard grant grant_permission(groups["sad-team"], perm_admin, argument="") grant_permission(groups["security-team"], perm_grant, argument="grantable.*") security_team_members = { name for (t, name) in groups['security-team'].my_members().keys() if t == 'User' } # SPECIFIC REQUEST: 'grantable.one', 'specific_arg' for 'sad-team' groupname = "sad-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": perm1.name, "argument": "specific_arg", "reason": "blah blah black sheep", "argument_type": "text" }), headers={'X-Grouper-User': username}) assert resp.code == 200 emails = _get_unsent_and_mark_as_sent_emails(session) assert len(emails) == 2, "email only sent to security-team" assert not security_team_members.difference(e.email for e in emails), \ "only security-team members get notification"
def test_edit_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") 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 test_revoke_permission_from_tag(users, http_client, base_url, session): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") 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 = Permission.get(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_graph_cycle_direct(session, graph, users, groups): # noqa """ Test adding members where all descendants already exist.""" add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) add_member(groups["team-sre"], groups["tech-ops"]) add_member(groups["tech-ops"], groups["team-sre"]) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"])
def test_cant_revoke_last_owner(get_plugins, session, groups, users): get_plugins.return_value = [GroupOwnershipPolicyPlugin()] group = groups["team-infra"] first_owner = users["*****@*****.**"] second_owner = users["*****@*****.**"] add_member(group, first_owner, role="owner") add_member(group, second_owner, role="owner") assert len(group.my_owners()) == 2 # Revoking the first owner does not raise an exception revoke_member(group, first_owner) session.commit() assert len(group.my_owners()) == 1 with pytest.raises(PluginRejectedGroupMembershipUpdate): revoke_member(group, second_owner) assert len(group.my_owners()) == 1
def test_basic_permission(standard_graph, session, users, groups, permissions): # noqa """ Test adding some permissions to various groups and ensuring that the permissions are all implemented as expected. This also tests permissions inheritance in the graph. """ graph = standard_graph # noqa grant_permission(groups["team-sre"], permissions["ssh"], argument="*") grant_permission(groups["tech-ops"], permissions["ssh"], argument="shell") grant_permission(groups["team-infra"], permissions["sudo"], argument="shell") session.commit() graph.update_from_db(session) assert sorted(get_group_permissions(graph, "team-sre")) == ["ssh:*", "sudo:shell"] assert sorted(get_group_permissions(graph, "tech-ops")) == ["ssh:shell", "sudo:shell"] assert sorted(get_group_permissions(graph, "team-infra")) == ["sudo:shell"] assert sorted(get_group_permissions(graph, "all-teams")) == [] assert sorted(get_user_permissions(graph, "gary")) == ["ssh:*", "ssh:shell", "sudo:shell"] assert sorted(get_user_permissions(graph, "zay")) == ["ssh:*", "ssh:shell", "sudo:shell"] assert sorted(get_user_permissions(graph, "zorkian")) == ["ssh:*", "sudo:shell"] assert sorted(get_user_permissions(graph, "testuser")) == []
def test_graph_add_member_existing(session, graph, users, groups): # noqa """ Test adding members to an existing relationship.""" add_member(groups["team-sre"], users["*****@*****.**"], role="owner") add_member(groups["tech-ops"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], groups["team-sre"]) add_member(groups["team-infra"], groups["tech-ops"]) add_member(groups["all-teams"], users["*****@*****.**"], role="owner") add_member(groups["all-teams"], groups["team-infra"]) add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
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()
def test_aggregate_request(graph, groups, permissions, session, standard_graph, users): gary = users["gary"] testuser = users["testuser"] not_involved = [user for name,user in users.items() if name not in ("gary","testuser")] print "users! {}".format(users.values()) assert not any([u.my_requests_aggregate().all() for u in users.values()]), \ "should have no pending requests to begin with" # one request to one team groups["team-sre"].add_member(users["testuser"], users["testuser"], reason="for the lulz") session.commit() assert len(gary.my_requests_aggregate().all()) == 1, "one pending request for owner" assert not any([u.my_requests_aggregate().all() for u in not_involved]), \ "no pending requests if you're not the owner" # two request to two teams, same owner groups["team-infra"].add_member(users["testuser"], users["testuser"], reason="for the lulz") session.commit() request_gary = gary.my_requests_aggregate().all() assert len(request_gary) == 2, "two pending request for owner" assert not any([u.my_requests_aggregate().all() for u in not_involved]), \ "no pending requests if you're not the owner" # resolving one request should reflect request = session.query(Request).filter_by(id=request_gary[0].id).scalar() request.update_status(users["gary"], "actioned", "for being a good person") session.commit() assert len(gary.my_requests_aggregate().all()) == 1, "one pending request for owner" assert not any([u.my_requests_aggregate().all() for u in not_involved]), \ "no pending requests if you're not the owner" # requests to dependent teams should reflect apprpriately groups["security-team"].add_member(users["testuser"], users["testuser"], reason="for the lulz") session.commit() assert len(gary.my_requests_aggregate().all()) == 1, "super owner should not get request" assert len(users["oliver"].my_requests_aggregate().all()) == 1, "owner should get request" user_not_gary_oliver = [u for n,u in users.items() if n not in ("gary","oliver")] assert not any([u.my_requests_aggregate().all() for u in user_not_gary_oliver])
def test_tags(session, users, http_client, base_url, graph): user = session.query(User).filter_by(username="******").scalar() perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() perm2 = Permission(name="it.literally.does.not.matter", description="Why is this not nullable?") perm2.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name="it.literally.does.not.matter").scalar(), "*") tag = PublicKeyTag(name="tyler_was_here") tag.add(session) session.commit() tag = PublicKeyTag.get(session, name="tyler_was_here") user = session.query(User).filter_by(username="******").scalar() grant_permission_to_tag(session, tag.id, perm.id, "prod") user = session.query(User).filter_by(username="******").scalar() add_public_key(session, user, key1) key = session.query(PublicKey).filter_by(user_id=user.id).scalar() user = session.query(User).filter_by(username="******").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['tags'][0] == 'tyler_was_here', "The public key should have the tag we gave it"
def test_tags(session, http_client, base_url, graph): perm = Permission(name=TAG_EDIT, description="Why is this not nullable?") perm.add(session) session.commit() perm2 = Permission(name="it.literally.does.not.matter", description="Why is this not nullable?") perm2.add(session) session.commit() grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name=TAG_EDIT).scalar(), "*") grant_permission(session.query(Group).filter_by(groupname="all-teams").scalar(), session.query(Permission).filter_by(name="it.literally.does.not.matter").scalar(), "*") 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 test_permission_grant_to_owners(session, standard_graph, groups, grantable_permissions): """Test we're getting correct owners according to granted 'grouper.permission.grant' permissions.""" perm_grant, _, perm1, perm2 = grantable_permissions assert not get_owners_by_grantable_permission( session), 'nothing to begin with' # grant a grant on a non-existent permission grant_permission(groups["auditors"], perm_grant, argument="notgrantable.one") assert not get_owners_by_grantable_permission( session), 'ignore grants for non-existent perms' # grant a wildcard grant -- make sure all permissions are represented and # the grant isn't inherited grant_permission(groups["all-teams"], perm_grant, argument="grantable.*") owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups['all-teams']] assert owners_by_arg_by_perm[ perm1.name]['*'] == expected, 'grants are not inherited' assert len(owners_by_arg_by_perm) == 2 assert len(owners_by_arg_by_perm[perm1.name]) == 1 assert len(owners_by_arg_by_perm[perm2.name]) == 1 # grant on argument substring grant_permission(groups["team-sre"], perm_grant, argument="{}/somesubstring*".format(perm1.name)) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) expected = [groups['all-teams']] assert owners_by_arg_by_perm[perm1.name]['*'] == expected expected = [groups["team-sre"]] assert owners_by_arg_by_perm[perm1.name]['somesubstring*'] == expected # make sure get_owner() respect substrings res = [ o for o, a in get_owner_arg_list( session, perm1, "somesubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == sorted([groups["all-teams"], groups["team-sre"]]), \ "should include substring wildcard matches" res = [ o for o, a in get_owner_arg_list( session, perm1, "othersubstring", owners_by_arg_by_perm=owners_by_arg_by_perm) ] assert sorted(res) == [groups["all-teams"] ], "negative test of substring wildcard matches" # permission admins have all the power perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="") session.commit() grant_permission(groups["security-team"], perm_admin) owners_by_arg_by_perm = get_owners_by_grantable_permission(session) all_permissions = Permission.get_all(session) for perm in all_permissions: assert perm.name in owners_by_arg_by_perm, 'all permission should be represented' assert groups["security-team"] in owners_by_arg_by_perm[perm.name]["*"], \ 'permission admin should be wildcard owners'
def test_request_autoexpiration(graph, groups, permissions, session, standard_graph, users, base_url, http_client): tech = groups["tech-ops"] sre = groups["team-sre"] security = groups["security-team"] sad = groups["sad-team"] infra = groups["team-infra"] tech.canjoin = "canask" tech.auto_expire = timedelta(days=5) tech.add(session) session.commit() sre.canjoin = "canjoin" sre.auto_expire = timedelta(days=500) sre.add(session) session.commit() security.canjoin = "canjoin" security.add(session) session.commit() sad.canjoin = "canjoin" sad.add(session) session.commit() infra.canjoin = "canjoin" infra.add(session) session.commit() # REQUEST 1 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() tech = session.query(Group).filter_by(groupname="tech-ops").scalar() fe_url = url(base_url, '/groups/{}/join'.format(tech.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name) }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() request = session.query(Request).filter_by(requesting_id=tech.id, requester_id=user.id).scalar() assert datetime.strptime(request.changes['expiration'], "%m/%d/%Y").date( ) == ( datetime.utcnow().date() + tech.auto_expire ), "Request expirations should be the current date + group.auto_expire for canask groups" # REQUEST 2 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() sre = session.query(Group).filter_by(groupname="team-sre").scalar() fe_url = url(base_url, '/groups/{}/join'.format(sre.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name) }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() request = session.query(Request).filter_by(requesting_id=sre.id, requester_id=user.id).scalar() assert datetime.strptime(request.changes['expiration'], "%m/%d/%Y").date( ) == ( datetime.utcnow().date() + sre.auto_expire ), "Request expirations should be the current date + group.auto_expire for canjoin groups" # REQUEST 3 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() security = session.query(Group).filter_by( groupname="security-team").scalar() fe_url = url(base_url, '/groups/{}/join'.format(security.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name) }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() request = session.query(Request).filter_by(requesting_id=security.id, requester_id=user.id).scalar() assert "expiration" not in request.changes, "The request should not have an expiration if none is provided and there is no auto_expiration" # REQUEST 4 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() sad = session.query(Group).filter_by(groupname="sad-team").scalar() fe_url = url(base_url, '/groups/{}/join'.format(sad.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name), "expiration": "01/19/2038" }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() request = session.query(Request).filter_by(requesting_id=sad.id, requester_id=user.id).scalar() assert datetime.strptime(request.changes['expiration'], "%m/%d/%Y").date( ) == date( year=2038, month=1, day=19 ), "User provided expiration times should not be overwritten by the auto_expiration" # REQUEST 5 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() infra = session.query(Group).filter_by(groupname="team-infra").scalar() fe_url = url(base_url, '/groups/{}/add'.format(infra.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name) }), headers={'X-Grouper-User': "******"}) assert resp.code == 200 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() request = session.query(Request).filter_by(requesting_id=infra.id, requester_id=user.id).scalar() assert "expiration" not in request.changes, "The request should not have an expiration if none is provided and the request was created by adding a member" # REQUEST 6 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() sre = session.query(Group).filter_by(groupname="team-sre").scalar() fe_url = url(base_url, '/groups/{}/edit/user/{}'.format(sre.groupname, user.name)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name), "role": "member", "expiration": "" }), headers={'X-Grouper-User': "******"}) assert resp.code == 200 # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() group_edge = session.query(GroupEdge).filter_by( group_id=sre.id, member_pk=user.id).scalar() assert group_edge.expiration is None, "The request should not have an expiration if none is provided and the user was edited by an approver"
def test_request_emails(graph, groups, permissions, session, standard_graph, users, base_url, http_client): tech = groups["tech-ops"] tech.canjoin = "canask" tech.add(session) session.commit() # REQUEST 1 before_reqs = session.query(Request).count() # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/groups/{}/join'.format(tech.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore", "member": "User: {}".format(user.name) }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 zaya_emails = len( _get_unsent_and_mark_as_sent_emails_with_username(session, "*****@*****.**")) fh_emails = len( _get_unsent_and_mark_as_sent_emails_with_username( session, "*****@*****.**")) assert zaya_emails + fh_emails == 2, "Only approvers for the requested group should receive an email" assert zaya_emails == 1, "Owners should receive exactly one email per canask request" assert fh_emails == 1, "NP-Owners should receive exactly one email per canask request" assert session.query(Request).count( ) == before_reqs + 1, "There should only be one added request" # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() request_id = session.query(Request).filter_by( requesting_id=tech.id, requester_id=user.id).scalar().id fe_url = url(base_url, '/groups/{}/requests/{}'.format(tech.groupname, request_id)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Response Please Ignore", "status": "actioned" }), headers={'X-Grouper-User': "******"}) assert resp.code == 200 fh_emails = len( _get_unsent_and_mark_as_sent_emails_with_username( session, "*****@*****.**")) testuser_emails = len( _get_unsent_and_mark_as_sent_emails_with_username( session, "*****@*****.**")) assert fh_emails + testuser_emails == 2, "Only the approver that didn't action the request and the reqester should get an email" assert fh_emails == 1, "NP-owners that did not action the request should receive an email" assert testuser_emails == 1, "The requester should receive an email when the request is handled" # REQUEST 2 before_reqs = session.query(Request).count() # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() fe_url = url(base_url, '/groups/{}/join'.format(tech.groupname)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({ "reason": "Test Request Please Ignore 2", "member": "User: {}".format(user.name) }), headers={'X-Grouper-User': user.username}) assert resp.code == 200 zaya_emails = len( _get_unsent_and_mark_as_sent_emails_with_username(session, "*****@*****.**")) fh_emails = len( _get_unsent_and_mark_as_sent_emails_with_username( session, "*****@*****.**")) assert zaya_emails + fh_emails == 2, "Only approvers for the requested group should receive an email" assert zaya_emails == 1, "Owners should receive exactly one email per canask request" assert fh_emails == 1, "NP-Owners should receive exactly one email per canask request" assert session.query(Request).count( ) == before_reqs + 1, "There should only be one added request" # Explicitly requery because pulling from the users dict causes DetachedSessionErrors user = session.query(User).filter_by(username="******").scalar() request_id = session.query(Request).filter_by( requesting_id=tech.id, requester_id=user.id).scalar().id fe_url = url(base_url, '/groups/{}/requests/{}'.format(tech.groupname, request_id)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "reason": "Test Response Please Ignore 2", "status": "actioned" }), headers={'X-Grouper-User': "******"}) assert resp.code == 200 zaya_emails = len( _get_unsent_and_mark_as_sent_emails_with_username(session, "*****@*****.**")) oliver_emails = len( _get_unsent_and_mark_as_sent_emails_with_username( session, "*****@*****.**")) assert zaya_emails + oliver_emails == 2, "Only the approver that didn't action the request and the reqester should get an email" assert zaya_emails == 1, "NP-owners that did not action the request should receive an email" assert oliver_emails == 1, "The requester should receive an email when the request is handled"
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 test_usertokens(users, session, http_client, base_url): user = users["*****@*****.**"] tok, secret = add_new_user_token(session, UserToken(user=user, name="Foo")) session.commit() api_url = url(base_url, '/token/validate') # Completely bogus input resp = yield http_client.fetch(api_url, method="POST", body=urlencode({'token': 'invalid'})) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "error" assert len(body["errors"]) == 1 assert body["errors"][0]["code"] == 1 valid_token = str(tok) + ":" + secret # Valid token resp = yield http_client.fetch(api_url, method="POST", body=urlencode({'token': valid_token})) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "ok" assert body["data"]["identity"] == str(tok) assert body["data"]["owner"] == user.username assert body["data"]["act_as_owner"] assert body["data"]["valid"] # Token with the last character changed to something invalid bad_char = "1" if secret[-1].isalpha() else "a" token_with_bad_secret = str(tok) + ":" + secret[:-1] + bad_char resp = yield http_client.fetch(api_url, method="POST", body=urlencode({'token': token_with_bad_secret})) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "error" assert len(body["errors"]) == 1 assert body["errors"][0]["code"] == 4 # Token with the token name frobbed to be something invalid token_with_bad_name = str(tok) + "z:" + secret resp = yield http_client.fetch(api_url, method="POST", body=urlencode({'token': token_with_bad_name})) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "error" assert len(body["errors"]) == 1 assert body["errors"][0]["code"] == 2 # Token with the user frobbed to be something invalid token_with_bad_user = "******" + str(tok) + ":" + secret resp = yield http_client.fetch(api_url, method="POST", body=urlencode({'token': token_with_bad_user})) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "error" assert len(body["errors"]) == 1 assert body["errors"][0]["code"] == 2 # Token with the user changed to another valid, but wrong user token_with_wrong_user = "******" + tok.name + ":" + secret resp = yield http_client.fetch(api_url, method="POST", body=urlencode({'token': token_with_wrong_user})) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "error" assert len(body["errors"]) == 1 assert body["errors"][0]["code"] == 2 # Disabled, but otherwise valid token disable_user_token(session, tok) session.commit() resp = yield http_client.fetch(api_url, method="POST", body=urlencode({'token': valid_token})) body = json.loads(resp.body) assert resp.code == 200 assert body["status"] == "error" assert len(body["errors"]) == 1 assert body["errors"][0]["code"] == 3
def test_expire_nonauditors(standard_graph, users, groups, session, permissions): """ Test expiration auditing and notification. """ graph = standard_graph # noqa # Test audit autoexpiration for all approvers approver_roles = ["owner", "np-owner", "manager"] for role in approver_roles: # Add non-auditor as an owner to an audited group add_member(groups["audited-team"], users["*****@*****.**"], role=role) session.commit() graph.update_from_db(session) group_md = graph.get_group_details("audited-team") assert group_md.get('audited', False) # Expire the edges. background = BackgroundThread(settings, None) background.expire_nonauditors(session) # Check that the edges are now marked as inactive. edge = session.query(GroupEdge).filter_by(group_id=groups["audited-team"].id, member_pk=users["*****@*****.**"].id).scalar() assert edge.expiration is not None assert edge.expiration < datetime.utcnow() + timedelta(days=settings.nonauditor_expiration_days) assert edge.expiration > datetime.utcnow() + timedelta(days=settings.nonauditor_expiration_days - 1) assert any(["Subject: Membership in audited-team set to expire" in email.body and "To: [email protected]" in email.body for email in _get_unsent_emails_and_send(session)]) audits = AuditLog.get_entries(session, action="nonauditor_flagged") assert len(audits) == 3 + 1 * (approver_roles.index(role) + 1) revoke_member(groups["audited-team"], users["*****@*****.**"]) # Ensure nonauditor, nonapprovers in audited groups do not get set to expired member_roles = ["member"] for role in member_roles: # Add non-auditor as an owner to an audited group add_member(groups["audited-team"], users["*****@*****.**"], role=role) session.commit() graph.update_from_db(session) group_md = graph.get_group_details("audited-team") assert group_md.get('audited', False) # Expire the edges. background = BackgroundThread(settings, None) background.expire_nonauditors(session) # Check that the edges are now marked as inactive. edge = session.query(GroupEdge).filter_by(group_id=groups["audited-team"].id, member_pk=users["*****@*****.**"].id).scalar() assert edge.expiration is None assert not any(["Subject: Membership in audited-team set to expire" in email.body and "To: [email protected]" in email.body for email in _get_unsent_emails_and_send(session)]) audits = AuditLog.get_entries(session, action="nonauditor_flagged") assert len(audits) == 3 + 1 * len(approver_roles) revoke_member(groups["audited-team"], users["*****@*****.**"])