Normalise grants and roles tables to improve grants query performance (#5846)
* Create function to define valid set of scopes for each resource (#5558) * Create function to define valid set of scopes for each resource * chore(iam): Update validScopeTypes() to use scope.AllowedIn() * chore(scope): Initialize with iota * feat(scope): Return an error instead scope.Unknown * feat(iam): Replace interface method `validScopeTypes` with `getResourceType` This allows us to call scope.AllowedIn() in one place vs in each implementation of `validScopeTypes` * chore(resource): Refactor other package functions into methods on resource.Type * fix(scope): Add defensive checks around invalid resource types * docs(resource): Add AllowedIn() to the areas to update when adding a new resource type * docs(resource): Improve error message when an invalid type is provided * test(grants): GrantsForUsers tests for Group resource (#5443) * test(grants): WIP: First stab at group associations * test(grants): Add GrantsForUser test for groups * chore(grants): Consolidate repetitive setup logic into functions * test(grants): Add GrantsForUser test for managed groups * test(grants): Add another user with different grants Ensure that non-applicable grants should not be returned because they are not applicable to the user * chore(grants): cleanup * chore(grants): Move common setup steps into a helper function * feat: Define new grants tables (#5486) Create new tables for grants: 1. `iam_role_global`: Roles that are placed in the global scope will be persisted in the `iam_role_global` table. A global role has a `grant_scope` which must be one of: * descendants * children * individual This enforces that a global role's grants either apply to: * All orgs and projects. * All orgs. * An individual set of orgs and/or projects. When the `grant_scope` is set to `individual`, entries for the specific set of orgs and/or projects can be added to the `iam_role_global_individual_grant_scope` table. Separately, a global role can be set to also apply its grant to the global scope by setting `grant_this_role_scope` to true. 2. `iam_role_org`: Roles that are placed in an org scope will be persisted in the `iam_role_org` table. An org role has a `grant_scope` which must be one of: * children * individual This enforces that an org role's grants either apply to: * All projects in the org. * An individual set of projects in the org. When the `grant_scope` is set to `individual`, entries for the specific set of projects can be added to the `iam_role_org_individual_grant_scope` table. **NOTE**: The projects must belong to the org's scope Separately, an org role can be set to also apply its grant to the org by setting `grant_this_role_scope` to true. 3. `resource_enm`: Contains all boundary resources. This is used by `iam_grant` to set the resource from a canonical_grant. 4. `iam_grant` Stores the canonical grant string and the resource for filtering on specific grants. Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> Co-authored-by: David Kanney <david.kanney@hashicorp.com> * add name scope id unique constraints to all iam_role tables (#5623) * Bosorawis domain iam role subtypes (#5626) * add subtype storage definitions * make gen * add all subtype definitions * add const for grant scope individual * remove unnecessary baseRole subtype * add new proto files to make target protobuild * make gen to get protoc-go-inject-tag * add clone, setTableName, and GetScope tests * add ResourceType and Actions test * add create and delete tests for globalROle * finish create and delete tests * add trigger for deleting base role * add trigger to sync update_time back to base iam_role table * add update tests * fix missing err checks * fix iam_role delete subtype trigger function name and use new.update_time instead of now() * add struct documentation to role subtypes * add version update check * add todo comment: * bosorawis sql split global grants scope table (#5638) * split iam_role_global_individual_grant_scope to have separate tables for org and project * small comment change * small comment change * WIP: add tests * remove grant_scope as immutable column * add trigger to delete individual grant scope when grant_scope changes * add a test that covers changing grant_scope * rename function and trigger in iam_role_global * improve assertion in sqltest for iam_role_global * update iam_role_org to delete redundant grants scope * minor comment fix * no longer handle individual grant scope deletion with triggers and rename some functions * add trigger test for grant_scope * rename delete_base_iam_role to delete_iam_role_subtype * SQL formatting use now() instead of interval * bosorawis domain iam implement getRoleScopeType (#5629) * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * rename test * change error code to RecordNotFound * Update internal/iam/repository_role.go Co-authored-by: David Kanney <david.kanney@hashicorp.com> * switch to slice instead of counter --------- Co-authored-by: David Kanney <david.kanney@hashicorp.com> * domain: iam: upgrade repository code to use new tables (#5643) * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * split iam_role_global_individual_grant_scope to have separate tables for org and project * small comment change * small comment change * WIP: add tests * remove grant_scope as immutable column * add trigger to delete individual grant scope when grant_scope changes * add a test that covers changing grant_scope * rename function and trigger in iam_role_global * improve assertion in sqltest for iam_role_global * update iam_role_org to delete redundant grants scope * minor comment fix * no longer handle individual grant scope deletion with triggers and rename some functions * rename test * add all subtype definitions * remove unnecessary baseRole subtype * add clone, setTableName, and GetScope tests * add ResourceType and Actions test * add create and delete tests for globalROle * finish create and delete tests * add trigger for deleting base role * add trigger to sync update_time back to base iam_role table * add update tests * fix missing err checks * fix iam_role delete subtype trigger function name and use new.update_time instead of now() * add struct documentation to role subtypes * add version update check * implement getRoleScopeId * implement getRoleScopeId * save * remove struct embedding from iam.Role * fix tests to use new iam.Role definition * repository_role_test.go move to new iam.Role model * repository_principal_role_test.go use new iam.Role model * repository_role_grant_test.go use new iam.Role model in test * add oplog info to sql schema * internal/iam/testing.go use new role schema in TestRole * add toRole helper function to all role subtype * remove tests that are no longer relevant * internal/iam/repository_scope.go use new iam model * internal/iam/repository_role_grant.go use new iam model * internal/iam/repository_principal_role.go use new iam model * internal/iam/repository_role_test.go add test case for global scoped role * internal/iam/repository_grant_scope.go use new iam model * fix query * make create and lookup role work and add tests * add role id to getRoleScopeId error message * make DeleteRole work with new model and add tests * fix update * ensure oplog.ReplayableMessage is implemented on all role subtypes * internal/iam/repository_role_grant.go fix slugging version properly * internal/iam/repository_role.go minor correction to error message saying org instead of scope * internal/iam/repository_role_test.go add more update tests * add immutable_fields tests * fix rebase * change error code to RecordNotFound * refactor to use getScopeType * fix delete test * add getRoleScope utility function * repository_principal_role.go: refactor to remove multiple switch statements * repository_role_grant.go: refactor to reduce LOC * repository_role.go small refactor to use alloc func * repository_grant_scope.go refactor * review comments * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * rename test * change error code to RecordNotFound * Update internal/iam/repository_role.go Co-authored-by: David Kanney <david.kanney@hashicorp.com> * switch to slice instead of counter * fix merge mistakes * handling special scopes in test function * fix TestRoleWithGrants * fix minor typo * make gen * fix comment typos * Bosorawis domain iam role use new model list role (#5676) * add and use new list roles query * run make gen * tweaked returned error * replace tabs with spaces in query string * missed one tab * remove leading spaces --------- Co-authored-by: David Kanney <david.kanney@hashicorp.com> * Domain: iam: Repository: update list-grant-scope and test setup to use new model (#5679) * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * split iam_role_global_individual_grant_scope to have separate tables for org and project * small comment change * small comment change * WIP: add tests * remove grant_scope as immutable column * add trigger to delete individual grant scope when grant_scope changes * add a test that covers changing grant_scope * rename function and trigger in iam_role_global * improve assertion in sqltest for iam_role_global * update iam_role_org to delete redundant grants scope * minor comment fix * no longer handle individual grant scope deletion with triggers and rename some functions * rename test * add all subtype definitions * remove unnecessary baseRole subtype * add clone, setTableName, and GetScope tests * add ResourceType and Actions test * add create and delete tests for globalROle * finish create and delete tests * add trigger for deleting base role * add trigger to sync update_time back to base iam_role table * add update tests * fix missing err checks * fix iam_role delete subtype trigger function name and use new.update_time instead of now() * add struct documentation to role subtypes * add version update check * implement getRoleScopeId * implement getRoleScopeId * save * remove struct embedding from iam.Role * fix tests to use new iam.Role definition * repository_role_test.go move to new iam.Role model * repository_principal_role_test.go use new iam.Role model * repository_role_grant_test.go use new iam.Role model in test * add oplog info to sql schema * internal/iam/testing.go use new role schema in TestRole * add toRole helper function to all role subtype * remove tests that are no longer relevant * internal/iam/repository_scope.go use new iam model * internal/iam/repository_role_grant.go use new iam model * internal/iam/repository_principal_role.go use new iam model * internal/iam/repository_role_test.go add test case for global scoped role * internal/iam/repository_grant_scope.go use new iam model * fix query * make create and lookup role work and add tests * add role id to getRoleScopeId error message * make DeleteRole work with new model and add tests * fix update * ensure oplog.ReplayableMessage is implemented on all role subtypes * internal/iam/repository_role_grant.go fix slugging version properly * internal/iam/repository_role.go minor correction to error message saying org instead of scope * internal/iam/repository_role_test.go add more update tests * add immutable_fields tests * fix rebase * change error code to RecordNotFound * refactor to use getScopeType * fix delete test * add getRoleScope utility function * repository_principal_role.go: refactor to remove multiple switch statements * repository_role_grant.go: refactor to reduce LOC * repository_role.go small refactor to use alloc func * repository_grant_scope.go refactor * review comments * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * rename test * change error code to RecordNotFound * Update internal/iam/repository_role.go Co-authored-by: David Kanney <david.kanney@hashicorp.com> * switch to slice instead of counter * fix merge mistakes * handling special scopes in test function * fix TestRoleWithGrants * fix minor typo * make gen * fix comment typos * Bosorawis domain iam role use new model list role (#5676) * add and use new list roles query * run make gen * tweaked returned error * replace tabs with spaces in query string * missed one tab * remove leading spaces * move ListRoleGrantScopes to repository_grant_scope.go * rename repository_grant_scope to repository_role_grant_scope * add proto definition for global role individual grant scope tables * fix test from removing embeded struct from RoleGrantScope * add grant_scope to proto definition * implement GlobalRoleIndividualOrgGrantScope and GlobalRoleIndividualProjectGrantScope * update comment * run make gen to update comment * implement OrgRoleIndividualGrantScope and add tests * implement part of ListRoleGrantScopes * Add more test * add more test cases and remove add-grants test * unexport listRoleGrantScopes * use reader from function parameter instead of struct method * rename test to match actual function * run make gen * unexport individual grants structs * unexport individual grants structs - missed one file * change TestRole and TestRoleGrantScope function to support new model * add validation for special scopes * add role_org_individual_grant_scope.pb.go to protobuild make target * remove dead code from listRoleGrantScopes * fix testRoleGrantScopeSpecial not handling org role special scope properly * add back query removed by rebase --------- Co-authored-by: David Kanney <david.kanney@hashicorp.com> * feat: grantsForUser for Global Resources (#5612) * feat: grantsForUser for Global Resources add query to fetch grants for a user for resources that are only globally scoped * Update query based on change to bifurcate individual table * Create subtests for different resources * Return grant.grant_scope instead of the request scope * Remove 'individual' subquery & unused reqScope parameter * Use sql.Named for better readability * Fix op function name * Remove individual grant scope logic from global resource repo function No need to handle individual grant scopes since global resources can only be queried via 'this' grant scope at the global scope. * Fix row scan order * Remove data gen function * Adjust query formatting Remove canonical_grant filter from query. `iam_grant.canonical_grant` is a primary key, so it can't be null anyway -- no need to filter out null canonical grants * Use the consts for u_auth and u_anon * Specify "empty" instead of "NULL" in struct field comment * Build query args with `pq.Array` instead of `fmt.Sprintf` * Fix TestGrantsForUserGlobalResources No longer using a hard-coded value for roleVersion * Refactor grantsForUserGlobalResources tests into testcases * go mod tidy * Update query comment for correctness --------- Co-authored-by: dkanney <dkanney@terpmail.umd.edu> Co-authored-by: dkanney <david.kanney@hashicorp.com> * Bosorawis domain iam implement role grant scopes all (#5701) * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * split iam_role_global_individual_grant_scope to have separate tables for org and project * small comment change * small comment change * WIP: add tests * remove grant_scope as immutable column * add trigger to delete individual grant scope when grant_scope changes * add a test that covers changing grant_scope * rename function and trigger in iam_role_global * improve assertion in sqltest for iam_role_global * update iam_role_org to delete redundant grants scope * minor comment fix * no longer handle individual grant scope deletion with triggers and rename some functions * rename test * add all subtype definitions * remove unnecessary baseRole subtype * add clone, setTableName, and GetScope tests * add ResourceType and Actions test * add create and delete tests for globalROle * finish create and delete tests * add trigger for deleting base role * add trigger to sync update_time back to base iam_role table * add update tests * fix missing err checks * fix iam_role delete subtype trigger function name and use new.update_time instead of now() * add struct documentation to role subtypes * add version update check * implement getRoleScopeId * implement getRoleScopeId * save * remove struct embedding from iam.Role * fix tests to use new iam.Role definition * repository_role_test.go move to new iam.Role model * repository_principal_role_test.go use new iam.Role model * repository_role_grant_test.go use new iam.Role model in test * add oplog info to sql schema * internal/iam/testing.go use new role schema in TestRole * add toRole helper function to all role subtype * remove tests that are no longer relevant * internal/iam/repository_scope.go use new iam model * internal/iam/repository_role_grant.go use new iam model * internal/iam/repository_principal_role.go use new iam model * internal/iam/repository_role_test.go add test case for global scoped role * internal/iam/repository_grant_scope.go use new iam model * fix query * make create and lookup role work and add tests * add role id to getRoleScopeId error message * make DeleteRole work with new model and add tests * fix update * ensure oplog.ReplayableMessage is implemented on all role subtypes * internal/iam/repository_role_grant.go fix slugging version properly * internal/iam/repository_role.go minor correction to error message saying org instead of scope * internal/iam/repository_role_test.go add more update tests * add immutable_fields tests * fix rebase * change error code to RecordNotFound * refactor to use getScopeType * fix delete test * add getRoleScope utility function * repository_principal_role.go: refactor to remove multiple switch statements * repository_role_grant.go: refactor to reduce LOC * repository_role.go small refactor to use alloc func * repository_grant_scope.go refactor * review comments * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * rename test * change error code to RecordNotFound * Update internal/iam/repository_role.go Co-authored-by: David Kanney <david.kanney@hashicorp.com> * switch to slice instead of counter * fix merge mistakes * handling special scopes in test function * fix TestRoleWithGrants * fix minor typo * make gen * fix comment typos * Bosorawis domain iam role use new model list role (#5676) * add and use new list roles query * run make gen * tweaked returned error * move ListRoleGrantScopes to repository_grant_scope.go * rename repository_grant_scope to repository_role_grant_scope * add proto definition for global role individual grant scope tables * fix test from removing embeded struct from RoleGrantScope * add grant_scope to proto definition * implement GlobalRoleIndividualOrgGrantScope and GlobalRoleIndividualProjectGrantScope * update comment * run make gen to update comment * implement OrgRoleIndividualGrantScope and add tests * implement part of ListRoleGrantScopes * Add more test * add more test cases and remove add-grants test * unexport listRoleGrantScopes * use reader from function parameter instead of struct method * rename test to match actual function * run make gen * unexport individual grants structs * unexport individual grants structs - missed one file * change TestRole and TestRoleGrantScope function to support new model * add validation for special scopes * add role_org_individual_grant_scope.pb.go to protobuild make target * remove dead code from listRoleGrantScopes * fix testRoleGrantScopeSpecial not handling org role special scope properly * change proto grant_this default to true * make TestRole readback the role to get updated version * implement toRoleGrantScope function on the subtypes * implement conversion function * add tests and AddRoleGrantScope before refactor * working delete grant scope before refactor * remove unused functions * refactor repository_role_grant_scope.go * add tests for SetRoleGrantScopes * all tests passing * refactor repository_role_grant_scope.go again * run make gen * no longer embed Resource in roleScopeGranter interface and make interface all internal functions * add additional test case * fix minor typo * add a constraint check for iam_role_org.grant_scope * refactor and comment repository_role_grant_scope.go and add test cases * remove unused code * rename roleScopeGranter to roleGrantScopeUpdater * interface and function rename * address PR comments * remove redundant constraint on iam_role_org * address pr comments * grant this scope to role by default when creating a role * tweak comments and variable names --------- Co-authored-by: David Kanney <david.kanney@hashicorp.com> * feat: add grant_this_role_scope column to iam_role_project (#5738) * add grant_this_role_scope and sqltes for iam_project_role * update role_project.proto to add grant_this_role_scope field * fix missing fields in role_project.proto * support GrantThisRoleScope field in iam/testing.go * update repository_scope to set GrantThisRoleScope to true * add GrantThisRoleScope to projec role tests to role_test.go * add grant_this_role_scope check to query.go * update roleGrantScopeUpdater to split setGrantScope to setHierarchicalGrantScope and removeHierarchicalGrantScope * update create_role to set default vaule for iam_role_project.grant_this_role_scope * update set, add, delete, list role grant scopes to support grant_this_role_scope in project_role * rename methods * run make gen * make TestRole not bump version when creating roles with default 'this' grants * address pr comments * fix variable names * feat: grantsForUser for Org resources (#5663) * Create grantsForUser query for Org resources * Finish & format query * Change name of field in query From 'role_type' to 'role_parent_scope_id' * Finish grantsForUserOrgResources test - Refactor to match the usual testcase pattern - Use test functions to create test resources * Remove unnecessary check against GrantScopeThis "this" now has its own field and no longer lives alongside the other grant scopes, so we should not check for it against grantScope * Ignore children & descendant grant scopes when querying org resources The only grant_scope that mattters when querying org resources is 'this' * Refactor query to simplify repo function Create separate CTEs for each case: global special (children or descendant), global individual, and org (this). This allows us to query directly into perms.GrantTuple; no additional logic required. * feat: grantsForUser for Project resources (#5669) * Reuse testInput * Create grantsForUserProjectResources query * Create grantsForUserProjectResources repo func * Create tests for grantsForUserProjectResources * Split up CTEs by grant scope and simplify repo function * Add test cases for missing reqScope id * Remove unnecessary join to iam_scope_org table * Reuse 'roles_with_grants' CTE across the grantsForUser queriers * Change reqScope parameter type from `Scope` to `string` (#5748) * Change 'reqScope' parameter type to: string * Simplify grantsForUserGlobalQuery & repo func * Test: GrantsForUser for Accounts for various user to role relationships (#5631) * test(iam): Refactor GrantsForUser tests into a single test * test(iam): Extend TestGrantsForUser to include Account resource Note: These tests will fail until GrantsForUser is refactored to return only grants whose scopes are applicable to this resource * test(iam): test(iam): Add Target resource to TestGrantsForUser * feat: grantsForUser for recursive Global or Org resources (#5747) * Create query: grantsForUserGlobalAndOrgResourcesQuery * Create repo function: grantsForUserGlobalAndOrgResourcesRecursive * Create tests for grantsForUserGlobalAndOrgResources * Restrict recursive list for Global/Org resources to global scope only Return error for non-recursive scopes * Remove unused option parameter * Add additional scope and test cases - Test for grants against a resource with no permission granted for it - Test for grants against a specific resource id without an explicit type set in the grant string - Add a project scope to ensure its grants aren't returned * Return error when passed 'Unknown' or 'All' resource type * Use constants for 'unknown' & '*' resources * feat: grantsForUser for recursive Project resources (#5783) * Create query: grantsForUserProjectResourcesGlobalScopeQuery * Create query: grantsForUserProjectResourcesOrgScopeQuery * Create repo function: grantsForUserProjectResourcesRecursiveScopes * Add recursive testcases to existing grantsForUserProjectResource test function * Address PR feedback - Add testcases for Unknown & All resource types - Change a grant string to a pinned resource. Its resource type changed from Target type to Unknown type - Call through to grantsForUserProjectResources when reqScopeId is a project scope (i.e. a non-recursve scope) * Add 'Recursive' to query var naming * feat: migration hook grants refactor (#5733) * scaffolding * comments * rename migration number * implement FindIllegalAssociations for 97006 hook * refactor findIllegalAssociation query * implement RepairIllegalAssociations and refactor queries * make gen * fix missing err check * pr feedback fix hook fix message wording * sql formatting * update comment * sql formatting * Apply suggestions from code review Co-authored-by: David Kanney <david.kanney@hashicorp.com> * change 'illegal' to 'invalid' * pr comment feedback * small comment update --------- Co-authored-by: David Kanney <david.kanney@hashicorp.com> * feat: grantsForUser for Global/Org/Project resources (#5807) * Change naming to be consistent with other grantsForUser functions * Create grantsForUser query for global-scoped Global/Org/Proj resources * Create grantsForUser repo function for Global/Org/Proj resources * Create tests for grantsForUserGlobalOrOrgOrProjectResources * Create grantsForUser query for org-scoped Globa/Org/Proj resources * Add tests for grantsForUser Global/Org/Proj resource query's org & project request_scopes * Add clarifying comments and shorter naming convention * Add 'children' grant_scoped org roles to result set * feat: grants: add resource type parameters to GrantsForUser (#5750) * add resource.Type to auth.Verify callchain * remove WithType option * update internal/auth/additional_verification_test.go to no longer use WithType * handle explicit resource type in aclAndGrantHashForUser * update interceptor_test.go to use resource.Scope for testing auth.Verify * use resource.Scope to test Audit events * add godoc comment on GrantsForUser * add recursive options to iam * add recursive options to auth * internal/auth/ldap/managed_group_role_grants_test.go fix GrantsForUser signature * internal/daemon/controller/handlers/accounts/account_service_test.go fix GrantsForUser signature * update GrantsForUser godoc * replace GrantsForUser tests with a new version with scoped down grants * add mix type assignment to TestGrantsForUser * move GrantsForUser test to internal/iam/repository_role_grant_ext_test.go * remove unused options * rewrite GrantsForUser tests to match the new expectation * handle recursive call in s.authResult * fix missing import from rebase * resolve ListResolvableAlias destination permissions properly * use global scope for ListResolvableAlias aclAndGrantHashForUser * make auth.WithRecursive take boolean input * make iam.WithRecursive option takes a boolean input * auth/options_test.go add assertion for default option values * make gen * add back ACL tests to grants for user * Support previous grants use-cases (#5823) * rename parameter to TestManagedGroupMember * make replace old GrantsForUser with new queries * fix tests and bugs where global grants don't get resolved * remove unnecessary grants from grantsForUserProjectResourcesOrgScopeRecursiveQuery * return full grants for any recursive call * handle this and role scope ID in test * order list result by create time instead of update time * add missing err check * handle converting role scope ID to this in grant scope API * fix role service tests * correct default scope role name * add special handling in grantsForUser for scope resource * fix tests to match role creation that does not bump version * creating alias in invalid scope now returns permissions denied instead of internal * remove test which looks for account in an authmethod in project scope which is an invalid config * fix TestLdapManagedGroupRoleGrants to use scope appropriate resource for the test * fix TestFetchActionSetForId and split org/proj test cases * Validate reqScopeId for grantsForUserRecursive() * Update tests based on recursive grantsForUser changes * Remove last recursive function in favor of grantsForUserRecursive() * move Scope resource to be AllowedIn all scope types * allow multiple resouce types when calling auth.Verify down to GrantsForUsers * use append instead of ranging over slice * minor fix to tests * Skip default role creation to avoid returning default role in test * Update wantErrMsg to accommodate multiple resource types * Add new entries to TestGrantsForUser test results based on recursive query changes * Update unneccessaryRoles in recursive global/org test case based on changes to recursive query --------- Co-authored-by: dkanney <david.kanney@hashicorp.com> * Migrate roles and grants to new table structure (#5814) * Refactor migration files to create tables, functions, triggers, and complete the actual data migration in their own files. update migrations and tests clean up removed files rebase onto bosorawis-sql-grants-migration-hook * Add comments to migration files, clean up some formatting, and add 30k project roles to test migration efficiency update migrations go test file Delete duplicate test file * Fix formatting in migration files * Rename hook number to reflect new number of migration files split migrations into hook files, large test file, and more specific test functions * Include missed oss file * Apply suggestions from code review Co-authored-by: Sorawis Nilparuk (Bo) <sorawis.nilparuk@hashicorp.com> --------- Co-authored-by: Sorawis Nilparuk (Bo) <sorawis.nilparuk@hashicorp.com> * Refactor GrantsForUser subqueries to use aggregate functions (#5822) * Use shorter var name * Refactor grantsForUserGlobalResourcesQuery: Use aggregate function array_agg to reduce rows returned from DB Adding mutliple grants to a role exposes the need to aggregate certain fields (canonical_grant). - Before this change, a row is returned for each distinct canonical grant for each role. - After this change, only one row is returned per role. Each row contains an array of all canonical_grants that apply to the role. * Refactor grantsForUserOrgResourcesQuery: Use aggregate function array_agg to reduce rows returned from DB Adding mutliple grants to a role exposes the need to aggregate certain fields (individual_grant_scopes, canonical_grant). - Before this change, a row is returned for each distinct canonical grant for each role. - After this change, only one row is returned per role. Each row contains an array of all canonical_grants (and individual_grant_scopes) that apply to the role. * Refactor grantsForUserProjectResourcesQuery: Use aggregate function array_agg to reduce rows returned from DB Adding mutliple grants to a role exposes the need to aggregate certain fields (individual_grant_scopes, canonical_grant). - Before this change, a row is returned for each distinct canonical grant for each role. - After this change, only one row is returned per role. Each row contains an array of all canonical_grants (and individual_grant_scopes) that apply to the role. * Address PR feedback * Finish PR feedback * Remove unnecessary if-check * Match test name to function name * Refactor grantsForUserRecursiveQuery: Use aggregate function array_agg to reduce rows returned from DB * fix broken test (#5842) * update tests to not check a table that will be dropped in the migration (#5847) * Fix global scope's parent scope id (#5844) * Use variable over hard-coded string * 'global' scope should have no parent scope * Clean up migrated tables (#5837) * Refactor migration files to create tables, functions, triggers, and complete the actual data migration in their own files. update migrations and tests clean up removed files rebase onto bosorawis-sql-grants-migration-hook * Add comments to migration files, clean up some formatting, and add 30k project roles to test migration efficiency update migrations go test file Delete duplicate test file * Fix formatting in migration files * Rename hook number to reflect new number of migration files split migrations into hook files, large test file, and more specific test functions * Drop unneeded iam_role_grant_scope table and unneeded name, description, and version columns from iam_role * fix whitespace * fix broken sql from removing iam role grant scope (#5860) * Ensure table drop also cascades changes to cross-table dependencies * fix broken pgtap from removing iam-role-grant-scope table * drop trigger which deletes from iam-role-grant-scope --------- Co-authored-by: Michael Milton <michael.milton@hashicorp.com> * test(groups): add grants tests for groups API (#5403) * first test with all the required setup * v1 of test * add primitive func and more test * refactor read tests into a single top level * move token generation to a function * add test for creates * add delete tests * add update test * only check for version and update_time * move setup resource into testcase to support grants with specific ID * add member tests * add group-member test example with multiple actions * remove duplicate group membership tests * ran make gen * fix missing parentID bug * fix typo * fix test names and add test cases * switch from google/uuid to hashicorp/go-uuid * add comment to groupmember tests * small comment change * pull shared test utility code from PR #5418 * refactor role grants out of authtoken package * unexport utility function * Remove dead code * lint and make gen * fix role cration logic * fix password TestAccountFunc implementation * implement TestAccountFunc for LDAP * implement TestAccountFunc for OIDC * implement TestUserFunc for managed groups * use managed groups in grants test * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * switch from list to map based test case for create tests * undo merge mistakes * fix merge mistakes * lint * add setup examples * add output fields tests for getgroup * reimplement with reflect * add test for CreateGroup * add all single resource action tests * add list test * rename function argument * move AssertOutputFields to handlers package * fix lint * make gen * use proto.Message instead of custom interface * switch to hashicorp/go-uuid * fix typo * fix error message * id= to ids= * make generating test accounts more randomized * Trigger CI checks * refactor auth/iam grants test setup * move a test to _test package * lint * minor comment fix * use Id instead of ID * make user/account setup in iam returns account instead of just account ID * missed one change * reorganize tests * make gen * feat: add grants tests for billing resource (#5559) * first test with all the required setup * v1 of test * add primitive func and more test * refactor read tests into a single top level * move token generation to a function * add test for creates * add delete tests * add update test * only check for version and update_time * move setup resource into testcase to support grants with specific ID * add member tests * add group-member test example with multiple actions * remove duplicate group membership tests * ran make gen * fix missing parentID bug * fix typo * fix test names and add test cases * switch from google/uuid to hashicorp/go-uuid * add comment to groupmember tests * small comment change * pull shared test utility code from PR #5418 * refactor role grants out of authtoken package * unexport utility function * Remove dead code * lint and make gen * fix role cration logic * fix password TestAccountFunc implementation * implement TestAccountFunc for LDAP * implement TestAccountFunc for OIDC * implement TestUserFunc for managed groups * use managed groups in grants test * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * switch from list to map based test case for create tests * undo merge mistakes * fix merge mistakes * lint * add setup examples * add output fields tests for getgroup * reimplement with reflect * add test for CreateGroup * add all single resource action tests * add list test * rename function argument * move AssertOutputFields to handlers package * fix lint * make gen * use proto.Message instead of custom interface * switch to hashicorp/go-uuid * fix typo * fix error message * id= to ids= * make generating test accounts more randomized * Trigger CI checks * refactor auth/iam grants test setup * lint * minor comment fix * use Id instead of ID * make user/account setup in iam returns account instead of just account ID * missed one change * reorganize tests * make gen * feat: add grants tests for billing resource add test coverage for testing grants with billing resource. This tests `monthly-active-users` action with billing. billing does not support output_fields so there are no tests for that * add negative test coverage for descendant scope and org scope * move negative tests to the billing resource * revert alias changes * revert alias test name change * resolve rebase conflict --------- Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> * Add grants tests for accounts (#5566) * first test with all the required setup * v1 of test * add primitive func and more test * small comment change * refactor role grants out of authtoken package * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * undo merge mistakes * fix merge mistakes * Trigger CI checks * refactor auth/iam grants test setup * Trigger CI checks * add CRUDL tests * add change-password and set-password tests * add UpdateAccount, ChangePassword, SetPassword tests * add ListAccount output_fields test * make gen * fix error message * rebased against llb * make gen * fix post rebase * fix typo * make some tests use type-specific grants * fix rebase issue * add negative test to read * test(credentials): Add grants tests (#5608) * test(credentials): List tests * test(credentials): Get tests * test(credentials): Add "attributes" output_field & one its subtypes to the Read tests * test(credentials): Create tests * test(credentials): Update tests * test(credentials): Delete tests * test(credentials): Add additional test cases for pinned cred store id * feat: add grants tests for alias (#5550) * first test with all the required setup * v1 of test * add primitive func and more test * refactor read tests into a single top level * move token generation to a function * add test for creates * add delete tests * add update test * only check for version and update_time * move setup resource into testcase to support grants with specific ID * add member tests * add group-member test example with multiple actions * remove duplicate group membership tests * ran make gen * fix missing parentID bug * fix typo * fix test names and add test cases * switch from google/uuid to hashicorp/go-uuid * add comment to groupmember tests * small comment change * pull shared test utility code from PR #5418 * refactor role grants out of authtoken package * unexport utility function * Remove dead code * lint and make gen * fix role cration logic * fix password TestAccountFunc implementation * implement TestAccountFunc for LDAP * implement TestAccountFunc for OIDC * implement TestUserFunc for managed groups * use managed groups in grants test * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * switch from list to map based test case for create tests * undo merge mistakes * fix merge mistakes * lint * add setup examples * add output fields tests for getgroup * reimplement with reflect * add test for CreateGroup * add all single resource action tests * add list test * rename function argument * move AssertOutputFields to handlers package * fix lint * make gen * use proto.Message instead of custom interface * switch to hashicorp/go-uuid * fix typo * fix error message * id= to ids= * make generating test accounts more randomized * Trigger CI checks * refactor auth/iam grants test setup * lint * minor comment fix * use Id instead of ID * make user/account setup in iam returns account instead of just account ID * missed one change * reorganize tests * make gen * feat: add grants tests for alias add test coverage for testing grants with alias resource. This tests all actions with aliases and different grant scopes * add tests for output fields * add more test cases for actions, id * update output assert to use shared assert function * rebase * use hashicorp/go-uuid instead of google/uuid --------- Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> Co-authored-by: Elim Tsiagbey <elim.tsiagbey@hashicorp.com> * test: add grants tests for host_catalogs (#5573) * first test with all the required setup * v1 of test * add primitive func and more test * refactor read tests into a single top level * move token generation to a function * add test for creates * add delete tests * add update test * only check for version and update_time * move setup resource into testcase to support grants with specific ID * add member tests * add group-member test example with multiple actions * remove duplicate group membership tests * ran make gen * fix missing parentID bug * fix typo * fix test names and add test cases * switch from google/uuid to hashicorp/go-uuid * add comment to groupmember tests * small comment change * pull shared test utility code from PR #5418 * refactor role grants out of authtoken package * unexport utility function * Remove dead code * lint and make gen * fix role cration logic * fix password TestAccountFunc implementation * implement TestAccountFunc for LDAP * implement TestAccountFunc for OIDC * implement TestUserFunc for managed groups * use managed groups in grants test * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * switch from list to map based test case for create tests * undo merge mistakes * fix merge mistakes * lint * add setup examples * add output fields tests for getgroup * reimplement with reflect * add test for CreateGroup * add all single resource action tests * add list test * rename function argument * move AssertOutputFields to handlers package * fix lint * make gen * use proto.Message instead of custom interface * switch to hashicorp/go-uuid * fix typo * fix error message * id= to ids= * make generating test accounts more randomized * Trigger CI checks * refactor auth/iam grants test setup * move a test to _test package * lint * minor comment fix * use Id instead of ID * make user/account setup in iam returns account instead of just account ID * missed one change * reorganize tests * make gen * Trigger CI checks * test: add grants tests for host_catalogs * add test coverage for output fields and write actions * add missing error check assertions * fixup! address PR comments * fixup! fix lint errors * chore(host_catalogs_test): Remove duplicate import * test(hostcatalogs): Add tests for actions w/o grants * test(hostcatalogs): Use ldap managed group for all test cases * feat: add grants tests for billing resource (#5559) * first test with all the required setup * v1 of test * add primitive func and more test * refactor read tests into a single top level * move token generation to a function * add test for creates * add delete tests * add update test * only check for version and update_time * move setup resource into testcase to support grants with specific ID * add member tests * add group-member test example with multiple actions * remove duplicate group membership tests * ran make gen * fix missing parentID bug * fix typo * fix test names and add test cases * switch from google/uuid to hashicorp/go-uuid * add comment to groupmember tests * small comment change * pull shared test utility code from PR #5418 * refactor role grants out of authtoken package * unexport utility function * Remove dead code * lint and make gen * fix role cration logic * fix password TestAccountFunc implementation * implement TestAccountFunc for LDAP * implement TestAccountFunc for OIDC * implement TestUserFunc for managed groups * use managed groups in grants test * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * switch from list to map based test case for create tests * undo merge mistakes * fix merge mistakes * lint * add setup examples * add output fields tests for getgroup * reimplement with reflect * add test for CreateGroup * add all single resource action tests * add list test * rename function argument * move AssertOutputFields to handlers package * fix lint * make gen * use proto.Message instead of custom interface * switch to hashicorp/go-uuid * fix typo * fix error message * id= to ids= * make generating test accounts more randomized * Trigger CI checks * refactor auth/iam grants test setup * lint * minor comment fix * use Id instead of ID * make user/account setup in iam returns account instead of just account ID * missed one change * reorganize tests * make gen * feat: add grants tests for billing resource add test coverage for testing grants with billing resource. This tests `monthly-active-users` action with billing. billing does not support output_fields so there are no tests for that * add negative test coverage for descendant scope and org scope * move negative tests to the billing resource * revert alias changes * revert alias test name change * resolve rebase conflict --------- Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> * Add grants tests for accounts (#5566) * first test with all the required setup * v1 of test * add primitive func and more test * small comment change * refactor role grants out of authtoken package * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * undo merge mistakes * fix merge mistakes * Trigger CI checks * refactor auth/iam grants test setup * Trigger CI checks * add CRUDL tests * add change-password and set-password tests * add UpdateAccount, ChangePassword, SetPassword tests * add ListAccount output_fields test * make gen * fix error message * rebased against llb * make gen * fix post rebase * fix typo * make some tests use type-specific grants * fix rebase issue * add negative test to read * test(credentials): Add grants tests (#5608) * test(credentials): List tests * test(credentials): Get tests * test(credentials): Add "attributes" output_field & one its subtypes to the Read tests * test(credentials): Create tests * test(credentials): Update tests * test(credentials): Delete tests * test(credentials): Add additional test cases for pinned cred store id * feat: add grants tests for alias (#5550) * first test with all the required setup * v1 of test * add primitive func and more test * refactor read tests into a single top level * move token generation to a function * add test for creates * add delete tests * add update test * only check for version and update_time * move setup resource into testcase to support grants with specific ID * add member tests * add group-member test example with multiple actions * remove duplicate group membership tests * ran make gen * fix missing parentID bug * fix typo * fix test names and add test cases * switch from google/uuid to hashicorp/go-uuid * add comment to groupmember tests * small comment change * pull shared test utility code from PR #5418 * refactor role grants out of authtoken package * unexport utility function * Remove dead code * lint and make gen * fix role cration logic * fix password TestAccountFunc implementation * implement TestAccountFunc for LDAP * implement TestAccountFunc for OIDC * implement TestUserFunc for managed groups * use managed groups in grants test * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * switch from list to map based test case for create tests * undo merge mistakes * fix merge mistakes * lint * add setup examples * add output fields tests for getgroup * reimplement with reflect * add test for CreateGroup * add all single resource action tests * add list test * rename function argument * move AssertOutputFields to handlers package * fix lint * make gen * use proto.Message instead of custom interface * switch to hashicorp/go-uuid * fix typo * fix error message * id= to ids= * make generating test accounts more randomized * Trigger CI checks * refactor auth/iam grants test setup * lint * minor comment fix * use Id instead of ID * make user/account setup in iam returns account instead of just account ID * missed one change * reorganize tests * make gen * feat: add grants tests for alias add test coverage for testing grants with alias resource. This tests all actions with aliases and different grant scopes * add tests for output fields * add more test cases for actions, id * update output assert to use shared assert function * rebase * use hashicorp/go-uuid instead of google/uuid --------- Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> Co-authored-by: Elim Tsiagbey <elim.tsiagbey@hashicorp.com> * test(hostcatalogs): Add an additional project to "create" test cases --------- Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> Co-authored-by: dkanney <dkanney@terpmail.umd.edu> Co-authored-by: David Kanney <david.kanney@hashicorp.com> * Add grants tests for authmethods (#5569) * test(authmethods): Simplify read actions test cases * test(authmethods): Create grants tests for write actions * test(authmethods): Create test cases for action: authenticate * test(authmethods): Update tests to use userFunc & other changes from upstream branch * test(authmethods): Add output_fields to ReadActions tests * test(authmethods): Add output_fields to WriteActions tests * chore(authmethods_test): Consolidate common output fields into a single struct * test(authmethods): Add test cases for project roles * test(authmethods): Fix authenticate testcases * test(authmethods): Add tests granting permission to specific ids using multiple roles * test(hostsets): add grants tests (#5591) * test(hostsets): List tests * test(hostsets): Get tests * test(hostsets): Create tests * test(hostsets): Update tests * test(hostsets): Delete tests * test(hostsets}: AddHostSetHosts tests * test(hostsets): RemoveHostSetHosts tests * test(hostsets): SetHostSetHosts tests * test(hostsets): Use unique host-catalog names to avoid duplicate key DB errors * test(hostsets): Add second project to enforce exclusivity when Listing host-sets * test: credentials store grants (#5592) * first test with all the required setup * v1 of test * add primitive func and more test * refactor read tests into a single top level * move token generation to a function * add test for creates * add delete tests * add update test * only check for version and update_time * move setup resource into testcase to support grants with specific ID * add member tests * add group-member test example with multiple actions * remove duplicate group membership tests * ran make gen * fix missing parentID bug * fix typo * fix test names and add test cases * switch from google/uuid to hashicorp/go-uuid * add comment to groupmember tests * small comment change * pull shared test utility code from PR #5418 * refactor role grants out of authtoken package * unexport utility function * Remove dead code * lint and make gen * fix role cration logic * fix password TestAccountFunc implementation * implement TestAccountFunc for LDAP * implement TestAccountFunc for OIDC * implement TestUserFunc for managed groups * use managed groups in grants test * undo removal of authtoken.TestAuthTokenWithRoles for future refactor * switch from list to map based test case for create tests * undo merge mistakes * fix merge mistakes * lint * add setup examples * add output fields tests for getgroup * reimplement with reflect * add test for CreateGroup * add all single resource action tests * add list test * rename function argument * move AssertOutputFields to handlers package * fix lint * make gen * use proto.Message instead of custom interface * switch to hashicorp/go-uuid * fix typo * fix error message * id= to ids= * make generating test accounts more randomized * Trigger CI checks * refactor auth/iam grants test setup * lint * minor comment fix * use Id instead of ID * make user/account setup in iam returns account instead of just account ID * missed one change * save * add list tests * add get test * add create and delete test * add delete and update tests * more tests * fix collection_authorized_actions grants not resolving * complete output_fields tests * fix import groups * make gen * fixed broken tests * fix rebase * switch all tests to TestUserGroupGrantsFunc * remove duplicate test * test: add grants tests for managed groups resource (#5642) * test: add grants tests for managed groups resource * PR reviews * address PR comments * test: add grants tests for auth tokens resource (#5644) * test: add grants tests for auth tokens resource * add authorized actions tests for resources with sub-resources (#5835) * add authorized actions tests for resources with sub-resources * make gen * bosorawis remove TestRoleGrantsForToken (#5840) * refactor auth method grants tests * refactor credential libraries grants tests * refactor hosts grants tests * refactor roles grants tests * refactor scopes grants tests * refactor tcp targets grants tests * refactor users grants tests * refactor worker grants tests * fix authmethod test broken during the refactor * remove authtoken.TestRoleGrantsForToken * add pinned ID test * correct event name * fix make gen and lint * Remove old test Role creation code Removing leftover logic from old grants data model because it caused Group-association tests to fail * update go mod * run make gen and move new migrations to new folder (#5862) * run make gen and move new migrations to new folder * make tools and make gen * update hook number * update prior migration * move 97005 to 97001 for consistency * test: add grants tests for session resource (#5855) * add grants tests for sessions * fix missing parentScopeId * make gen * Resolve GrantsForUser queries via resolveQuery() (#5836) * Add validation for nil resource type * Remove redundant recursive test * Refactor GrantsForUser() to perform query resolution & data mapping - Remove grantsForUser sub-functions - Resolve grantsForUser queries via resolveQuery() - resolveQuery() and map data to GrantTuples in GrantsForUser() * remove dead code --------- Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> * add grants tests for scope resource (#5845) (#5865) * 'List' tests for scopes * 'Get' tests for scopes * 'Create' tests for scopes * 'List Key Version Destruction Jobs' tests for scopes * Add output_fields testing to 'Get' test * Add additional test case for list-key-version-destruction-jobs * test: add grants tests for roles resource (#5864) * add grants tests for roles * make gen * test: add grants tests for worker resource (#5841) * add read createControllerLed createWorkerLed tests # Conflicts: # internal/daemon/controller/handlers/workers/grants_test.go * minor refactor * add output fields tests for list * add tests for addworkertags readcertificateauthority reinitializecertificateauthority * finish all worker api tests * fix make gen and lint * fix unchecked error lint * add missing output fields assertion * Bosorawis fix set role grant scope not handle children grant already exist (#5868) * add new test case where role already has children and attempt to set children and individual project * handle case where role already has children grant scope attempt to set children and project * test: add grants tests for target resource (#5861) * additional tests to target resource * add test coverage for SetTargetCredentialSources * make gen * review suggestions * test: add grants test for user resource and fix ACL bug (#5869) * add some list-resolvable-aliases tests * fix missing fields * add more test and found edge case with children grants * more list-resolvable test cases * fix edge case where resource ids not overlapping between parent and child are not considered * resolve conflict * remove name, version, description from expect output_fields * remove name and description from toProto and add godoc comment --------- Co-authored-by: David Kanney <david.kanney@hashicorp.com> Co-authored-by: Elim Tsiagbey <elim.tsiagbey@hashicorp.com> Co-authored-by: Sorawis Nilparuk <sorawis.nilparuk@hashicorp.com> Co-authored-by: Elim Tsiagbey <elimty02@gmail.com> Co-authored-by: dkanney <dkanney@terpmail.umd.edu>
This commit is contained in:
126 files changed
+97018
-4310
No files matched your search
@@ -173,7 +173,7 @@ func NewVerifierContext(ctx context.Context,
|
||||
// may come from the URL and may come from the token) and whether or not to
|
||||
// proceed, e.g. whether the authn/authz check resulted in failure. If an error
|
||||
// occurs it's logged to the system log.
|
||||
func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) {
|
||||
func Verify(ctx context.Context, resourceType resource.Type, opt ...Option) (ret VerifyResults) {
|
||||
const op = "auth.Verify"
|
||||
ret.Error = handlers.ForbiddenError()
|
||||
v, ok := ctx.Value(verifierKey).(*verifier)
|
||||
@@ -272,11 +272,11 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) {
|
||||
ScopeId: opts.withScopeId,
|
||||
Id: opts.withId,
|
||||
Pin: opts.withPin,
|
||||
Type: opts.withType,
|
||||
Type: resourceType,
|
||||
// Parent Scope ID will be filled in via performAuthCheck
|
||||
}
|
||||
// Global scope has no parent ID; account for this
|
||||
if opts.withId == scope.Global.String() && opts.withType == resource.Scope {
|
||||
if opts.withId == scope.Global.String() && resourceType == resource.Scope {
|
||||
v.res.ScopeId = scope.Global.String()
|
||||
}
|
||||
|
||||
@@ -284,10 +284,12 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) {
|
||||
v.decryptToken(ctx)
|
||||
}
|
||||
|
||||
resourcesToFetchGrants := append([]resource.Type{resourceType}, opts.withFetchAdditionalResourceGrants...)
|
||||
|
||||
var authResults perms.ACLResults
|
||||
var userData template.Data
|
||||
var err error
|
||||
authResults, ret.UserData, ret.Scope, v.acl, ret.grants, err = v.performAuthCheck(ctx)
|
||||
authResults, ret.UserData, ret.Scope, v.acl, ret.grants, err = v.performAuthCheck(ctx, resourcesToFetchGrants, opts.withRecursive)
|
||||
if err != nil {
|
||||
event.WriteError(ctx, op, err, event.WithInfoMsg("error performing authn/authz check"))
|
||||
return
|
||||
@@ -491,7 +493,7 @@ func (v *verifier) decryptToken(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (v verifier) performAuthCheck(ctx context.Context) (
|
||||
func (v verifier) performAuthCheck(ctx context.Context, resourceType []resource.Type, isRecursiveRequest bool) (
|
||||
aclResults perms.ACLResults,
|
||||
userData template.Data,
|
||||
scopeInfo *scopes.ScopeInfo,
|
||||
@@ -651,7 +653,8 @@ func (v verifier) performAuthCheck(ctx context.Context) (
|
||||
|
||||
// Fetch and parse grants for this user ID (which may include grants for
|
||||
// u_anon and u_auth)
|
||||
grantTuples, err = iamRepo.GrantsForUser(v.ctx, *userData.User.Id)
|
||||
iamOpt := []iam.Option{iam.WithRecursive(isRecursiveRequest)}
|
||||
grantTuples, err = iamRepo.GrantsForUser(v.ctx, *userData.User.Id, resourceType, scopeInfo.Id, iamOpt...)
|
||||
if err != nil {
|
||||
retErr = errors.Wrap(ctx, err, op)
|
||||
return
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/server"
|
||||
"github.com/hashicorp/boundary/internal/tests/api"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/eventlogger/filters/encrypt"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -243,7 +244,7 @@ func TestVerify_AuditEvent(t *testing.T) {
|
||||
ctx := NewVerifierContext(ctx, iamRepoFn, tokenRepoFn, serversRepoFn, testKms, &requestInfo)
|
||||
|
||||
_ = os.WriteFile(eventConfig.AuditEvents.Name(), nil, 0o666) // clean out audit events from previous calls
|
||||
_ = Verify(ctx, tt.opt...)
|
||||
_ = Verify(ctx, resource.Scope, tt.opt...)
|
||||
got := api.CloudEventFromFile(t, eventConfig.AuditEvents.Name())
|
||||
|
||||
if tt.wantAuthAuditData {
|
||||
@@ -285,6 +286,7 @@ func TestGrantsHash(t *testing.T) {
|
||||
}
|
||||
|
||||
o, _ := iam.TestScopes(t, iamRepo)
|
||||
resourceType := resource.Scope
|
||||
req := httptest.NewRequest("GET", "http://127.0.0.1/v1/scopes/"+o.GetPublicId(), nil)
|
||||
|
||||
at := authtoken.TestAuthToken(t, conn, kms, o.GetPublicId())
|
||||
@@ -302,7 +304,7 @@ func TestGrantsHash(t *testing.T) {
|
||||
verifierCtx := NewVerifierContext(ctx, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
|
||||
// Create auth result from token
|
||||
res := Verify(verifierCtx, WithScopeId(o.GetPublicId()))
|
||||
res := Verify(verifierCtx, resourceType, WithScopeId(o.GetPublicId()))
|
||||
hash1, err := res.GrantsHash(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -325,7 +327,7 @@ func TestGrantsHash(t *testing.T) {
|
||||
verifierCtx = NewVerifierContext(ctx, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
|
||||
// Create auth result from token
|
||||
res = Verify(verifierCtx, WithScopeId(o.GetPublicId()))
|
||||
res = Verify(verifierCtx, resourceType, WithScopeId(o.GetPublicId()))
|
||||
hash2, err := res.GrantsHash(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -337,7 +339,7 @@ func TestGrantsHash(t *testing.T) {
|
||||
_ = iam.TestUserRole(t, conn, newRole.GetPublicId(), user.GetPublicId())
|
||||
|
||||
// Recreate auth result with new grants, should have a new hash
|
||||
res = Verify(verifierCtx, WithScopeId(o.GetPublicId()))
|
||||
res = Verify(verifierCtx, resourceType, WithScopeId(o.GetPublicId()))
|
||||
hash3, err := res.GrantsHash(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, bytes.Equal(hash1, hash3))
|
||||
|
||||
@@ -23,23 +23,40 @@ type Option func(*options)
|
||||
|
||||
// options = how options are represented
|
||||
type options struct {
|
||||
withScopeId string
|
||||
withPin string
|
||||
withId string
|
||||
withAction action.Type
|
||||
withType resource.Type
|
||||
withUserId string
|
||||
withKms *kms.Kms
|
||||
withRecoveryTokenNotAllowed bool
|
||||
withAnonymousUserNotAllowed bool
|
||||
withResource *perms.Resource
|
||||
withActions []string
|
||||
withScopeId string
|
||||
withPin string
|
||||
withId string
|
||||
withAction action.Type
|
||||
withUserId string
|
||||
withKms *kms.Kms
|
||||
withRecursive bool
|
||||
withRecoveryTokenNotAllowed bool
|
||||
withAnonymousUserNotAllowed bool
|
||||
withResource *perms.Resource
|
||||
withActions []string
|
||||
withFetchAdditionalResourceGrants []resource.Type
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
return options{}
|
||||
}
|
||||
|
||||
func WithRecursive(isRecursive bool) Option {
|
||||
return func(o *options) {
|
||||
o.withRecursive = isRecursive
|
||||
}
|
||||
}
|
||||
|
||||
// WithFetchAdditionalResourceGrants allows auth.Verify to fetch grants for additional resources to build a more
|
||||
// complete GrantTuples of the requesting identity. This ensures that we can accurately determine
|
||||
// authorized_action and authorized_collection_action for sub-resources
|
||||
// E.g. Reading 'host-catalog' should also fetch authorized actions for 'hosts'
|
||||
func WithFetchAdditionalResourceGrants(resources ...resource.Type) Option {
|
||||
return func(o *options) {
|
||||
o.withFetchAdditionalResourceGrants = append(o.withFetchAdditionalResourceGrants, resources...)
|
||||
}
|
||||
}
|
||||
|
||||
func WithScopeId(id string) Option {
|
||||
return func(o *options) {
|
||||
o.withScopeId = id
|
||||
@@ -64,12 +81,6 @@ func WithAction(action action.Type) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithType(rt resource.Type) Option {
|
||||
return func(o *options) {
|
||||
o.withType = rt
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserId(id string) Option {
|
||||
return func(o *options) {
|
||||
o.withUserId = id
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -20,16 +19,20 @@ func Test_GetOpts(t *testing.T) {
|
||||
withKms := new(kms.Kms)
|
||||
res := new(perms.Resource)
|
||||
|
||||
// test default values
|
||||
defaultOpts := getDefaultOptions()
|
||||
assert.Equal(t, options{}, defaultOpts)
|
||||
|
||||
opts := getOpts(
|
||||
WithScopeId("foo"),
|
||||
WithPin("bar"),
|
||||
WithId("zip"),
|
||||
WithAction(action.AddHosts),
|
||||
WithType(resource.Group),
|
||||
WithUserId("user"),
|
||||
WithKms(withKms),
|
||||
WithRecoveryTokenNotAllowed(true),
|
||||
WithAnonymousUserNotAllowed(true),
|
||||
WithRecursive(true),
|
||||
WithResource(res),
|
||||
WithActions([]string{"callback"}),
|
||||
)
|
||||
@@ -38,11 +41,11 @@ func Test_GetOpts(t *testing.T) {
|
||||
withPin: "bar",
|
||||
withId: "zip",
|
||||
withAction: action.AddHosts,
|
||||
withType: resource.Group,
|
||||
withUserId: "user",
|
||||
withKms: withKms,
|
||||
withRecoveryTokenNotAllowed: true,
|
||||
withAnonymousUserNotAllowed: true,
|
||||
withRecursive: true,
|
||||
withResource: res,
|
||||
withActions: []string{"callback"},
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ func (s Service) ListAccounts(ctx context.Context, req *pbs.ListAccountsRequest)
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetAuthMethodId(), action.List)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetAuthMethodId(), action.List, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -368,7 +368,7 @@ func (s Service) GetAccount(ctx context.Context, req *pbs.GetAccountRequest) (*p
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -410,7 +410,7 @@ func (s Service) CreateAccount(ctx context.Context, req *pbs.CreateAccountReques
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetAuthMethodId(), action.Create)
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetAuthMethodId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -449,7 +449,7 @@ func (s Service) UpdateAccount(ctx context.Context, req *pbs.UpdateAccountReques
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update)
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -485,7 +485,7 @@ func (s Service) DeleteAccount(ctx context.Context, req *pbs.DeleteAccountReques
|
||||
if err := validateDeleteRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -504,7 +504,7 @@ func (s Service) ChangePassword(ctx context.Context, req *pbs.ChangePasswordRequ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.ChangePassword)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.ChangePassword, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -543,7 +543,7 @@ func (s Service) SetPassword(ctx context.Context, req *pbs.SetPasswordRequest) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.SetPassword)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.SetPassword, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -1030,7 +1030,7 @@ func (s Service) setPasswordInRepo(ctx context.Context, scopeId, id string, vers
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (auth.AuthMethod, requestauth.VerifyResults) {
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type, isRecursive bool) (auth.AuthMethod, requestauth.VerifyResults) {
|
||||
res := requestauth.VerifyResults{}
|
||||
pwRepo, err := s.pwRepoFn()
|
||||
if err != nil {
|
||||
@@ -1049,7 +1049,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []requestauth.Option{requestauth.WithType(resource.Account), requestauth.WithAction(a)}
|
||||
opts := []requestauth.Option{requestauth.WithAction(a), requestauth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -1129,7 +1129,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
authMeth = am
|
||||
}
|
||||
opts = append(opts, requestauth.WithScopeId(authMeth.GetScopeId()), requestauth.WithPin(parentId))
|
||||
return authMeth, requestauth.Verify(ctx, opts...)
|
||||
return authMeth, requestauth.Verify(ctx, resource.Account, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in auth.Account, opt ...handlers.Option) (*pb.Account, error) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/server"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/accounts"
|
||||
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
@@ -4453,7 +4454,7 @@ func TestGrantsAcrossManagedGroups(t *testing.T) {
|
||||
wrap := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrap))
|
||||
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrap))
|
||||
|
||||
databaseWrapper, err := kmsCache.GetWrapper(ctx, org.PublicId, kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
@@ -4496,7 +4497,9 @@ func TestGrantsAcrossManagedGroups(t *testing.T) {
|
||||
iam.TestManagedGroupRole(t, conn, ldapRole.GetPublicId(), ldapMg.GetPublicId())
|
||||
iam.TestRoleGrant(t, conn, ldapRole.GetPublicId(), "ids=ttcp_ldap;actions=read")
|
||||
|
||||
grants, err := iamRepo.GrantsForUser(ctx, user.GetPublicId())
|
||||
// targets must be in a project scope so we're passing in a scope ID which we expect the user to have access to
|
||||
// which is the project under the role's org
|
||||
grants, err := iamRepo.GrantsForUser(ctx, user.GetPublicId(), []resource.Type{resource.Target}, proj.PublicId)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify we see both grants
|
||||
@@ -4520,7 +4523,7 @@ func TestGrantsAcrossManagedGroups(t *testing.T) {
|
||||
assert.Equal(t, 1, numDeleted)
|
||||
|
||||
// Verify we don't see the ldap grant anymore
|
||||
grants, err = iamRepo.GrantsForUser(ctx, user.GetPublicId())
|
||||
grants, err = iamRepo.GrantsForUser(ctx, user.GetPublicId(), []resource.Type{resource.Target}, proj.PublicId)
|
||||
require.NoError(t, err)
|
||||
foundOidc = false
|
||||
foundLdap = false
|
||||
@@ -4543,7 +4546,7 @@ func TestGrantsAcrossManagedGroups(t *testing.T) {
|
||||
assert.Equal(t, 1, numDeleted)
|
||||
|
||||
// Verify we don't see the oidc grant anymore
|
||||
grants, err = iamRepo.GrantsForUser(ctx, user.GetPublicId())
|
||||
grants, err = iamRepo.GrantsForUser(ctx, user.GetPublicId(), []resource.Type{resource.Target}, proj.PublicId)
|
||||
require.NoError(t, err)
|
||||
foundOidc = false
|
||||
foundLdap = false
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -98,7 +98,7 @@ func (s Service) ListAliases(ctx context.Context, req *pbs.ListAliasesRequest) (
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -240,7 +240,7 @@ func (s Service) GetAlias(ctx context.Context, req *pbs.GetAliasRequest) (*pbs.G
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func (s Service) CreateAlias(ctx context.Context, req *pbs.CreateAliasRequest) (
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -316,7 +316,7 @@ func (s Service) UpdateAlias(ctx context.Context, req *pbs.UpdateAliasRequest) (
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -352,7 +352,7 @@ func (s Service) DeleteAlias(ctx context.Context, req *pbs.DeleteAliasRequest) (
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -467,7 +467,7 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
iamRepo, err := s.iamRepoFn()
|
||||
if err != nil {
|
||||
@@ -476,7 +476,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Alias), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -508,7 +508,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Alias, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *target.Alias, opt ...handlers.Option) (*pb.Alias, error) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -78,6 +78,11 @@ var (
|
||||
resource.Account: accounts.CollectionActions,
|
||||
resource.ManagedGroup: managed_groups.CollectionActions,
|
||||
}
|
||||
|
||||
additionalResourceGrants = []resource.Type{
|
||||
resource.Account,
|
||||
resource.ManagedGroup,
|
||||
}
|
||||
)
|
||||
|
||||
// Service handles request as described by the pbs.AuthMethodServiceServer interface.
|
||||
@@ -155,7 +160,7 @@ func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRe
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -320,7 +325,7 @@ func (s Service) GetAuthMethod(ctx context.Context, req *pbs.GetAuthMethodReques
|
||||
if err := validateGetRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -365,7 +370,7 @@ func (s Service) CreateAuthMethod(ctx context.Context, req *pbs.CreateAuthMethod
|
||||
if err := validateCreateRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -410,7 +415,7 @@ func (s Service) UpdateAuthMethod(ctx context.Context, req *pbs.UpdateAuthMethod
|
||||
if err := validateUpdateRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -464,7 +469,7 @@ func (s Service) ChangeState(ctx context.Context, req *pbs.ChangeStateRequest) (
|
||||
if err := validateChangeStateRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ChangeState)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ChangeState, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -512,7 +517,7 @@ func (s Service) DeleteAuthMethod(ctx context.Context, req *pbs.DeleteAuthMethod
|
||||
if err := validateDeleteRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -546,7 +551,7 @@ func (s Service) Authenticate(ctx context.Context, req *pbs.AuthenticateRequest)
|
||||
}
|
||||
}
|
||||
|
||||
authResults := s.authResult(ctx, req.GetAuthMethodId(), action.Authenticate)
|
||||
authResults := s.authResult(ctx, req.GetAuthMethodId(), action.Authenticate, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -765,12 +770,17 @@ func (s Service) changeStateInRepo(ctx context.Context, req *pbs.ChangeStateRequ
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "Given auth method type does not support changing state")
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) requestauth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) requestauth.VerifyResults {
|
||||
const op = "authmethods.(Service).authResult"
|
||||
res := requestauth.VerifyResults{}
|
||||
|
||||
var parentId string
|
||||
opts := []requestauth.Option{requestauth.WithType(resource.AuthMethod), requestauth.WithAction(a)}
|
||||
opts := []requestauth.Option{
|
||||
requestauth.WithAction(a),
|
||||
requestauth.WithRecursive(isRecursive),
|
||||
requestauth.WithFetchAdditionalResourceGrants(additionalResourceGrants...),
|
||||
}
|
||||
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -847,7 +857,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) reque
|
||||
opts = append(opts, requestauth.WithId(id))
|
||||
}
|
||||
opts = append(opts, requestauth.WithScopeId(parentId))
|
||||
return requestauth.Verify(ctx, opts...)
|
||||
return requestauth.Verify(ctx, resource.AuthMethod, opts...)
|
||||
}
|
||||
|
||||
func toAuthMethodProto(ctx context.Context, in auth.AuthMethod, opt ...handlers.Option) (*pb.AuthMethod, error) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -84,7 +84,7 @@ func (s Service) ListAuthTokens(ctx context.Context, req *pbs.ListAuthTokensRequ
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -224,7 +224,7 @@ func (s Service) GetAuthToken(ctx context.Context, req *pbs.GetAuthTokenRequest)
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ReadSelf)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ReadSelf, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func (s Service) DeleteAuthToken(ctx context.Context, req *pbs.DeleteAuthTokenRe
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.DeleteSelf)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.DeleteSelf, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -334,11 +334,11 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.AuthToken), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -375,7 +375,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.AuthToken, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *authtoken.AuthToken, opt ...handlers.Option) (*pb.AuthToken, error) {
|
||||
|
||||
@@ -9,13 +9,17 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
a "github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
at "github.com/hashicorp/boundary/internal/daemon/controller/handlers/authtokens"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -65,49 +69,258 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListAuthTokensRequest
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
name string
|
||||
input *pbs.ListAuthTokensRequest
|
||||
userFunc func() (*iam.User, a.Account)
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
expectOutfields []string
|
||||
}{
|
||||
{
|
||||
name: "global role grant this and children returns global and org groups",
|
||||
name: "global role grant this returns global token",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=auth-token;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,scope_id,scope,user_id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{globalAT.PublicId, org1AT.PublicId, org2AT.PublicId},
|
||||
wantIDs: []string{globalAT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this and children returns org groups",
|
||||
name: "global role grant this and children returns global and org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=auth-token;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{globalAT.PublicId, org1AT.PublicId, org2AT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
globals.AuthMethodIdField,
|
||||
globals.AccountIdField,
|
||||
globals.CreatedTimeField,
|
||||
globals.UpdatedTimeField,
|
||||
globals.ApproximateLastUsedTimeField,
|
||||
globals.ExpirationTimeField,
|
||||
globals.AuthorizedActionsField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this and descendants returns global and org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=auth-token;actions=list,read;output_fields=id,approximate_last_used_time,expiration_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{globalAT.PublicId, org1AT.PublicId, org2AT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ApproximateLastUsedTimeField,
|
||||
globals.ExpirationTimeField,
|
||||
globals.AuthorizedActionsField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant descendants with recursive returns global and org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=auth-token;actions=list,read;output_fields=id,approximate_last_used_time,expiration_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{org1AT.PublicId, org2AT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ApproximateLastUsedTimeField,
|
||||
globals.ExpirationTimeField,
|
||||
globals.AuthorizedActionsField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant descendants without recursive returns error",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: false,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=auth-token;actions=list,read;output_fields=id,approximate_last_used_time,expiration_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
wantIDs: nil,
|
||||
},
|
||||
{
|
||||
name: "org role grant this returns org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: org1.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org1.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=auth-token;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=auth-token;actions=list,read;output_fields=id,auth_method_id,account_id,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{org1AT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.AuthMethodIdField,
|
||||
globals.AccountIdField,
|
||||
globals.CreatedTimeField,
|
||||
globals.UpdatedTimeField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this and children returns org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: org1.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{org1AT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
globals.AuthMethodIdField,
|
||||
globals.AccountIdField,
|
||||
globals.CreatedTimeField,
|
||||
globals.UpdatedTimeField,
|
||||
globals.ApproximateLastUsedTimeField,
|
||||
globals.ExpirationTimeField,
|
||||
globals.AuthorizedActionsField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children returns org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: org1.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{org1AT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
globals.AuthMethodIdField,
|
||||
globals.AccountIdField,
|
||||
globals.CreatedTimeField,
|
||||
globals.UpdatedTimeField,
|
||||
globals.ApproximateLastUsedTimeField,
|
||||
globals.ExpirationTimeField,
|
||||
globals.AuthorizedActionsField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this pinned id returns org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: org1.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{fmt.Sprintf("ids=%s;type=auth-token;actions=*", org1AT.PublicId)},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{org1AT.PublicId},
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
globals.AuthMethodIdField,
|
||||
globals.AccountIdField,
|
||||
globals.CreatedTimeField,
|
||||
globals.UpdatedTimeField,
|
||||
globals.ApproximateLastUsedTimeField,
|
||||
globals.ExpirationTimeField,
|
||||
globals.AuthorizedActionsField,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this does not list other org tokens",
|
||||
input: &pbs.ListAuthTokensRequest{
|
||||
ScopeId: org2.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: nil,
|
||||
expectOutfields: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
user, acct := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, acct.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.ListAuthTokens(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
@@ -124,6 +337,304 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
for _, id := range tc.wantIDs {
|
||||
require.Contains(t, gotIDs, id)
|
||||
}
|
||||
for _, item := range got.Items {
|
||||
handlers.TestAssertOutputFields(t, item, tc.expectOutfields)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.GetAuthTokenRequest
|
||||
userFunc func() (*iam.User, a.Account)
|
||||
wantErr error
|
||||
wantId string
|
||||
expectOutfields []string
|
||||
}{
|
||||
{
|
||||
name: "global role grant this returns global token",
|
||||
input: &pbs.GetAuthTokenRequest{
|
||||
Id: globalAT.PublicId,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=read;output_fields=id,scope_id,scope,user_id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
},
|
||||
wantId: globalAT.PublicId,
|
||||
},
|
||||
{
|
||||
name: "org role grant this returns org token",
|
||||
input: &pbs.GetAuthTokenRequest{
|
||||
Id: org1AT.PublicId,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=auth-token;actions=read;output_fields=id,auth_method_id,account_id,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.AuthMethodIdField,
|
||||
globals.AccountIdField,
|
||||
globals.CreatedTimeField,
|
||||
globals.UpdatedTimeField,
|
||||
},
|
||||
wantId: org1AT.PublicId,
|
||||
},
|
||||
{
|
||||
name: "org role grant this cannot read global token",
|
||||
input: &pbs.GetAuthTokenRequest{
|
||||
Id: globalAT.PublicId,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
expectOutfields: nil,
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children can read org token",
|
||||
input: &pbs.GetAuthTokenRequest{
|
||||
Id: org1AT.PublicId,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,scope_id,scope,user_id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.AuthMethodIdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
globals.AccountIdField,
|
||||
globals.CreatedTimeField,
|
||||
globals.UpdatedTimeField,
|
||||
globals.ApproximateLastUsedTimeField,
|
||||
globals.ExpirationTimeField,
|
||||
globals.AuthorizedActionsField,
|
||||
},
|
||||
wantId: org1AT.PublicId,
|
||||
},
|
||||
{
|
||||
name: "org role grant this pinned id returns org token",
|
||||
input: &pbs.GetAuthTokenRequest{
|
||||
Id: org1AT.PublicId,
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{fmt.Sprintf("ids=%s;type=auth-token;actions=read;output_fields=id,scope_id,scope,user_id", org1AT.PublicId)},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
expectOutfields: []string{
|
||||
globals.IdField,
|
||||
globals.ScopeIdField,
|
||||
globals.ScopeField,
|
||||
globals.UserIdField,
|
||||
},
|
||||
wantId: org1AT.PublicId,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, acct := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, acct.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.GetAuthToken(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
assert.Equal(t, tc.wantId, got.GetItem().Id)
|
||||
handlers.TestAssertOutputFields(t, got.GetItem(), tc.expectOutfields)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGrants_CreateActions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
atRepoFn := func() (*authtoken.Repository, error) { return atRepo, nil }
|
||||
|
||||
s, err := at.NewService(ctx, atRepoFn, iamRepoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, _ := iam.TestScopes(t, iamRepo)
|
||||
org2, _ := iam.TestScopes(t, iamRepo)
|
||||
org3, _ := iam.TestScopes(t, iamRepo)
|
||||
pinnedId := authtoken.TestAuthToken(t, conn, kmsCache, org2.GetPublicId())
|
||||
selfToken := authtoken.TestAuthToken(t, conn, kmsCache, org3.GetPublicId())
|
||||
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
createTokenFn func() *authtoken.AuthToken
|
||||
userFunc func() (*iam.User, a.Account)
|
||||
ctxAuthToken func(ctx context.Context, user *iam.User, acctId string) (*authtoken.AuthToken, error)
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "global role grant this deletes global token",
|
||||
createTokenFn: func() *authtoken.AuthToken {
|
||||
return authtoken.TestAuthToken(t, conn, kmsCache, globals.GlobalPrefix)
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=delete"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
ctxAuthToken: func(ctx context.Context, user *iam.User, acctId string) (*authtoken.AuthToken, error) {
|
||||
return atRepo.CreateAuthToken(ctx, user, acctId)
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children can delete org tokens",
|
||||
createTokenFn: func() *authtoken.AuthToken {
|
||||
return authtoken.TestAuthToken(t, conn, kmsCache, org1.GetPublicId())
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=auth-token;actions=delete"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
ctxAuthToken: func(ctx context.Context, user *iam.User, acctId string) (*authtoken.AuthToken, error) {
|
||||
return atRepo.CreateAuthToken(ctx, user, acctId)
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "global role grant this and descendants can delete org tokens",
|
||||
createTokenFn: func() *authtoken.AuthToken {
|
||||
return authtoken.TestAuthToken(t, conn, kmsCache, org2.GetPublicId())
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
ctxAuthToken: func(ctx context.Context, user *iam.User, acctId string) (*authtoken.AuthToken, error) {
|
||||
return atRepo.CreateAuthToken(ctx, user, acctId)
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "global role grant this can delete pinned org token",
|
||||
createTokenFn: func() *authtoken.AuthToken {
|
||||
return pinnedId
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{fmt.Sprintf("ids=%s;type=auth-token;actions=*", pinnedId.PublicId)},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
ctxAuthToken: func(ctx context.Context, user *iam.User, acctId string) (*authtoken.AuthToken, error) {
|
||||
return atRepo.CreateAuthToken(ctx, user, acctId)
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "org role grant this can delete self",
|
||||
createTokenFn: func() *authtoken.AuthToken {
|
||||
return selfToken
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=delete:self"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
ctxAuthToken: func(ctx context.Context, user *iam.User, acctId string) (*authtoken.AuthToken, error) {
|
||||
return selfToken, nil
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "org role grant this can't delete global",
|
||||
createTokenFn: func() *authtoken.AuthToken {
|
||||
return authtoken.TestAuthToken(t, conn, kmsCache, globals.GlobalPrefix)
|
||||
},
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{org2.PublicId},
|
||||
},
|
||||
}),
|
||||
ctxAuthToken: func(ctx context.Context, user *iam.User, acctId string) (*authtoken.AuthToken, error) {
|
||||
return atRepo.CreateAuthToken(ctx, user, acctId)
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, acct := tc.userFunc()
|
||||
inputToken := tc.createTokenFn()
|
||||
input := &pbs.DeleteAuthTokenRequest{
|
||||
Id: inputToken.PublicId,
|
||||
}
|
||||
authToken, err := tc.ctxAuthToken(ctx, user, acct.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
// authToken, err := atRepo.CreateAuthToken(ctx, user, acct.GetPublicId())
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, authToken, iamRepo)
|
||||
_, finalErr := s.DeleteAuthToken(fullGrantAuthCtx, input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -63,7 +63,7 @@ func NewService(
|
||||
func (s Service) MonthlyActiveUsers(ctx context.Context, req *pbs.MonthlyActiveUsersRequest) (*pbs.MonthlyActiveUsersResponse, error) {
|
||||
const op = "billing.(Service).MonthlyActiveUsers"
|
||||
|
||||
authResults := s.authResult(ctx, action.MonthlyActiveUsers)
|
||||
authResults := s.authResult(ctx, action.MonthlyActiveUsers, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, errors.Wrap(ctx, authResults.Error, op)
|
||||
}
|
||||
@@ -115,11 +115,11 @@ func (s Service) MonthlyActiveUsers(ctx context.Context, req *pbs.MonthlyActiveU
|
||||
return &pbs.MonthlyActiveUsersResponse{Items: activeUsers}, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
opts := []auth.Option{
|
||||
auth.WithType(resource.Billing),
|
||||
auth.WithAction(a),
|
||||
auth.WithScopeId(scope.Global.String()),
|
||||
auth.WithRecursive(isRecursive),
|
||||
}
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Billing, opts...)
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package billing_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/ldap"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/billing"
|
||||
cauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
billingservice "github.com/hashicorp/boundary/internal/daemon/controller/handlers/billing"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestGrants_MonthlyActiveUsers tests read actions to assert that grants are being applied properly
|
||||
//
|
||||
// Role - which scope the role is created in
|
||||
// - global level
|
||||
func TestGrants_MonthlyActiveUsers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrap := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrap))
|
||||
|
||||
repoFn := func() (*billing.Repository, error) {
|
||||
return billing.TestRepo(t, conn), nil
|
||||
}
|
||||
billing.TestGenerateActiveUsers(t, conn)
|
||||
|
||||
today := time.Now().UTC()
|
||||
monthStart := time.Date(today.Year(), today.Month(), 1, 0, 0, 0, 0, time.UTC)
|
||||
threeMonthsAgo := time.Date(monthStart.AddDate(0, -3, 0).Year(), monthStart.AddDate(0, -3, 0).Month(), 1, 0, 0, 0, 0, time.UTC).Format("2006-01")
|
||||
oneMonthAgo := time.Date(monthStart.AddDate(0, -1, 0).Year(), monthStart.AddDate(0, -1, 0).Month(), 1, 0, 0, 0, 0, time.UTC).Format("2006-01")
|
||||
|
||||
s, err := billingservice.NewService(ctx, repoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("MonthlyActiveUsers", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.MonthlyActiveUsersRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantErr error
|
||||
expectedItemsReturned int
|
||||
}{
|
||||
{
|
||||
name: "global role with wildcard returns monthly active users",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
expectedItemsReturned: 2,
|
||||
},
|
||||
{
|
||||
name: "global role + direct association with type billing returns monthly active users",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=billing;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
expectedItemsReturned: 2,
|
||||
},
|
||||
{
|
||||
name: "global role + direct association with multiple roles returns monthly active users",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=group;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=billing;actions=no-op"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=billing;actions=monthly-active-users"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
expectedItemsReturned: 2,
|
||||
},
|
||||
{
|
||||
name: "global role + group association with type billing returns monthly active users",
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=billing;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
expectedItemsReturned: 2,
|
||||
},
|
||||
{
|
||||
name: "global role + managed group association with type billing returns monthly active users",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=billing;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
expectedItemsReturned: 2,
|
||||
},
|
||||
{
|
||||
name: "global role with type billing and actions monthly-active-users returns monthly active users",
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=billing;actions=monthly-active-users"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
expectedItemsReturned: 2,
|
||||
},
|
||||
{
|
||||
name: "global role with non-applicable type returns an error",
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=group;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "global role with billing type and non-applicable actions returns an error",
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=billing;actions=no-op"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "global role grant this with a non-applicable type returns a permission error",
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=group;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "global role grant descendant returns no monthly active users",
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=billing;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "global role grant descendant returns no monthly active users",
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=billing;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
input: &pbs.MonthlyActiveUsersRequest{
|
||||
StartTime: threeMonthsAgo,
|
||||
EndTime: oneMonthAgo,
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, accountID := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, accountID.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := cauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.MonthlyActiveUsers(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
require.Len(t, got.Items, tc.expectedItemsReturned)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
+8
-9
@@ -151,7 +151,7 @@ func (s Service) ListCredentialLibraries(ctx context.Context, req *pbs.ListCrede
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -281,7 +281,7 @@ func (s Service) GetCredentialLibrary(ctx context.Context, req *pbs.GetCredentia
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -319,7 +319,7 @@ func (s Service) CreateCredentialLibrary(ctx context.Context, req *pbs.CreateCre
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetCredentialStoreId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetCredentialStoreId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -382,7 +382,7 @@ func (s Service) UpdateCredentialLibrary(ctx context.Context, req *pbs.UpdateCre
|
||||
if err := validateUpdateRequest(req, currentCredentialType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -418,7 +418,7 @@ func (s Service) DeleteCredentialLibrary(ctx context.Context, req *pbs.DeleteCre
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -598,7 +598,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
const op = "credentiallibraries.(Service).authResult"
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
@@ -608,7 +608,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.CredentialLibrary), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -667,8 +667,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
res.Error = errors.New(ctx, errors.InvalidParameter, op, "unrecognized credential store subtype from id")
|
||||
return res
|
||||
}
|
||||
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.CredentialLibrary, opts...)
|
||||
}
|
||||
|
||||
func newOutputOpts(
|
||||
|
||||
@@ -8,9 +8,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
@@ -31,6 +33,8 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
}
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
sche := scheduler.TestScheduler(t, conn, wrap)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(ctx, rw, rw, kmsCache, sche)
|
||||
@@ -45,24 +49,24 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListCredentialLibrariesRequest
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
name string
|
||||
input *pbs.ListCredentialLibrariesRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
}{
|
||||
{
|
||||
name: "global role grant descendant returns all credentials library",
|
||||
input: &pbs.ListCredentialLibrariesRequest{
|
||||
CredentialStoreId: proj1CredStore[0].GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=credential-library;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=credential-library;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{proj1Libs[0].GetPublicId(), proj1Libs[1].GetPublicId(), proj1Libs[2].GetPublicId()},
|
||||
},
|
||||
@@ -71,13 +75,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
input: &pbs.ListCredentialLibrariesRequest{
|
||||
CredentialStoreId: proj1CredStore[0].GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org.GetPublicId(),
|
||||
GrantStrings: []string{"ids=*;type=credential-library;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: org.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=credential-library;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{proj1Libs[0].GetPublicId(), proj1Libs[1].GetPublicId(), proj1Libs[2].GetPublicId()},
|
||||
},
|
||||
@@ -85,8 +89,10 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.ListCredentialLibraries(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
|
||||
@@ -130,7 +130,7 @@ func (s Service) ListCredentials(ctx context.Context, req *pbs.ListCredentialsRe
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -260,7 +260,7 @@ func (s Service) GetCredential(ctx context.Context, req *pbs.GetCredentialReques
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (s Service) CreateCredential(ctx context.Context, req *pbs.CreateCredential
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetCredentialStoreId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetCredentialStoreId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -345,7 +345,7 @@ func (s Service) UpdateCredential(ctx context.Context, req *pbs.UpdateCredential
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -381,7 +381,7 @@ func (s Service) DeleteCredential(ctx context.Context, req *pbs.DeleteCredential
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -584,7 +584,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
const op = "credentials.(Service).authResult"
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
@@ -594,7 +594,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Credential), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -629,8 +629,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return res
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(cs.GetProjectId()))
|
||||
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Credential, opts...)
|
||||
}
|
||||
|
||||
func newOutputOpts(
|
||||
|
||||
@@ -6,20 +6,43 @@ package credentials_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
const (
|
||||
// UsernamePasswordAttributesField is the field name for the UsernamePassword subtype of the `Credential.Attrs` attributes field
|
||||
//
|
||||
// When the "attributes" field is specified as an output_field and can be one of many sub-types, the expected output field must be the corresponding sub-type and not `globals.AttributesField`
|
||||
UsernamePasswordAttributesField = "username_password_attributes"
|
||||
)
|
||||
|
||||
// expectedOutput consolidates common output fields for the test cases
|
||||
type expectedOutput struct {
|
||||
err error
|
||||
outputFields []string
|
||||
}
|
||||
|
||||
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
|
||||
//
|
||||
// Role - which scope the role is created in
|
||||
@@ -39,9 +62,8 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
rw := db.New(conn)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
@@ -51,88 +73,803 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
s, err := credentials.NewService(ctx, iamRepoFn, staticRepoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
credStore := static.TestCredentialStore(t, conn, wrap, proj.GetPublicId())
|
||||
var wantCreds []string
|
||||
for i := 0; i < 10; i++ {
|
||||
var credIds []string
|
||||
for i := range 5 {
|
||||
user := fmt.Sprintf("user-%d", i)
|
||||
pass := fmt.Sprintf("pass-%d", i)
|
||||
c := static.TestUsernamePasswordCredential(t, conn, wrap, user, pass, credStore.GetPublicId(), proj.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
wantCreds = append(wantCreds, c.GetPublicId())
|
||||
c := static.TestUsernamePasswordCredential(t, conn, wrap, user, pass, credStore.GetPublicId(), proj.GetPublicId(), static.WithName(fmt.Sprintf("cred-%d", i)), static.WithDescription(fmt.Sprintf("desc-%d", i)))
|
||||
credIds = append(credIds, c.GetPublicId())
|
||||
}
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListCredentialsRequest
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
wantOutfields map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "global role grant this returns all created credentials",
|
||||
input: &pbs.ListCredentialsRequest{
|
||||
CredentialStoreId: credStore.GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
name: "global role grant this returns 403 because credentials live on projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=credential;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
wantIDs: wantCreds,
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "org role grant this returns all created credentials",
|
||||
input: &pbs.ListCredentialsRequest{
|
||||
CredentialStoreId: credStore.GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
name: "org role grant this returns 403 because credentials live on projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=credential;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: org.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
wantIDs: wantCreds,
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "project role grant this returns all created credentials",
|
||||
input: &pbs.ListCredentialsRequest{
|
||||
CredentialStoreId: credStore.GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
name: "project role grant this returns all credentials on the project",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: proj.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=credential;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
credIds[0]: {globals.IdField},
|
||||
credIds[1]: {globals.IdField},
|
||||
credIds[2]: {globals.IdField},
|
||||
credIds[3]: {globals.IdField},
|
||||
credIds[4]: {globals.IdField},
|
||||
},
|
||||
wantErr: nil,
|
||||
wantIDs: wantCreds,
|
||||
},
|
||||
{
|
||||
name: "global role grant this & children returns 403 because credentials live on projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "org role grant children returns all credentials on child projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,credential_store_id,scope,name,description,created_time,updated_time,version,type,authorized_actions,attributes"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
credIds[0]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[1]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[2]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[3]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[4]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & descendants returns all credentials on descendant projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,scope,type,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
credIds[0]: {globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
credIds[1]: {globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
credIds[2]: {globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
credIds[3]: {globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
credIds[4]: {globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this returns all credentials on the project",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,credential_store_id,scope,name,description,created_time,updated_time,version,type,authorized_actions,attributes"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
credIds[0]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[1]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[2]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[3]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
credIds[4]: {globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant with credential-store id, list action, credential type, and descendant scope returns all credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=" + credStore.PublicId + ";type=credential;actions=list,no-op;output_fields=id,name,description,credential_store_id"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
credIds[0]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.CredentialStoreIdField},
|
||||
credIds[1]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.CredentialStoreIdField},
|
||||
credIds[2]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.CredentialStoreIdField},
|
||||
credIds[3]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.CredentialStoreIdField},
|
||||
credIds[4]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.CredentialStoreIdField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant with credential-store id, list action, credential type, and children scope returns all credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=" + credStore.PublicId + ";type=credential;actions=list,no-op;output_fields=id,name,description,version,credential_store_id"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
credIds[0]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[1]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[2]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[3]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[4]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant with credential-store id, list action, credential type, and this scope returns all credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=" + credStore.PublicId + ";type=credential;actions=list,no-op;output_fields=id,name,description,version,credential_store_id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
credIds[0]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[1]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[2]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[3]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
credIds[4]: {globals.IdField, globals.NameField, globals.DescriptionField, globals.VersionField, globals.CredentialStoreIdField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "incorrect grants can't list credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=credential;actions=read,create,update,delete"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "no grants returns 403",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, nil),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.ListCredentials(fullGrantAuthCtx, tc.input)
|
||||
// Call function to create direct/indirect relationship
|
||||
user, account := tc.userFunc()
|
||||
|
||||
// Create auth token for the user
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create auth context from the auth token
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
got, finalErr := s.ListCredentials(fullGrantAuthCtx, &pbs.ListCredentialsRequest{CredentialStoreId: credStore.GetPublicId()})
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
var gotIDs []string
|
||||
var gotIds []string
|
||||
for _, g := range got.Items {
|
||||
gotIDs = append(gotIDs, g.GetId())
|
||||
gotIds = append(gotIds, g.GetId())
|
||||
|
||||
// check if the output fields are as expected
|
||||
if tc.wantOutfields[g.Id] != nil {
|
||||
handlers.TestAssertOutputFields(t, g, tc.wantOutfields[g.Id])
|
||||
}
|
||||
}
|
||||
|
||||
wantIds := slices.Collect(maps.Keys(tc.wantOutfields))
|
||||
require.ElementsMatch(t, wantIds, gotIds)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
canGetCredential map[string]expectedOutput
|
||||
}{
|
||||
{
|
||||
name: "global role grant this returns 403",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {err: handlers.ForbiddenError()},
|
||||
credIds[1]: {err: handlers.ForbiddenError()},
|
||||
credIds[2]: {err: handlers.ForbiddenError()},
|
||||
credIds[3]: {err: handlers.ForbiddenError()},
|
||||
credIds[4]: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this returns 403",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {err: handlers.ForbiddenError()},
|
||||
credIds[1]: {err: handlers.ForbiddenError()},
|
||||
credIds[2]: {err: handlers.ForbiddenError()},
|
||||
credIds[3]: {err: handlers.ForbiddenError()},
|
||||
credIds[4]: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this returns all credentials on the project",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {outputFields: []string{globals.IdField}},
|
||||
credIds[1]: {outputFields: []string{globals.IdField}},
|
||||
credIds[2]: {outputFields: []string{globals.IdField}},
|
||||
credIds[3]: {outputFields: []string{globals.IdField}},
|
||||
credIds[4]: {outputFields: []string{globals.IdField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & children returns 403 because credentials live on projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {err: handlers.ForbiddenError()},
|
||||
credIds[1]: {err: handlers.ForbiddenError()},
|
||||
credIds[2]: {err: handlers.ForbiddenError()},
|
||||
credIds[3]: {err: handlers.ForbiddenError()},
|
||||
credIds[4]: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant children returns all credentials on child projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {outputFields: []string{globals.IdField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[1]: {outputFields: []string{globals.IdField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[2]: {outputFields: []string{globals.IdField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[3]: {outputFields: []string{globals.IdField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[4]: {outputFields: []string{globals.IdField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & descendants returns all credentials on descendant projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,scope,type,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {outputFields: []string{globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[1]: {outputFields: []string{globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[2]: {outputFields: []string{globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[3]: {outputFields: []string{globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
credIds[4]: {outputFields: []string{globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this returns all credentials on the project",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id,credential_store_id,scope,name,description,created_time,updated_time,version,type,authorized_actions,attributes"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField}},
|
||||
credIds[1]: {outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField}},
|
||||
credIds[2]: {outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField}},
|
||||
credIds[3]: {outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField}},
|
||||
credIds[4]: {outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "incorrect grants returns no credential",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=credential;actions=list,create,update,delete;output_fields=id,name,description,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {err: handlers.ForbiddenError()},
|
||||
credIds[1]: {err: handlers.ForbiddenError()},
|
||||
credIds[2]: {err: handlers.ForbiddenError()},
|
||||
credIds[3]: {err: handlers.ForbiddenError()},
|
||||
credIds[4]: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no grants returns no credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, nil),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {err: handlers.ForbiddenError()},
|
||||
credIds[1]: {err: handlers.ForbiddenError()},
|
||||
credIds[2]: {err: handlers.ForbiddenError()},
|
||||
credIds[3]: {err: handlers.ForbiddenError()},
|
||||
credIds[4]: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant with credential id, get action, and this scope returns the id'd credential",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=" + credIds[0] + ";actions=read;output_fields=id,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {outputFields: []string{globals.IdField, globals.AuthorizedActionsField}},
|
||||
credIds[1]: {err: handlers.ForbiddenError()},
|
||||
credIds[2]: {err: handlers.ForbiddenError()},
|
||||
credIds[3]: {err: handlers.ForbiddenError()},
|
||||
credIds[4]: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant with credential id, get action, and this & children scope returns the id'd credential",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=" + credIds[4] + ";actions=read;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
canGetCredential: map[string]expectedOutput{
|
||||
credIds[0]: {err: handlers.ForbiddenError()},
|
||||
credIds[1]: {err: handlers.ForbiddenError()},
|
||||
credIds[2]: {err: handlers.ForbiddenError()},
|
||||
credIds[3]: {err: handlers.ForbiddenError()},
|
||||
credIds[4]: {outputFields: []string{globals.IdField}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
for id, expected := range tc.canGetCredential {
|
||||
input := &pbs.GetCredentialRequest{Id: id}
|
||||
got, err := s.GetCredential(fullGrantAuthCtx, input)
|
||||
if expected.err != nil {
|
||||
require.ErrorIs(t, err, expected.err)
|
||||
continue
|
||||
}
|
||||
// check if the output fields are as expected
|
||||
if tc.canGetCredential[id].outputFields != nil {
|
||||
handlers.TestAssertOutputFields(t, got.Item, tc.canGetCredential[id].outputFields)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.ElementsMatch(t, tc.wantIDs, gotIDs)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGrants_WriteActions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
rw := db.New(conn)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
staticRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(ctx, rw, rw, kmsCache)
|
||||
}
|
||||
s, err := credentials.NewService(ctx, iamRepoFn, staticRepoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
credStore := static.TestCredentialStore(t, conn, wrap, proj.GetPublicId())
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
expected expectedOutput
|
||||
}{
|
||||
{
|
||||
name: "global role grant this can't create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{err: handlers.ForbiddenError()},
|
||||
},
|
||||
{
|
||||
name: "org role grant this can't create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{err: handlers.ForbiddenError()},
|
||||
},
|
||||
{
|
||||
name: "project role grant this can create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField}},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & descendants can create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=credential;actions=create,read,update;output_fields=id,credential_store_id,scope,name,description,created_time,updated_time,version,type,authorized_actions,attributes"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField}},
|
||||
},
|
||||
{
|
||||
name: "org role grant this & children can create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=credential;actions=create;output_fields=id,scope,type,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
},
|
||||
{
|
||||
name: "global role grant pinned to credential-store id, credential type, and descendant scope can create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=" + credStore.PublicId + ";type=credential;actions=create;output_fields=id,credential_store_id,name,scope,description,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.NameField, globals.ScopeField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
},
|
||||
{
|
||||
name: "global role grant pinned to credential-store id, credential type, and children scope cannot create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=" + credStore.PublicId + ";type=credential;actions=create;output_fields=id,credential_store_id,name,scope,description,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{err: handlers.ForbiddenError()},
|
||||
},
|
||||
{
|
||||
name: "org role grant pinned to credential-store id, credential type, and children scope can create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=" + credStore.PublicId + ";type=credential;actions=create;output_fields=id,credential_store_id,name,scope,description,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.NameField, globals.ScopeField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
},
|
||||
{
|
||||
name: "project role grant pinned to credential-store id, credential type, and this scope can create credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=" + credStore.PublicId + ";type=credential;actions=create;output_fields=id,credential_store_id,name,scope,description,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.NameField, globals.ScopeField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
got, err := s.CreateCredential(fullGrantAuthCtx, &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: credStore.GetPublicId(),
|
||||
Name: &wrappers.StringValue{Value: "test credential - " + tc.name},
|
||||
Description: &wrappers.StringValue{Value: "test desc"},
|
||||
Type: credential.UsernamePasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("static-username"),
|
||||
Password: wrapperspb.String("static-password"),
|
||||
},
|
||||
},
|
||||
}})
|
||||
if tc.expected.err != nil {
|
||||
require.ErrorIs(t, err, tc.expected.err)
|
||||
return
|
||||
}
|
||||
// check if the output fields are as expected
|
||||
require.NoError(t, err)
|
||||
handlers.TestAssertOutputFields(t, got.Item, tc.expected.outputFields)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
expected expectedOutput
|
||||
}{
|
||||
{
|
||||
name: "global role grant this can't update credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{err: handlers.ForbiddenError()},
|
||||
},
|
||||
{
|
||||
name: "org role grant this can't update credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{err: handlers.ForbiddenError()},
|
||||
},
|
||||
{
|
||||
name: "project role grant this can update credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField}},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & descendants can update credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=credential;actions=update;output_fields=id,credential_store_id,scope,name,description,created_time,updated_time,version,type,authorized_actions,attributes"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField, globals.CredentialStoreIdField, globals.ScopeField, globals.NameField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, UsernamePasswordAttributesField}},
|
||||
},
|
||||
{
|
||||
name: "org role grant this & children can update credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=credential;actions=update;output_fields=id,scope,type,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{outputFields: []string{globals.IdField, globals.ScopeField, globals.TypeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
},
|
||||
{
|
||||
name: "incorrect grants can't update credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=credential;actions=list,create,delete;output_fields=id,name,description,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: expectedOutput{err: handlers.ForbiddenError()},
|
||||
},
|
||||
{
|
||||
name: "no grants can't update credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, nil),
|
||||
expected: expectedOutput{err: handlers.ForbiddenError()},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
credStore := static.TestCredentialStore(t, conn, wrap, proj.GetPublicId())
|
||||
cred := static.TestUsernamePasswordCredential(t, conn, wrap, "user", "pass", credStore.GetPublicId(), proj.GetPublicId(), static.WithName("cred"), static.WithDescription("desc"))
|
||||
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
got, err := s.UpdateCredential(fullGrantAuthCtx, &pbs.UpdateCredentialRequest{
|
||||
Id: cred.PublicId,
|
||||
Item: &pb.Credential{
|
||||
Name: &wrappers.StringValue{Value: "updated credential name (" + tc.name + ")"},
|
||||
Description: &wrappers.StringValue{Value: "test desc"},
|
||||
Version: 1,
|
||||
},
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name", "description"}},
|
||||
})
|
||||
if tc.expected.err != nil {
|
||||
require.ErrorIs(t, err, tc.expected.err)
|
||||
return
|
||||
}
|
||||
// check if the output fields are as expected
|
||||
require.NoError(t, err)
|
||||
handlers.TestAssertOutputFields(t, got.Item, tc.expected.outputFields)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "global role grant this can't delete credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "org role grant this can't delete credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "project role grant this can delete credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "global role grant this & descendants can delete credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=credential;actions=delete"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "org role grant this & children can delete credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=credential;actions=delete"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "incorrect grants can't delete credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=credential;actions=list,create,update"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "no grants can't delete credentials",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, nil),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
credStore := static.TestCredentialStore(t, conn, wrap, proj.GetPublicId())
|
||||
cred := static.TestUsernamePasswordCredential(t, conn, wrap, "user", "pass", credStore.GetPublicId(), proj.GetPublicId(), static.WithName("cred"), static.WithDescription("desc"))
|
||||
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
_, err = s.DeleteCredential(fullGrantAuthCtx, &pbs.DeleteCredentialRequest{Id: cred.PublicId})
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -73,6 +73,12 @@ var (
|
||||
staticCollectionTypeMap = map[resource.Type]action.ActionSet{
|
||||
resource.Credential: credentials.CollectionActions,
|
||||
}
|
||||
|
||||
additionalResourceGrants = []resource.Type{
|
||||
resource.Credential,
|
||||
resource.CredentialLibrary,
|
||||
}
|
||||
|
||||
validateVaultWorkerFilterFn = vaultWorkerFilterUnsupported
|
||||
vaultWorkerFilterToProto = false
|
||||
)
|
||||
@@ -149,7 +155,7 @@ func (s Service) ListCredentialStores(ctx context.Context, req *pbs.ListCredenti
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -301,7 +307,7 @@ func (s Service) GetCredentialStore(ctx context.Context, req *pbs.GetCredentialS
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -324,7 +330,7 @@ func (s Service) GetCredentialStore(ctx context.Context, req *pbs.GetCredentialS
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, authResults.Scope, cs.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -346,7 +352,7 @@ func (s Service) CreateCredentialStore(ctx context.Context, req *pbs.CreateCrede
|
||||
if err := validateCreateRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -369,7 +375,7 @@ func (s Service) CreateCredentialStore(ctx context.Context, req *pbs.CreateCrede
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, authResults.Scope, cs.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -394,7 +400,7 @@ func (s Service) UpdateCredentialStore(ctx context.Context, req *pbs.UpdateCrede
|
||||
if err := validateUpdateRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -417,7 +423,7 @@ func (s Service) UpdateCredentialStore(ctx context.Context, req *pbs.UpdateCrede
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, authResults.Scope, cs.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -437,7 +443,7 @@ func (s Service) DeleteCredentialStore(ctx context.Context, req *pbs.DeleteCrede
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -605,7 +611,7 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
iamRepo, err := s.iamRepoFn()
|
||||
if err != nil {
|
||||
@@ -624,7 +630,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.CredentialStore), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive), auth.WithFetchAdditionalResourceGrants(additionalResourceGrants...)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -667,7 +673,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.CredentialStore, opts...)
|
||||
}
|
||||
|
||||
func newOutputOpts(
|
||||
@@ -697,7 +703,7 @@ func newOutputOpts(
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, item.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, authzScopes[item.GetProjectId()], item.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -1000,15 +1006,15 @@ func validateListRequest(ctx context.Context, req *pbs.ListCredentialStoresReque
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateAuthorizedCollectionActions(ctx context.Context, authResults auth.VerifyResults, id string) (map[string]*structpb.ListValue, error) {
|
||||
func calculateAuthorizedCollectionActions(ctx context.Context, authResults auth.VerifyResults, itemScopeInfo *scopes.ScopeInfo, itemId string) (map[string]*structpb.ListValue, error) {
|
||||
var collectionActions map[string]*structpb.ListValue
|
||||
var err error
|
||||
switch globals.ResourceInfoFromPrefix(id).Subtype {
|
||||
switch globals.ResourceInfoFromPrefix(itemId).Subtype {
|
||||
case vault.Subtype:
|
||||
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, vaultCollectionTypeMap, authResults.Scope, id)
|
||||
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, vaultCollectionTypeMap, itemScopeInfo, itemId)
|
||||
|
||||
case static.Subtype:
|
||||
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, staticCollectionTypeMap, authResults.Scope, id)
|
||||
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, staticCollectionTypeMap, itemScopeInfo, itemId)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -95,7 +95,7 @@ func (s Service) ListGroups(ctx context.Context, req *pbs.ListGroupsRequest) (*p
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -233,7 +233,7 @@ func (s Service) GetGroup(ctx context.Context, req *pbs.GetGroupRequest) (*pbs.G
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -271,7 +271,7 @@ func (s Service) CreateGroup(ctx context.Context, req *pbs.CreateGroupRequest) (
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -309,7 +309,7 @@ func (s Service) UpdateGroup(ctx context.Context, req *pbs.UpdateGroupRequest) (
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -345,7 +345,7 @@ func (s Service) DeleteGroup(ctx context.Context, req *pbs.DeleteGroupRequest) (
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -363,7 +363,7 @@ func (s Service) AddGroupMembers(ctx context.Context, req *pbs.AddGroupMembersRe
|
||||
if err := validateAddGroupMembersRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddMembers)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddMembers, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -401,7 +401,7 @@ func (s Service) SetGroupMembers(ctx context.Context, req *pbs.SetGroupMembersRe
|
||||
if err := validateSetGroupMembersRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetMembers)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetMembers, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -439,7 +439,7 @@ func (s Service) RemoveGroupMembers(ctx context.Context, req *pbs.RemoveGroupMem
|
||||
if err := validateRemoveGroupMembersRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveMembers)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveMembers, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -625,7 +625,7 @@ func (s Service) removeMembersInRepo(ctx context.Context, groupId string, userId
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -634,7 +634,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Group), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -661,7 +661,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Group, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *iam.Group, members []*iam.GroupMember, opt ...handlers.Option) (*pb.Group, error) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -73,6 +73,11 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
additionalResourceGrants = []resource.Type{
|
||||
resource.HostSet,
|
||||
resource.Host,
|
||||
}
|
||||
|
||||
validateWorkerFilterFn = validateWorkerFilterUnsupported
|
||||
workerFilterToProto = false
|
||||
)
|
||||
@@ -162,7 +167,7 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -318,7 +323,7 @@ func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequ
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -375,7 +380,7 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -435,7 +440,7 @@ func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatal
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -489,7 +494,7 @@ func (s Service) DeleteHostCatalog(ctx context.Context, req *pbs.DeleteHostCatal
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -692,11 +697,11 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.HostCatalog), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -753,7 +758,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.HostCatalog, opts...)
|
||||
}
|
||||
|
||||
func toPluginInfo(plg *plugin.Plugin) *plugins.PluginInfo {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -124,7 +124,7 @@ func (s Service) ListHostSetsWithOptions(ctx context.Context, req *pbs.ListHostS
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetHostCatalogId(), action.List)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetHostCatalogId(), action.List, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -304,7 +304,7 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -346,7 +346,7 @@ func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetReques
|
||||
if err := validateCreateRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetHostCatalogId(), action.Create)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetHostCatalogId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -391,7 +391,7 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques
|
||||
if err := validateUpdateRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cat, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update)
|
||||
cat, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -431,7 +431,7 @@ func (s Service) DeleteHostSet(ctx context.Context, req *pbs.DeleteHostSetReques
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -449,7 +449,7 @@ func (s Service) AddHostSetHosts(ctx context.Context, req *pbs.AddHostSetHostsRe
|
||||
if err := validateAddRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.AddHosts)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.AddHosts, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -488,7 +488,7 @@ func (s Service) SetHostSetHosts(ctx context.Context, req *pbs.SetHostSetHostsRe
|
||||
if err := validateSetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.SetHosts)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.SetHosts, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -526,7 +526,7 @@ func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetH
|
||||
if err := validateRemoveRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.RemoveHosts)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.RemoveHosts, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -854,7 +854,7 @@ func (s Service) removeInRepo(ctx context.Context, projectId, setId string, host
|
||||
return out, hl, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (host.Catalog, auth.VerifyResults) {
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type, isRecursive bool) (host.Catalog, auth.VerifyResults) {
|
||||
res := auth.VerifyResults{}
|
||||
|
||||
staticRepo, err := s.staticRepoFn()
|
||||
@@ -869,7 +869,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.HostSet), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -929,7 +929,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
cat = pc
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(cat.GetProjectId()), auth.WithPin(parentId))
|
||||
return cat, auth.Verify(ctx, opts...)
|
||||
return cat, auth.Verify(ctx, resource.HostSet, opts...)
|
||||
}
|
||||
|
||||
func toPluginInfo(plg *plugin.Plugin) *plugins.PluginInfo {
|
||||
|
||||
@@ -8,8 +8,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/hosts"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
@@ -52,6 +54,8 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
}
|
||||
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
@@ -68,24 +72,24 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListHostsRequest
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
name string
|
||||
input *pbs.ListHostsRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
}{
|
||||
{
|
||||
name: "global role grant this returns all created hosts",
|
||||
input: &pbs.ListHostsRequest{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=host;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=host;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: wantHs,
|
||||
},
|
||||
@@ -94,13 +98,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
input: &pbs.ListHostsRequest{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=host;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: org.PublicId,
|
||||
Grants: []string{"ids=*;type=host;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: wantHs,
|
||||
},
|
||||
@@ -109,13 +113,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
input: &pbs.ListHostsRequest{
|
||||
HostCatalogId: hc.GetPublicId(),
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: proj.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=host;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: proj.PublicId,
|
||||
Grants: []string{"ids=*;type=host;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: wantHs,
|
||||
},
|
||||
@@ -123,8 +127,10 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.ListHosts(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
|
||||
@@ -105,7 +105,7 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetHostCatalogId(), action.List)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetHostCatalogId(), action.List, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -285,7 +285,7 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func (s Service) CreateHost(ctx context.Context, req *pbs.CreateHostRequest) (*p
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetHostCatalogId(), action.Create)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetHostCatalogId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -369,7 +369,7 @@ func (s Service) UpdateHost(ctx context.Context, req *pbs.UpdateHostRequest) (*p
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cat, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update)
|
||||
cat, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -407,7 +407,7 @@ func (s Service) DeleteHost(ctx context.Context, req *pbs.DeleteHostRequest) (*p
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -533,7 +533,7 @@ func (s Service) deleteFromRepo(ctx context.Context, projectId, id string) (bool
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (host.Catalog, auth.VerifyResults) {
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type, isRecursive bool) (host.Catalog, auth.VerifyResults) {
|
||||
res := auth.VerifyResults{}
|
||||
staticRepo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
@@ -547,7 +547,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Host), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -605,7 +605,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
cat = plcat
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(cat.GetProjectId()), auth.WithPin(parentId))
|
||||
return cat, auth.Verify(ctx, opts...)
|
||||
return cat, auth.Verify(ctx, resource.Host, opts...)
|
||||
}
|
||||
|
||||
func toPluginInfo(plg *plugin.Plugin) *plugins.PluginInfo {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -123,7 +123,7 @@ func (s Service) ListManagedGroups(ctx context.Context, req *pbs.ListManagedGrou
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetAuthMethodId(), action.List)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetAuthMethodId(), action.List, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -302,7 +302,7 @@ func (s Service) GetManagedGroup(ctx context.Context, req *pbs.GetManagedGroupRe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -344,7 +344,7 @@ func (s Service) CreateManagedGroup(ctx context.Context, req *pbs.CreateManagedG
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetAuthMethodId(), action.Create)
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetAuthMethodId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -383,7 +383,7 @@ func (s Service) UpdateManagedGroup(ctx context.Context, req *pbs.UpdateManagedG
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update)
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -419,7 +419,7 @@ func (s Service) DeleteManagedGroup(ctx context.Context, req *pbs.DeleteManagedG
|
||||
if err := validateDeleteRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete)
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -708,7 +708,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (auth.AuthMethod, requestauth.VerifyResults) {
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type, isRecursive bool) (auth.AuthMethod, requestauth.VerifyResults) {
|
||||
const op = "managed_groups.(Service)."
|
||||
res := requestauth.VerifyResults{}
|
||||
oidcRepo, err := s.oidcRepoFn()
|
||||
@@ -723,7 +723,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []requestauth.Option{requestauth.WithType(resource.ManagedGroup), requestauth.WithAction(a)}
|
||||
opts := []requestauth.Option{requestauth.WithAction(a), requestauth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -789,7 +789,7 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
return nil, res
|
||||
}
|
||||
opts = append(opts, requestauth.WithPin(parentId))
|
||||
return authMeth, requestauth.Verify(ctx, opts...)
|
||||
return authMeth, requestauth.Verify(ctx, resource.ManagedGroup, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in auth.ManagedGroup, opt ...handlers.Option) (*pb.ManagedGroup, error) {
|
||||
|
||||
@@ -5,37 +5,39 @@ package roles_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/roles"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
|
||||
//
|
||||
// Role - which scope the role is created in
|
||||
// - global level
|
||||
// - org level
|
||||
// - project level
|
||||
// Grant - what IAM grant scope is set for the permission
|
||||
// - global: descendant
|
||||
// - org: children
|
||||
// - project
|
||||
// Scopes [resource]:
|
||||
// - global [globalRole]
|
||||
// - org1 [org1Role]
|
||||
// - proj1 [proj1Role]
|
||||
// - org2 [org2Role]
|
||||
// - proj2 [proj2Role]
|
||||
// - proj3 [proj3Role]
|
||||
type expect struct {
|
||||
wantErr error
|
||||
wantFields []string
|
||||
}
|
||||
|
||||
func TestGrants_ReadActions(t *testing.T) {
|
||||
const (
|
||||
// Set role description to this value for every role that should be included in the result set.
|
||||
// This is used to work around a non-deterministic behavior from roles being created as a part of
|
||||
// the test setup.
|
||||
// This also means that `description` must also be included in the output_fields
|
||||
roleDescription = "test role"
|
||||
)
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
@@ -46,81 +48,114 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
s, err := roles.NewService(ctx, repoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, _ := iam.TestScopes(t, iamRepo)
|
||||
org2, proj2 := iam.TestScopes(t, iamRepo)
|
||||
proj3 := iam.TestProject(t, iamRepo, org2.PublicId)
|
||||
|
||||
var defaultOrg1Roles []string
|
||||
org1Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, org1.GetPublicId()), &pbs.ListRolesRequest{
|
||||
ScopeId: org1.GetPublicId(),
|
||||
})
|
||||
rw := db.New(conn)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
for _, r := range org1Roles.Items {
|
||||
defaultOrg1Roles = append(defaultOrg1Roles, r.GetId())
|
||||
}
|
||||
|
||||
org2Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, org2.GetPublicId()), &pbs.ListRolesRequest{
|
||||
ScopeId: org2.GetPublicId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
var defaultOrg2Roles []string
|
||||
for _, r := range org2Roles.Items {
|
||||
defaultOrg2Roles = append(defaultOrg2Roles, r.GetId())
|
||||
}
|
||||
org1, noRoleProj := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj3 := iam.TestProject(t, iamRepo, org2.PublicId, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
proj2Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, proj2.GetPublicId()), &pbs.ListRolesRequest{
|
||||
ScopeId: proj2.GetPublicId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
var defaultProj2Roles []string
|
||||
for _, r := range proj2Roles.Items {
|
||||
defaultProj2Roles = append(defaultProj2Roles, r.GetId())
|
||||
}
|
||||
|
||||
proj3Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, proj3.GetPublicId()), &pbs.ListRolesRequest{
|
||||
ScopeId: proj3.GetPublicId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
var defaultProj3Roles []string
|
||||
for _, r := range proj3Roles.Items {
|
||||
defaultProj3Roles = append(defaultProj3Roles, r.GetId())
|
||||
}
|
||||
|
||||
org1Role := iam.TestRole(t, conn, org1.GetPublicId(), iam.WithDescription("org1"), iam.WithName("org1"))
|
||||
org2Role := iam.TestRole(t, conn, org2.GetPublicId(), iam.WithDescription("org2"), iam.WithName("org2"))
|
||||
|
||||
proj2Role := iam.TestRole(t, conn, proj2.GetPublicId(), iam.WithDescription("proj2"), iam.WithName("proj2"))
|
||||
proj3Role := iam.TestRole(t, conn, proj3.GetPublicId(), iam.WithDescription("proj3"), iam.WithName("proj3"))
|
||||
globalRole := iam.TestRole(t, conn, globals.GlobalPrefix, iam.WithDescription(roleDescription), iam.WithName("glob"))
|
||||
iam.TestRoleGrant(t, conn, globalRole.PublicId, "ids=*;type=*;actions=*")
|
||||
org1Role := iam.TestRole(t, conn, org1.GetPublicId(), iam.WithDescription(roleDescription), iam.WithName("org1"))
|
||||
iam.TestRoleGrant(t, conn, org1Role.PublicId, "ids=*;type=*;actions=*")
|
||||
org2Role := iam.TestRole(t, conn, org2.GetPublicId(), iam.WithDescription(roleDescription), iam.WithName("org2"))
|
||||
iam.TestRoleGrant(t, conn, org2Role.PublicId, "ids=*;type=*;actions=*")
|
||||
proj2Role := iam.TestRole(t, conn, proj2.GetPublicId(), iam.WithDescription(roleDescription), iam.WithName("proj2"))
|
||||
iam.TestRoleGrant(t, conn, proj2Role.PublicId, "ids=*;type=*;actions=*")
|
||||
proj3Role := iam.TestRole(t, conn, proj3.GetPublicId(), iam.WithDescription(roleDescription), iam.WithName("proj3"))
|
||||
iam.TestRoleGrant(t, conn, proj3Role.PublicId, "ids=*;type=*;actions=*")
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListRolesRequest
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
wantErr error
|
||||
addRolesAtThisScope bool
|
||||
wantIDs []string
|
||||
name string
|
||||
input *pbs.ListRolesRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantErr error
|
||||
idOutputFieldsMap map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "global role grant this and descendants returns global org and project roles",
|
||||
input: &pbs.ListRolesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=list,read;output_fields=id,name,description,scope_id,authorized_actions,principal_ids"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
globalRole.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
org1Role.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
org2Role.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
proj2Role.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
proj3Role.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project grant scope list recursive returns no result and no error",
|
||||
input: &pbs.ListRolesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: noRoleProj.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=list,read;output_fields=id,name,description,scope_id,authorized_actions,principal_ids"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
idOutputFieldsMap: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "project grant scope list non-recursive returns error",
|
||||
input: &pbs.ListRolesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: noRoleProj.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=list,read;output_fields=id,name,description,scope_id,authorized_actions,principal_ids"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
idOutputFieldsMap: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children returns global and org roles",
|
||||
input: &pbs.ListRolesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=role;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=list,read;output_fields=id,name,description,scope_id,authorized_actions,principal_ids"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
globalRole.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
org1Role.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
org2Role.PublicId: {globals.IdField, globals.NameField, globals.DescriptionField, globals.ScopeIdField, globals.AuthorizedActionsField},
|
||||
},
|
||||
addRolesAtThisScope: true,
|
||||
wantErr: nil,
|
||||
wantIDs: append(append([]string{
|
||||
org1Role.PublicId,
|
||||
org2Role.PublicId,
|
||||
}, defaultOrg1Roles...), defaultOrg2Roles...),
|
||||
},
|
||||
{
|
||||
name: "no role recursive list returns error",
|
||||
input: &pbs.ListRolesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "org role grant this and children returns org and project roles",
|
||||
@@ -128,54 +163,563 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
ScopeId: org2.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org2.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=role;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=list,read;output_fields=id,description,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
org2Role.PublicId: {globals.IdField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
proj2Role.PublicId: {globals.IdField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
proj3Role.PublicId: {globals.IdField, globals.DescriptionField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this and individual project returns org and granted project roles",
|
||||
input: &pbs.ListRolesRequest{
|
||||
ScopeId: org2.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=list,read;output_fields=id,description,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=list,read;output_fields=id,description,created_time"},
|
||||
GrantScopes: []string{proj3.PublicId},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
org2Role.PublicId: {globals.IdField, globals.DescriptionField, globals.UpdatedTimeField},
|
||||
proj3Role.PublicId: {globals.IdField, globals.DescriptionField, globals.CreatedTimeField},
|
||||
},
|
||||
addRolesAtThisScope: true,
|
||||
wantErr: nil,
|
||||
wantIDs: append(append([]string{
|
||||
proj2Role.PublicId,
|
||||
proj3Role.PublicId,
|
||||
}, defaultProj2Roles...), defaultProj3Roles...),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
// TestAuthTokenWithRoles creates a default role, so we need to add it to the expected list
|
||||
// if the grant scope contains 'this'
|
||||
// This will add the default roles to the expected list of roles
|
||||
if tc.addRolesAtThisScope {
|
||||
var rolesAtThisScope []string
|
||||
rolesAtThisScopeList, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, tc.input.ScopeId), &pbs.ListRolesRequest{
|
||||
ScopeId: tc.input.ScopeId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
for _, r := range rolesAtThisScopeList.Items {
|
||||
rolesAtThisScope = append(rolesAtThisScope, r.GetId())
|
||||
}
|
||||
tc.wantIDs = append(tc.wantIDs, rolesAtThisScope...)
|
||||
}
|
||||
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.ListRoles(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
var gotIDs []string
|
||||
for _, g := range got.Items {
|
||||
gotIDs = append(gotIDs, g.GetId())
|
||||
var filteredRoles []*pb.Role
|
||||
for _, r := range got.Items {
|
||||
// only include roles with description that indicates that the roles are created as a part of
|
||||
// the test setup
|
||||
if r.GetDescription().GetValue() != roleDescription {
|
||||
continue
|
||||
}
|
||||
filteredRoles = append(filteredRoles, r)
|
||||
}
|
||||
require.Len(t, filteredRoles, len(tc.idOutputFieldsMap))
|
||||
for _, item := range filteredRoles {
|
||||
wantFields, ok := tc.idOutputFieldsMap[item.GetId()]
|
||||
require.True(t, ok)
|
||||
handlers.TestAssertOutputFields(t, item, wantFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Read", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
inputExpectMap map[*pbs.GetRoleRequest]expect
|
||||
}{
|
||||
{
|
||||
name: "global role grant this and descendants can read all roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=read;output_fields=id,name,created_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
inputExpectMap: map[*pbs.GetRoleRequest]expect{
|
||||
{Id: globalRole.PublicId}: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
{Id: org1Role.PublicId}: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
{Id: org2Role.PublicId}: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
{Id: proj2Role.PublicId}: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
{Id: proj3Role.PublicId}: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children can read global and org roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,updated_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
inputExpectMap: map[*pbs.GetRoleRequest]expect{
|
||||
{Id: globalRole.PublicId}: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
{Id: org1Role.PublicId}: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
{Id: org2Role.PublicId}: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
{Id: proj2Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj3Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this and children can read self and projects roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,updated_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
inputExpectMap: map[*pbs.GetRoleRequest]expect{
|
||||
{Id: globalRole.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org1Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org2Role.PublicId}: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
{Id: proj2Role.PublicId}: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
{Id: proj3Role.PublicId}: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this can read self",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputExpectMap: map[*pbs.GetRoleRequest]expect{
|
||||
{Id: globalRole.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org1Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org2Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj2Role.PublicId}: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
{Id: proj3Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "individually granted scopes can read granted scopes roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{org2.PublicId, proj3.PublicId},
|
||||
},
|
||||
}),
|
||||
inputExpectMap: map[*pbs.GetRoleRequest]expect{
|
||||
{Id: globalRole.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org1Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org2Role.PublicId}: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
{Id: proj2Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj3Role.PublicId}: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple roles can read granted scopes roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{org2.PublicId, proj3.PublicId},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputExpectMap: map[*pbs.GetRoleRequest]expect{
|
||||
{Id: globalRole.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org1Role.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: org2Role.PublicId}: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
{Id: proj2Role.PublicId}: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
{Id: proj3Role.PublicId}: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
for input, expected := range tc.inputExpectMap {
|
||||
got, finalErr := s.GetRole(fullGrantAuthCtx, input)
|
||||
if expected.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, expected.wantErr)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
handlers.TestAssertOutputFields(t, got.Item, expected.wantFields)
|
||||
}
|
||||
require.ElementsMatch(t, tc.wantIDs, gotIDs)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGrants_CreateRole(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
repoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
s, err := roles.NewService(ctx, repoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, _ := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj3 := iam.TestProject(t, iamRepo, org2.PublicId, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
inputScopeExpectMap map[string]expect
|
||||
}{
|
||||
{
|
||||
name: "global role grant this and descendants can create all roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=create;output_fields=id,name,created_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
inputScopeExpectMap: map[string]expect{
|
||||
globals.GlobalPrefix: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
org1.PublicId: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
org2.PublicId: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
proj2.PublicId: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
proj3.PublicId: {wantFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children can create global and org roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,updated_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
inputScopeExpectMap: map[string]expect{
|
||||
globals.GlobalPrefix: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
org1.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
org2.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
proj2.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
proj3.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this and children can create self and projects roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,updated_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
inputScopeExpectMap: map[string]expect{
|
||||
globals.GlobalPrefix: {wantErr: handlers.ForbiddenError()},
|
||||
org1.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
proj2.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
proj3.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this can create self",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputScopeExpectMap: map[string]expect{
|
||||
globals.GlobalPrefix: {wantErr: handlers.ForbiddenError()},
|
||||
org1.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
proj2.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj3.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "individually granted scopes can create granted scopes roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{org2.PublicId, proj3.PublicId},
|
||||
},
|
||||
}),
|
||||
inputScopeExpectMap: map[string]expect{
|
||||
globals.GlobalPrefix: {wantErr: handlers.ForbiddenError()},
|
||||
org1.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj2.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
proj3.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple roles can create granted scopes roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{org2.PublicId, proj3.PublicId},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputScopeExpectMap: map[string]expect{
|
||||
globals.GlobalPrefix: {wantErr: handlers.ForbiddenError()},
|
||||
org1.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj2.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj3.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
for inputScopeId, expected := range tc.inputScopeExpectMap {
|
||||
randomId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
|
||||
got, finalErr := s.CreateRole(fullGrantAuthCtx, &pbs.CreateRoleRequest{
|
||||
Item: &pb.Role{
|
||||
ScopeId: inputScopeId,
|
||||
Name: wrapperspb.String(randomId),
|
||||
Description: wrapperspb.String(randomId),
|
||||
},
|
||||
})
|
||||
if expected.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, expected.wantErr)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
handlers.TestAssertOutputFields(t, got.Item, expected.wantFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrants_SetRoleGrants(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
repoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
s, err := roles.NewService(ctx, repoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, _ := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj3 := iam.TestProject(t, iamRepo, org2.PublicId, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
globalRole := iam.TestRole(t, conn, globals.GlobalPrefix)
|
||||
iam.TestRoleGrant(t, conn, globalRole.PublicId, "ids=*;type=*;actions=*")
|
||||
org1Role := iam.TestRole(t, conn, org1.GetPublicId())
|
||||
iam.TestRoleGrant(t, conn, org1Role.PublicId, "ids=*;type=*;actions=*")
|
||||
org2Role := iam.TestRole(t, conn, org2.GetPublicId())
|
||||
iam.TestRoleGrant(t, conn, org2Role.PublicId, "ids=*;type=*;actions=*")
|
||||
proj2Role := iam.TestRole(t, conn, proj2.GetPublicId())
|
||||
iam.TestRoleGrant(t, conn, proj2Role.PublicId, "ids=*;type=*;actions=*")
|
||||
proj3Role := iam.TestRole(t, conn, proj3.GetPublicId())
|
||||
iam.TestRoleGrant(t, conn, proj3Role.PublicId, "ids=*;type=*;actions=*")
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
inputRoleIdExpectMap map[string]expect
|
||||
}{
|
||||
{
|
||||
name: "global role grant this and descendants can set role grants in all roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=set-grants;output_fields=id,updated_time,created_time"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
inputRoleIdExpectMap: map[string]expect{
|
||||
globalRole.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.CreatedTimeField}},
|
||||
org1Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.CreatedTimeField}},
|
||||
org2Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.CreatedTimeField}},
|
||||
proj2Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.CreatedTimeField}},
|
||||
proj3Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.CreatedTimeField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children can set role grants in global and org roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,updated_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
inputRoleIdExpectMap: map[string]expect{
|
||||
globalRole.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
org1Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
org2Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
proj2Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
proj3Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this and children can set role grants in self and projects roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,updated_time,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
inputRoleIdExpectMap: map[string]expect{
|
||||
globalRole.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org1Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
proj2Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
proj3Role.PublicId: {wantFields: []string{globals.IdField, globals.UpdatedTimeField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this can set role grants in self",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputRoleIdExpectMap: map[string]expect{
|
||||
globalRole.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org1Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
proj2Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj3Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "individually granted scopes can set role grants in granted scopes roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{org2.PublicId, proj3.PublicId},
|
||||
},
|
||||
}),
|
||||
inputRoleIdExpectMap: map[string]expect{
|
||||
globalRole.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org1Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj2Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
proj3Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple roles can set role grants in granted scopes roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{org2.PublicId, proj3.PublicId},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*;output_fields=id,version,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputRoleIdExpectMap: map[string]expect{
|
||||
globalRole.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org1Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj2Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj3Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pinned id and action set role grants",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, oidc.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{fmt.Sprintf("ids=%s,%s;actions=read,set-grants;output_fields=id,version,authorized_actions", org2Role.PublicId, proj3Role.PublicId)},
|
||||
GrantScopes: []string{org2.PublicId, proj3.PublicId},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{fmt.Sprintf("ids=%s;actions=read,set-grants;output_fields=id,version,authorized_actions", proj2Role.PublicId)},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputRoleIdExpectMap: map[string]expect{
|
||||
globalRole.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org1Role.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
org2Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj2Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
proj3Role.PublicId: {wantFields: []string{globals.IdField, globals.VersionField, globals.AuthorizedActionsField}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
for inputRoleId, expected := range tc.inputRoleIdExpectMap {
|
||||
readbackRole, _, _, _, err := iamRepo.LookupRole(ctx, inputRoleId)
|
||||
require.NoError(t, err)
|
||||
randomId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
got, finalErr := s.SetRoleGrants(fullGrantAuthCtx, &pbs.SetRoleGrantsRequest{
|
||||
Id: inputRoleId,
|
||||
Version: readbackRole.Version,
|
||||
// use any randomized grants string
|
||||
GrantStrings: []string{fmt.Sprintf("ids=ttcp_%s;actions=read", randomId[:6])},
|
||||
})
|
||||
if expected.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, expected.wantErr)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
handlers.TestAssertOutputFields(t, got.Item, expected.wantFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -244,7 +244,7 @@ func (s Service) GetRole(ctx context.Context, req *pbs.GetRoleRequest) (*pbs.Get
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -282,7 +282,7 @@ func (s Service) CreateRole(ctx context.Context, req *pbs.CreateRoleRequest) (*p
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -320,7 +320,7 @@ func (s Service) UpdateRole(ctx context.Context, req *pbs.UpdateRoleRequest) (*p
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -356,7 +356,7 @@ func (s Service) DeleteRole(ctx context.Context, req *pbs.DeleteRoleRequest) (*p
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -374,7 +374,7 @@ func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipa
|
||||
if err := validateAddRolePrincipalsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddPrincipals)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddPrincipals, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -412,7 +412,7 @@ func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipa
|
||||
if err := validateSetRolePrincipalsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetPrincipals)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetPrincipals, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -450,7 +450,7 @@ func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePr
|
||||
if err := validateRemoveRolePrincipalsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemovePrincipals)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemovePrincipals, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -488,7 +488,7 @@ func (s Service) AddRoleGrants(ctx context.Context, req *pbs.AddRoleGrantsReques
|
||||
if err := validateAddRoleGrantsRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddGrants)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddGrants, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -526,7 +526,7 @@ func (s Service) SetRoleGrants(ctx context.Context, req *pbs.SetRoleGrantsReques
|
||||
if err := validateSetRoleGrantsRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetGrants)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetGrants, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -564,7 +564,7 @@ func (s Service) RemoveRoleGrants(ctx context.Context, req *pbs.RemoveRoleGrants
|
||||
if err := validateRemoveRoleGrantsRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveGrants)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveGrants, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -602,7 +602,7 @@ func (s Service) AddRoleGrantScopes(ctx context.Context, req *pbs.AddRoleGrantSc
|
||||
if err := validateRoleGrantScopesRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddGrantScopes)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddGrantScopes, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -641,7 +641,7 @@ func (s Service) SetRoleGrantScopes(ctx context.Context, req *pbs.SetRoleGrantSc
|
||||
if err := validateRoleGrantScopesRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetGrantScopes)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetGrantScopes, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -680,7 +680,7 @@ func (s Service) RemoveRoleGrantScopes(ctx context.Context, req *pbs.RemoveRoleG
|
||||
if err := validateRoleGrantScopesRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveGrants)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveGrants, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -952,7 +952,7 @@ func (s Service) addGrantScopesInRepo(ctx context.Context, req grantScopeRequest
|
||||
|
||||
deduped := strutil.RemoveDuplicates(req.GetGrantScopeIds(), false)
|
||||
|
||||
if err := validateRoleGrantScopesHierarchy(ctx, repo, req.GetId(), deduped); err != nil {
|
||||
if err := validateAndCleanRoleGrantScopesHierarchy(ctx, repo, req.GetId(), deduped); err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
@@ -980,7 +980,7 @@ func (s Service) setGrantScopesInRepo(ctx context.Context, req grantScopeRequest
|
||||
|
||||
deduped := strutil.RemoveDuplicates(req.GetGrantScopeIds(), false)
|
||||
|
||||
if err := validateRoleGrantScopesHierarchy(ctx, repo, req.GetId(), deduped); err != nil {
|
||||
if err := validateAndCleanRoleGrantScopesHierarchy(ctx, repo, req.GetId(), deduped); err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
@@ -1005,7 +1005,6 @@ func (s Service) removeGrantScopesInRepo(ctx context.Context, req grantScopeRequ
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
_, err = repo.DeleteRoleGrantScopes(ctx, req.GetId(), req.GetVersion(), strutil.RemoveDuplicates(req.GetGrantScopeIds(), false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
@@ -1021,7 +1020,7 @@ func (s Service) removeGrantScopesInRepo(ctx context.Context, req grantScopeRequ
|
||||
return out, pr, roleGrants, grantScopes, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isResursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -1030,7 +1029,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Role), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isResursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -1057,7 +1056,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Role, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *iam.Role, principals []*iam.PrincipalRole, grants []*iam.RoleGrant, grantScopes []*iam.RoleGrantScope, opt ...handlers.Option) (*pb.Role, error) {
|
||||
@@ -1458,13 +1457,15 @@ func validateRoleGrantScopesRequest(ctx context.Context, req grantScopeRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateRoleGrantScopesHierarchy is the companion to the domain-side logic to
|
||||
// validateAndCleanRoleGrantScopesHierarchy is the companion to the domain-side logic to
|
||||
// validate scopes. It doesn't do all of the same checking but will allow for
|
||||
// better error messages when possible. We perform this check after
|
||||
// authentication to limit the possibility of an anonymous user causing DB load
|
||||
// due to this lookup, which is not a cheap one.
|
||||
func validateRoleGrantScopesHierarchy(ctx context.Context, repo *iam.Repository, roleId string, grantScopes []string) error {
|
||||
const op = "service.(Service).validateRoleGrantScopesHierarchy"
|
||||
// This function also converts grant scope that is the role's scope ID to 'this'
|
||||
// by mutating grantScopes input
|
||||
func validateAndCleanRoleGrantScopesHierarchy(ctx context.Context, repo *iam.Repository, roleId string, grantScopes []string) error {
|
||||
const op = "service.(Service).validateAndCleanRoleGrantScopesHierarchy"
|
||||
// We want to ensure that the values being passed in make sense to whatever
|
||||
// extent we can right now, so we can provide nice errors back instead of DB
|
||||
// errors.
|
||||
@@ -1474,12 +1475,18 @@ func validateRoleGrantScopesHierarchy(ctx context.Context, repo *iam.Repository,
|
||||
}
|
||||
switch {
|
||||
case role.ScopeId == scope.Global.String():
|
||||
// Nothing, any grant scope is allowed for global
|
||||
for i, grantScope := range grantScopes {
|
||||
if grantScope == scope.Global.String() {
|
||||
grantScopes[i] = globals.GrantScopeThis
|
||||
}
|
||||
}
|
||||
case strings.HasPrefix(role.ScopeId, scope.Project.Prefix()):
|
||||
// In this case only "this" or the same project scope is allowed
|
||||
for _, grantScope := range grantScopes {
|
||||
for i, grantScope := range grantScopes {
|
||||
switch grantScope {
|
||||
case globals.GrantScopeThis, role.ScopeId:
|
||||
case globals.GrantScopeThis:
|
||||
case role.ScopeId:
|
||||
grantScopes[i] = globals.GrantScopeThis
|
||||
default:
|
||||
return handlers.InvalidArgumentErrorf(
|
||||
"Invalid grant scope.",
|
||||
@@ -1490,10 +1497,11 @@ func validateRoleGrantScopesHierarchy(ctx context.Context, repo *iam.Repository,
|
||||
}
|
||||
case strings.HasPrefix(role.ScopeId, scope.Org.Prefix()):
|
||||
// Orgs can have "this", its own scope, a project scope, or "children"
|
||||
for _, grantScope := range grantScopes {
|
||||
for i, grantScope := range grantScopes {
|
||||
switch {
|
||||
case grantScope == role.ScopeId,
|
||||
grantScope == globals.GrantScopeThis,
|
||||
case grantScope == role.ScopeId:
|
||||
grantScopes[i] = globals.GrantScopeThis
|
||||
case grantScope == globals.GrantScopeThis,
|
||||
grantScope == globals.GrantScopeChildren,
|
||||
strings.HasPrefix(grantScope, scope.Project.Prefix()):
|
||||
default:
|
||||
|
||||
@@ -219,8 +219,7 @@ func TestList(t *testing.T) {
|
||||
var wantProjRoles []*pb.Role
|
||||
var totalRoles []*pb.Role
|
||||
for i := 0; i < 10; i++ {
|
||||
or := iam.TestRole(t, conn, oWithRoles.GetPublicId())
|
||||
_ = iam.TestRoleGrantScope(t, conn, or.GetPublicId(), globals.GrantScopeChildren)
|
||||
or := iam.TestRole(t, conn, oWithRoles.GetPublicId(), iam.WithGrantScopeIds([]string{globals.GrantScopeThis, globals.GrantScopeChildren}))
|
||||
wantOrgRoles = append(wantOrgRoles, &pb.Role{
|
||||
Id: or.GetPublicId(),
|
||||
ScopeId: or.GetScopeId(),
|
||||
@@ -908,7 +907,7 @@ func TestCreate(t *testing.T) {
|
||||
Name: &wrapperspb.StringValue{Value: "name"},
|
||||
Description: &wrapperspb.StringValue{Value: "desc"},
|
||||
GrantScopeIds: []string{globals.GrantScopeThis},
|
||||
Version: 2,
|
||||
Version: 1,
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
@@ -928,7 +927,7 @@ func TestCreate(t *testing.T) {
|
||||
Name: &wrapperspb.StringValue{Value: "name"},
|
||||
Description: &wrapperspb.StringValue{Value: "desc"},
|
||||
GrantScopeIds: []string{globals.GrantScopeThis},
|
||||
Version: 2,
|
||||
Version: 1,
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
@@ -950,7 +949,7 @@ func TestCreate(t *testing.T) {
|
||||
Name: &wrapperspb.StringValue{Value: "name"},
|
||||
Description: &wrapperspb.StringValue{Value: "desc"},
|
||||
GrantScopeIds: []string{globals.GrantScopeThis},
|
||||
Version: 2,
|
||||
Version: 1,
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
@@ -2602,9 +2601,9 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
{
|
||||
name: "Add grant scopes on role with grant scopes - global",
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{"global", o.PublicId},
|
||||
existing: []string{"global"},
|
||||
add: []string{"children", p.PublicId},
|
||||
result: []string{"global", o.PublicId, p.PublicId, "children"},
|
||||
result: []string{"this", p.PublicId, "children"},
|
||||
},
|
||||
{
|
||||
name: "Add duplicate grant on role with grant scopes - global",
|
||||
@@ -2624,8 +2623,8 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
name: "Add grant scope matching existing grant scopes - global",
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{"this"},
|
||||
add: []string{"this", "children"},
|
||||
wantErr: true,
|
||||
add: []string{"this"},
|
||||
result: []string{"this"},
|
||||
},
|
||||
{
|
||||
name: "Add invalid grant scope - global",
|
||||
@@ -2639,14 +2638,16 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{scope.Global.String()},
|
||||
add: []string{"this"},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Add scope to this grant scope - global",
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{"this"},
|
||||
add: []string{scope.Global.String()},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Add children when descendants exists - global",
|
||||
@@ -2674,6 +2675,7 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
existing: []string{o.PublicId, p.PublicId},
|
||||
add: []string{"children"},
|
||||
result: []string{o.PublicId, p.PublicId, "children"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Add duplicate grant on role with grant scopes - org",
|
||||
@@ -2687,7 +2689,8 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{"this"},
|
||||
add: []string{"this", "children"},
|
||||
wantErr: true,
|
||||
result: []string{"this", "children"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Add invalid grant scope - org",
|
||||
@@ -2708,14 +2711,16 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{o.PublicId},
|
||||
add: []string{"this"},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Add scope to this grant scope - org",
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{"this"},
|
||||
add: []string{o.PublicId},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Add other org/proj scopes - org",
|
||||
@@ -2728,7 +2733,7 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
name: "Add grant scopes on empty role - proj with id",
|
||||
scopeId: p.PublicId,
|
||||
add: []string{p.PublicId},
|
||||
result: []string{p.PublicId},
|
||||
result: []string{"this"},
|
||||
},
|
||||
{
|
||||
name: "Add grant scopes on empty role - proj with this",
|
||||
@@ -2768,14 +2773,16 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
scopeId: p.PublicId,
|
||||
existing: []string{p.PublicId},
|
||||
add: []string{"this"},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Add scope to this grant scope - proj",
|
||||
scopeId: p.PublicId,
|
||||
existing: []string{"this"},
|
||||
add: []string{p.PublicId},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Add other org/proj scopes - proj",
|
||||
@@ -2791,14 +2798,20 @@ func TestAddGrantScopes(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
role := iam.TestRole(t, conn, tc.scopeId, iam.WithGrantScopeIds([]string{"testing-none"}))
|
||||
for _, e := range tc.existing {
|
||||
_ = iam.TestRoleGrantScope(t, conn, role.GetPublicId(), e)
|
||||
_ = iam.TestRoleGrantScope(t, conn, role, e)
|
||||
}
|
||||
noAuthCtx := auth.DisabledAuthTestContext(repoFn, tc.scopeId)
|
||||
readbackRole, err := s.GetRole(noAuthCtx, &pbs.GetRoleRequest{
|
||||
Id: role.PublicId,
|
||||
})
|
||||
require.NoError(err)
|
||||
|
||||
req := &pbs.AddRoleGrantScopesRequest{
|
||||
Id: role.GetPublicId(),
|
||||
Version: role.GetVersion(),
|
||||
Version: readbackRole.GetItem().GetVersion(),
|
||||
}
|
||||
req.GrantScopeIds = tc.add
|
||||
got, err := s.AddRoleGrantScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), req)
|
||||
got, err := s.AddRoleGrantScopes(noAuthCtx, req)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
if tc.wantErrContains != "" {
|
||||
@@ -2928,7 +2941,7 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{"this"},
|
||||
set: []string{scope.Global.String(), "children"},
|
||||
result: []string{scope.Global.String(), "children"},
|
||||
result: []string{"this", "children"},
|
||||
},
|
||||
{
|
||||
name: "Set other org/proj scopes - global",
|
||||
@@ -2949,14 +2962,16 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{},
|
||||
set: []string{scope.Global.String(), "children", "this"},
|
||||
wantErr: true,
|
||||
result: []string{"this", "children"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Set both grant scope - global",
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{},
|
||||
set: []string{"this", scope.Global.String(), "children"},
|
||||
wantErr: true,
|
||||
result: []string{"this", "children"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Set children and descendants - global",
|
||||
@@ -2989,8 +3004,8 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
name: "Set duplicate grant on role with grant scopes - org",
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{"this"},
|
||||
set: []string{p.PublicId, "children", "children"},
|
||||
result: []string{p.PublicId, "children"},
|
||||
set: []string{"children", "children"},
|
||||
result: []string{"children"},
|
||||
},
|
||||
{
|
||||
name: "Set grant scope matching existing grant scopes - org",
|
||||
@@ -3011,7 +3026,7 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{"this"},
|
||||
set: []string{o.PublicId, "children"},
|
||||
result: []string{o.PublicId, "children"},
|
||||
result: []string{"this", "children"},
|
||||
},
|
||||
{
|
||||
name: "Set invalid grant scope - org",
|
||||
@@ -3032,20 +3047,22 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{},
|
||||
set: []string{o.PublicId, "children", "this"},
|
||||
wantErr: true,
|
||||
result: []string{"this", "children"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Set both grant scope - org",
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{},
|
||||
set: []string{"this", o.PublicId, "children"},
|
||||
wantErr: true,
|
||||
result: []string{"this", "children"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Set grant scopes on empty role - proj with id",
|
||||
scopeId: p.PublicId,
|
||||
set: []string{p.PublicId},
|
||||
result: []string{p.PublicId},
|
||||
result: []string{"this"},
|
||||
},
|
||||
{
|
||||
name: "Set grant scopes on empty role - proj with this",
|
||||
@@ -3071,7 +3088,7 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
scopeId: p.PublicId,
|
||||
existing: []string{"this"},
|
||||
set: []string{p.PublicId},
|
||||
result: []string{p.PublicId},
|
||||
result: []string{"this"},
|
||||
},
|
||||
{
|
||||
name: "Set other org/proj scopes - org",
|
||||
@@ -3106,14 +3123,16 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
scopeId: p.PublicId,
|
||||
existing: []string{},
|
||||
set: []string{p.PublicId, "this"},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Set both grant scope - proj",
|
||||
scopeId: p.PublicId,
|
||||
existing: []string{},
|
||||
set: []string{"this", p.PublicId},
|
||||
wantErr: true,
|
||||
result: []string{"this"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Set other org/proj scopes - proj",
|
||||
@@ -3129,14 +3148,18 @@ func TestSetGrantScopes(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
role := iam.TestRole(t, conn, tc.scopeId, iam.WithGrantScopeIds([]string{"testing-none"}))
|
||||
for _, e := range tc.existing {
|
||||
_ = iam.TestRoleGrantScope(t, conn, role.GetPublicId(), e)
|
||||
_ = iam.TestRoleGrantScope(t, conn, role, e)
|
||||
}
|
||||
req := &pbs.SetRoleGrantScopesRequest{
|
||||
Id: role.GetPublicId(),
|
||||
Version: role.GetVersion(),
|
||||
}
|
||||
req.GrantScopeIds = tc.set
|
||||
got, err := s.SetRoleGrantScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), req)
|
||||
noAuthCtx := auth.DisabledAuthTestContext(repoFn, tc.scopeId)
|
||||
readbackRole, err := s.GetRole(noAuthCtx, &pbs.GetRoleRequest{
|
||||
Id: role.PublicId,
|
||||
})
|
||||
require.NoError(err)
|
||||
got, err := s.SetRoleGrantScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), &pbs.SetRoleGrantScopesRequest{
|
||||
Id: role.GetPublicId(),
|
||||
Version: readbackRole.GetItem().GetVersion(),
|
||||
GrantScopeIds: tc.set,
|
||||
})
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
if tc.wantErrContains != "" {
|
||||
@@ -3233,15 +3256,15 @@ func TestRemoveGrantScopes(t *testing.T) {
|
||||
{
|
||||
name: "Remove partial - global",
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{"this", o.PublicId, "descendants", p.PublicId},
|
||||
remove: []string{o.PublicId, p.PublicId},
|
||||
result: []string{"this", "descendants"},
|
||||
existing: []string{"this", "descendants"},
|
||||
remove: []string{"descendants"},
|
||||
result: []string{"this"},
|
||||
},
|
||||
{
|
||||
name: "Remove duplicate - global",
|
||||
scopeId: scope.Global.String(),
|
||||
existing: []string{"this", "children", o.PublicId},
|
||||
remove: []string{"children", o.PublicId, "children"},
|
||||
existing: []string{"this", "children", p.PublicId},
|
||||
remove: []string{"children", p.PublicId, "children"},
|
||||
result: []string{"this"},
|
||||
},
|
||||
{
|
||||
@@ -3267,15 +3290,15 @@ func TestRemoveGrantScopes(t *testing.T) {
|
||||
{
|
||||
name: "Remove partial - org",
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{"children", o.PublicId, p.PublicId},
|
||||
remove: []string{"children"},
|
||||
result: []string{o.PublicId, p.PublicId},
|
||||
existing: []string{"this", p.PublicId},
|
||||
remove: []string{"this"},
|
||||
result: []string{p.PublicId},
|
||||
},
|
||||
{
|
||||
name: "Remove duplicate - org",
|
||||
scopeId: o.PublicId,
|
||||
existing: []string{"this", p.PublicId, "children"},
|
||||
remove: []string{"children", p.PublicId, "children"},
|
||||
existing: []string{"this", "children"},
|
||||
remove: []string{"children", "children"},
|
||||
result: []string{"this"},
|
||||
},
|
||||
{
|
||||
@@ -3295,8 +3318,8 @@ func TestRemoveGrantScopes(t *testing.T) {
|
||||
{
|
||||
name: "Remove all - proj",
|
||||
scopeId: p.PublicId,
|
||||
existing: []string{p.PublicId},
|
||||
remove: []string{p.PublicId},
|
||||
existing: []string{"this"},
|
||||
remove: []string{"this"},
|
||||
},
|
||||
{
|
||||
name: "Remove duplicate - proj",
|
||||
@@ -3326,14 +3349,20 @@ func TestRemoveGrantScopes(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
role := iam.TestRole(t, conn, tc.scopeId, iam.WithGrantScopeIds([]string{"testing-none"}))
|
||||
for _, e := range tc.existing {
|
||||
_ = iam.TestRoleGrantScope(t, conn, role.GetPublicId(), e)
|
||||
_ = iam.TestRoleGrantScope(t, conn, role, e)
|
||||
}
|
||||
noAuthCtx := auth.DisabledAuthTestContext(repoFn, tc.scopeId)
|
||||
// have to read back the role to get the correct role version
|
||||
readbackRole, err := s.GetRole(noAuthCtx, &pbs.GetRoleRequest{
|
||||
Id: role.PublicId,
|
||||
})
|
||||
require.NoError(err)
|
||||
req := &pbs.RemoveRoleGrantScopesRequest{
|
||||
Id: role.GetPublicId(),
|
||||
Version: role.GetVersion(),
|
||||
Version: readbackRole.GetItem().GetVersion(),
|
||||
}
|
||||
req.GrantScopeIds = tc.remove
|
||||
got, err := s.RemoveRoleGrantScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), req)
|
||||
got, err := s.RemoveRoleGrantScopes(noAuthCtx, req)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
@@ -3345,12 +3374,11 @@ func TestRemoveGrantScopes(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
role := iam.TestRole(t, conn, p.GetPublicId(), iam.WithGrantScopeIds([]string{"testing-none"}))
|
||||
role := iam.TestRole(t, conn, p.GetPublicId(), iam.WithGrantScopeIds([]string{"this"}))
|
||||
failCases := []struct {
|
||||
name string
|
||||
req *pbs.RemoveRoleGrantScopesRequest
|
||||
|
||||
err error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Bad Version",
|
||||
|
||||
@@ -5,32 +5,29 @@ package scopes_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/scopes"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
|
||||
//
|
||||
// Role - which scope the role is created in
|
||||
// - global level
|
||||
// - org level
|
||||
// Grant - what IAM grant scope is set for the permission
|
||||
// - global: descendant
|
||||
// - org: children
|
||||
// Scopes [resource]:
|
||||
// - global [globalScope]
|
||||
// - org1
|
||||
// - proj1
|
||||
func TestGrants_ReadActions(t *testing.T) {
|
||||
func TestGrants_ListScopes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
@@ -41,72 +38,756 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
}
|
||||
s, err := scopes.NewServiceFn(ctx, repoFn, kmsCache, 1000)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo)
|
||||
org1 := iam.TestOrg(t, iamRepo, iam.WithName("org1"), iam.WithDescription("test org 1"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2 := iam.TestOrg(t, iamRepo, iam.WithName("org2"), iam.WithDescription("test org 2"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj1 := iam.TestProject(t, iamRepo, org1.GetPublicId(), iam.WithName("proj1"), iam.WithDescription("test project 1"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj2a := iam.TestProject(t, iamRepo, org2.GetPublicId(), iam.WithName("proj2a"), iam.WithDescription("test project 2a"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj2b := iam.TestProject(t, iamRepo, org2.GetPublicId(), iam.WithName("proj2b"), iam.WithDescription("test project 2b"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListScopesRequest
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
}{
|
||||
{
|
||||
name: "global role grant this returns all created scopes",
|
||||
input: &pbs.ListScopesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=scope;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
org1.PublicId,
|
||||
proj1.PublicId,
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListScopesRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantOutfields map[string][]string
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "no grants returns no scopes",
|
||||
input: &pbs.ListScopesRequest{ScopeId: globals.GlobalPrefix},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "global role grant this returns all org scopes",
|
||||
input: &pbs.ListScopesRequest{ScopeId: globals.GlobalPrefix},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list,no-op;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
org1.PublicId: {globals.IdField},
|
||||
org2.PublicId: {globals.IdField},
|
||||
},
|
||||
{
|
||||
name: "org role grant this returns all created scopes",
|
||||
input: &pbs.ListScopesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
{
|
||||
RoleScopeID: org1.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=scope;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
proj1.PublicId,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & children returns org scopes",
|
||||
input: &pbs.ListScopesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
},
|
||||
}
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list,read;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
org1.PublicId: {globals.IdField},
|
||||
org2.PublicId: {globals.IdField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & children recursive returns org and project scopes",
|
||||
input: &pbs.ListScopesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list,read;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
org1.PublicId: {globals.IdField},
|
||||
org2.PublicId: {globals.IdField},
|
||||
proj1.PublicId: {globals.IdField},
|
||||
proj2a.PublicId: {globals.IdField},
|
||||
proj2b.PublicId: {globals.IdField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this & descendants recursive returns org and project scopes",
|
||||
input: &pbs.ListScopesRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list,read;output_fields=id,name"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantOutfields: map[string][]string{
|
||||
org1.PublicId: {globals.IdField, globals.NameField},
|
||||
org2.PublicId: {globals.IdField, globals.NameField},
|
||||
proj1.PublicId: {globals.IdField, globals.NameField},
|
||||
proj2a.PublicId: {globals.IdField, globals.NameField},
|
||||
proj2b.PublicId: {globals.IdField, globals.NameField},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.ListScopes(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Call function to create direct/indirect relationship
|
||||
user, account := tc.userFunc()
|
||||
|
||||
// Create auth token for the user
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create auth context from the auth token
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
got, finalErr := s.ListScopes(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
var gotIds []string
|
||||
|
||||
for _, g := range got.Items {
|
||||
gotIds = append(gotIds, g.GetId())
|
||||
|
||||
// check if the output fields are as expected
|
||||
if tc.wantOutfields[g.Id] != nil {
|
||||
handlers.TestAssertOutputFields(t, g, tc.wantOutfields[g.Id])
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
var gotIDs []string
|
||||
for _, g := range got.Items {
|
||||
gotIDs = append(gotIDs, g.GetId())
|
||||
}
|
||||
require.ElementsMatch(t, tc.wantIDs, gotIDs)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
wantIds := slices.Collect(maps.Keys(tc.wantOutfields))
|
||||
require.ElementsMatch(t, wantIds, gotIds)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// expectedOutput consolidates common output fields for the test cases
|
||||
type expectedOutput struct {
|
||||
err error
|
||||
outputFields []string
|
||||
}
|
||||
|
||||
func TestGrants_GetScope(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
s, err := scopes.NewServiceFn(ctx, repoFn, kmsCache, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1 := iam.TestOrg(t, iamRepo, iam.WithName("org1"), iam.WithDescription("test org 1"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2 := iam.TestOrg(t, iamRepo, iam.WithName("org2"), iam.WithDescription("test org 2"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj1 := iam.TestProject(t, iamRepo, org1.GetPublicId(), iam.WithName("proj1"), iam.WithDescription("test project 1"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj2 := iam.TestProject(t, iamRepo, org2.GetPublicId(), iam.WithName("proj2"), iam.WithDescription("test project 2"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
expected map[*pbs.GetScopeRequest]expectedOutput
|
||||
}{
|
||||
{
|
||||
name: "no grants returns no scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this returns scopes under the global scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=read;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {outputFields: []string{globals.IdField}},
|
||||
{Id: org1.PublicId}: {outputFields: []string{globals.IdField}},
|
||||
{Id: org2.PublicId}: {outputFields: []string{globals.IdField}},
|
||||
{Id: proj1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this and children returns scopes under the global and org scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=read;output_fields=id,name"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {outputFields: []string{globals.IdField, globals.NameField}},
|
||||
{Id: org1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField}},
|
||||
{Id: org2.PublicId}: {outputFields: []string{globals.IdField, globals.NameField}},
|
||||
{Id: proj1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField}},
|
||||
{Id: proj2.PublicId}: {outputFields: []string{globals.IdField, globals.NameField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant children returns scopes under org scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=read;output_fields=id,name,scope_id"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField}},
|
||||
{Id: proj2.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant descendants returns scopes under org scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=read;output_fields=id,name,scope_id,description"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField, globals.DescriptionField}},
|
||||
{Id: proj2.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField, globals.DescriptionField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org1 role grant this returns scopes under the org1 scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=scope;actions=read;output_fields=id,name,scope_id,description,version"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField, globals.DescriptionField, globals.VersionField}},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org1 role grant children returns scopes under project scopes (nothing)",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=scope;actions=read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org1 role grant this and children returns scopes under the org1 scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=scope;actions=read;output_fields=id,name,scope_id,description,version,type"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField, globals.DescriptionField, globals.VersionField, globals.TypeField}},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "proj1 role grant this returns scopes under proj1 scope (nothing)",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj1.PublicId,
|
||||
Grants: []string{"ids=*;type=scope;actions=read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this scope pinned to org1 scope returns org1 scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{fmt.Sprintf("ids=%s;types=scope;actions=read;output_fields=id,name,scope_id,description,version,type,authorized_actions", org1.PublicId)},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField, globals.DescriptionField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField}},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant individual scopes (org1) returns org1 scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=read;output_fields=id,name,scope_id,description,version,type,authorized_actions,created_time,updated_time"},
|
||||
GrantScopes: []string{org1.PublicId},
|
||||
},
|
||||
}),
|
||||
expected: map[*pbs.GetScopeRequest]expectedOutput{
|
||||
{Id: scope.Global.String()}: {err: handlers.ForbiddenError()},
|
||||
{Id: org1.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: org2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
{Id: proj1.PublicId}: {outputFields: []string{globals.IdField, globals.NameField, globals.ScopeIdField, globals.DescriptionField, globals.VersionField, globals.TypeField, globals.AuthorizedActionsField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
{Id: proj2.PublicId}: {err: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
for input, expectedOutput := range tc.expected {
|
||||
got, err := s.GetScope(fullGrantAuthCtx, input)
|
||||
// not found means expect error
|
||||
if expectedOutput.err != nil {
|
||||
require.ErrorIs(t, err, expectedOutput.err)
|
||||
continue
|
||||
}
|
||||
// check if the output fields are as expected
|
||||
if expectedOutput.outputFields != nil {
|
||||
handlers.TestAssertOutputFields(t, got.Item, expectedOutput.outputFields)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrants_CreateScopes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
repoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
s, err := scopes.NewServiceFn(ctx, repoFn, kmsCache, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1 := iam.TestOrg(t, iamRepo, iam.WithName("org1"), iam.WithDescription("test org 1"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2 := iam.TestOrg(t, iamRepo, iam.WithName("org2"), iam.WithDescription("test org 2"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
canCreateInScopes map[*pbs.CreateScopeRequest]error
|
||||
}{
|
||||
{
|
||||
name: "no grants returns no creatable scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{}),
|
||||
canCreateInScopes: map[*pbs.CreateScopeRequest]error{
|
||||
{Item: &pb.Scope{ScopeId: scope.Global.String()}}: handlers.ForbiddenError(),
|
||||
{Item: &pb.Scope{ScopeId: org1.PublicId}}: handlers.ForbiddenError(),
|
||||
{Item: &pb.Scope{ScopeId: org2.PublicId}}: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this can create scopes in the global scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=create"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canCreateInScopes: map[*pbs.CreateScopeRequest]error{
|
||||
{Item: &pb.Scope{ScopeId: scope.Global.String()}}: nil,
|
||||
{Item: &pb.Scope{ScopeId: org1.PublicId}}: handlers.ForbiddenError(),
|
||||
{Item: &pb.Scope{ScopeId: org2.PublicId}}: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant children can create scopes in org scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=create"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
canCreateInScopes: map[*pbs.CreateScopeRequest]error{
|
||||
{Item: &pb.Scope{ScopeId: scope.Global.String()}}: handlers.ForbiddenError(),
|
||||
{Item: &pb.Scope{ScopeId: org1.PublicId}}: nil,
|
||||
{Item: &pb.Scope{ScopeId: org2.PublicId}}: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant descendants can create scopes in org scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=create"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
canCreateInScopes: map[*pbs.CreateScopeRequest]error{
|
||||
{Item: &pb.Scope{ScopeId: scope.Global.String()}}: handlers.ForbiddenError(),
|
||||
{Item: &pb.Scope{ScopeId: org1.PublicId}}: nil,
|
||||
{Item: &pb.Scope{ScopeId: org2.PublicId}}: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role individual grant to org1 can create scopes in org1 scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=create"},
|
||||
GrantScopes: []string{org1.PublicId},
|
||||
},
|
||||
}),
|
||||
canCreateInScopes: map[*pbs.CreateScopeRequest]error{
|
||||
{Item: &pb.Scope{ScopeId: scope.Global.String()}}: handlers.ForbiddenError(),
|
||||
{Item: &pb.Scope{ScopeId: org1.PublicId}}: nil,
|
||||
{Item: &pb.Scope{ScopeId: org2.PublicId}}: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
for req, wantErr := range tc.canCreateInScopes {
|
||||
_, err := s.CreateScope(fullGrantAuthCtx, req)
|
||||
if wantErr != nil {
|
||||
require.ErrorIs(t, err, wantErr)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrants_ListKeyVersionDestructionJobs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
s, err := scopes.NewServiceFn(ctx, repoFn, kmsCache, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1 := iam.TestOrg(t, iamRepo, iam.WithName("org1"), iam.WithDescription("test org 1"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2 := iam.TestOrg(t, iamRepo, iam.WithName("org2"), iam.WithDescription("test org 2"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj1 := iam.TestProject(t, iamRepo, org1.GetPublicId(), iam.WithName("proj1"), iam.WithDescription("test project 1"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj2 := iam.TestProject(t, iamRepo, org2.GetPublicId(), iam.WithName("proj2"), iam.WithDescription("test project 2"), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
canListInScopes map[string]error
|
||||
}{
|
||||
{
|
||||
name: "no grants cannot list key version destruction jobs",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): handlers.ForbiddenError(),
|
||||
org1.PublicId: handlers.ForbiddenError(),
|
||||
org2.PublicId: handlers.ForbiddenError(),
|
||||
proj1.PublicId: handlers.ForbiddenError(),
|
||||
proj2.PublicId: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant this can list key version destruction jobs in the global scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix,
|
||||
password.TestAuthMethodWithAccount,
|
||||
[]iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): nil,
|
||||
org1.PublicId: handlers.ForbiddenError(),
|
||||
org2.PublicId: handlers.ForbiddenError(),
|
||||
proj1.PublicId: handlers.ForbiddenError(),
|
||||
proj2.PublicId: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant children can list key version destruction jobs in org scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix,
|
||||
password.TestAuthMethodWithAccount,
|
||||
[]iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): handlers.ForbiddenError(),
|
||||
org1.PublicId: nil,
|
||||
org2.PublicId: nil,
|
||||
proj1.PublicId: handlers.ForbiddenError(),
|
||||
proj2.PublicId: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org1 role grant this can list key version destruction jobs in the org1 scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix,
|
||||
password.TestAuthMethodWithAccount,
|
||||
[]iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): handlers.ForbiddenError(),
|
||||
org1.PublicId: nil,
|
||||
org2.PublicId: handlers.ForbiddenError(),
|
||||
proj1.PublicId: handlers.ForbiddenError(),
|
||||
proj2.PublicId: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org1 role grant children can list key version destruction jobs in its project scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix,
|
||||
password.TestAuthMethodWithAccount,
|
||||
[]iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): handlers.ForbiddenError(),
|
||||
org1.PublicId: handlers.ForbiddenError(),
|
||||
org2.PublicId: handlers.ForbiddenError(),
|
||||
proj1.PublicId: nil,
|
||||
proj2.PublicId: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant descendants can list key version destruction jobs in its project scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix,
|
||||
password.TestAuthMethodWithAccount,
|
||||
[]iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): handlers.ForbiddenError(),
|
||||
org1.PublicId: nil,
|
||||
org2.PublicId: nil,
|
||||
proj1.PublicId: nil,
|
||||
proj2.PublicId: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant org1 scope can list key version destruction jobs in the org1 scope",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix,
|
||||
password.TestAuthMethodWithAccount,
|
||||
[]iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{org1.PublicId},
|
||||
},
|
||||
}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): handlers.ForbiddenError(),
|
||||
org1.PublicId: nil,
|
||||
org2.PublicId: handlers.ForbiddenError(),
|
||||
proj1.PublicId: handlers.ForbiddenError(),
|
||||
proj2.PublicId: handlers.ForbiddenError(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant to proj1 and proj2 role grant this can list key version destruction in granted projects",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix,
|
||||
password.TestAuthMethodWithAccount,
|
||||
[]iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{proj1.PublicId},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=scope;actions=list-key-version-destruction-jobs"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
canListInScopes: map[string]error{
|
||||
scope.Global.String(): handlers.ForbiddenError(),
|
||||
org1.PublicId: handlers.ForbiddenError(),
|
||||
org2.PublicId: handlers.ForbiddenError(),
|
||||
proj1.PublicId: nil,
|
||||
proj2.PublicId: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
for scopeId, wantErr := range tc.canListInScopes {
|
||||
_, err = s.ListKeyVersionDestructionJobs(fullGrantAuthCtx, &pbs.ListKeyVersionDestructionJobsRequest{
|
||||
ScopeId: scopeId,
|
||||
})
|
||||
if wantErr != nil {
|
||||
require.ErrorIs(t, err, wantErr)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrants_List_AuthorizedAction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
rw := db.New(conn)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
_, _ = iam.TestScopes(t, iamRepo)
|
||||
_, _ = iam.TestScopes(t, iamRepo)
|
||||
repoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
s, err := scopes.NewServiceFn(ctx, repoFn, kmsCache, 1000)
|
||||
require.NoError(t, err)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
user, account := iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=*;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
})()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, err := s.ListScopes(fullGrantAuthCtx, &pbs.ListScopesRequest{ScopeId: globals.GlobalPrefix, Recursive: true})
|
||||
require.NoError(t, err)
|
||||
for _, item := range got.Items {
|
||||
switch item.GetType() {
|
||||
case "global":
|
||||
require.ElementsMatch(t, item.AuthorizedActions, []string{"no-op", "read", "update", "delete", "attach-storage-policy", "detach-storage-policy"})
|
||||
require.Len(t, item.AuthorizedCollectionActions, 11)
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Alias.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.AuthMethod.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.StorageBucket.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.AuthToken.PluralString()].AsSlice(), []string{"list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Group.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Role.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Scope.PluralString()].AsSlice(), []string{"create", "list", "list-keys", "rotate-keys", "list-key-version-destruction-jobs", "destroy-key-version"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.User.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Worker.PluralString()].AsSlice(), []string{"read-certificate-authority", "create-controller-led", "create-worker-led", "reinitialize-certificate-authority", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.SessionRecording.PluralString()].AsSlice(), []string{"list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Policy.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
case "org":
|
||||
require.ElementsMatch(t, item.AuthorizedActions, []string{"no-op", "read", "update", "delete", "attach-storage-policy", "detach-storage-policy"})
|
||||
require.Len(t, item.AuthorizedCollectionActions, 9)
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.AuthMethod.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.StorageBucket.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.AuthToken.PluralString()].AsSlice(), []string{"list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Group.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Role.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Scope.PluralString()].AsSlice(), []string{"create", "list", "list-keys", "rotate-keys", "list-key-version-destruction-jobs", "destroy-key-version"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.User.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.SessionRecording.PluralString()].AsSlice(), []string{"list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Policy.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
case "project":
|
||||
require.ElementsMatch(t, item.AuthorizedActions, []string{"no-op", "read", "update", "delete"})
|
||||
require.Len(t, item.AuthorizedCollectionActions, 7)
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.CredentialStore.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Group.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.HostCatalog.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Role.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Scope.PluralString()].AsSlice(), []string{"list-keys", "rotate-keys", "list-key-version-destruction-jobs", "destroy-key-version"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Session.PluralString()].AsSlice(), []string{"list"})
|
||||
require.ElementsMatch(t, item.AuthorizedCollectionActions[resource.Target.PluralString()].AsSlice(), []string{"create", "list"})
|
||||
default:
|
||||
t.Fatalf("unknown item type: %s", item.GetType())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,23 @@ var (
|
||||
resource.Target: targets.CollectionActions,
|
||||
},
|
||||
}
|
||||
additionalResourceGrants = []resource.Type{
|
||||
resource.Alias,
|
||||
resource.AuthMethod,
|
||||
resource.AuthToken,
|
||||
resource.StorageBucket,
|
||||
resource.Group,
|
||||
resource.Role,
|
||||
resource.Scope,
|
||||
resource.User,
|
||||
resource.SessionRecording,
|
||||
resource.Policy,
|
||||
resource.CredentialStore,
|
||||
resource.HostCatalog,
|
||||
resource.Worker,
|
||||
resource.Session,
|
||||
resource.Target,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -168,7 +185,10 @@ func (s *Service) ListScopes(ctx context.Context, req *pbs.ListScopesRequest) (*
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
|
||||
// Hard-coding 'isRecursive' to true because list scope returns child scopes which requires
|
||||
// additional grants for those child-scopes (recursive) to calculate authorized_actions on the returned scope
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, true)
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -314,7 +334,7 @@ func (s *Service) GetScope(ctx context.Context, req *pbs.GetScopeRequest) (*pbs.
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -366,7 +386,7 @@ func (s *Service) CreateScope(ctx context.Context, req *pbs.CreateScopeRequest)
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -418,7 +438,7 @@ func (s *Service) UpdateScope(ctx context.Context, req *pbs.UpdateScopeRequest)
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -468,7 +488,7 @@ func (s *Service) DeleteScope(ctx context.Context, req *pbs.DeleteScopeRequest)
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -487,7 +507,7 @@ func (s *Service) ListKeys(ctx context.Context, req *pbs.ListKeysRequest) (*pbs.
|
||||
if err := validateListKeysRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ListScopeKeys)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ListScopeKeys, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -533,7 +553,7 @@ func (s *Service) RotateKeys(ctx context.Context, req *pbs.RotateKeysRequest) (*
|
||||
if err := validateRotateKeysRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.RotateScopeKeys)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.RotateScopeKeys, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -553,7 +573,7 @@ func (s *Service) ListKeyVersionDestructionJobs(ctx context.Context, req *pbs.Li
|
||||
if err := validateListKeyVersionDestructionJobsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.ListScopeKeyVersionDestructionJobs)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.ListScopeKeyVersionDestructionJobs, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -595,7 +615,7 @@ func (s *Service) DestroyKeyVersion(ctx context.Context, req *pbs.DestroyKeyVers
|
||||
if err := validateDestroyKeyVersionRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.DestroyScopeKeyVersion)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.DestroyScopeKeyVersion, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -767,7 +787,7 @@ func sortKeys(keys []*pb.Key) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -776,7 +796,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Scope), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive), auth.WithFetchAdditionalResourceGrants(additionalResourceGrants...)}
|
||||
switch a {
|
||||
case action.List, action.Create, action.ListScopeKeys, action.ListScopeKeyVersionDestructionJobs, action.DestroyScopeKeyVersion:
|
||||
parentId = id
|
||||
@@ -803,7 +823,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Scope, opts...)
|
||||
}
|
||||
|
||||
func ToProto(ctx context.Context, in *iam.Scope, opt ...handlers.Option) (*pb.Scope, error) {
|
||||
|
||||
@@ -0,0 +1,653 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package sessions_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/ldap"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
cred "github.com/hashicorp/boundary/internal/credential"
|
||||
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/sessions"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/server"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testSession is a wrapper around "session" to help with setting up additional resources and mutating the session states
|
||||
// to fill in
|
||||
func testSession(t *testing.T,
|
||||
conn *db.DB,
|
||||
kmsCache *kms.Kms,
|
||||
wrapper wrapping.Wrapper,
|
||||
targetRepo *target.Repository,
|
||||
sessRepo *session.Repository,
|
||||
projectId string, isActive bool,
|
||||
) *session.Session {
|
||||
cats := static.TestCatalogs(t, conn, projectId, 1)
|
||||
hosts := static.TestHosts(t, conn, cats[0].PublicId, 1)
|
||||
sets := static.TestSets(t, conn, cats[0].PublicId, 1)
|
||||
_ = static.TestSetMembers(t, conn, sets[0].PublicId, hosts)
|
||||
randomString, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
vaultStore := vault.TestCredentialStore(t, conn, wrapper, projectId, fmt.Sprintf("http://vault%s", randomString), fmt.Sprintf("vault-token-%s", randomString), fmt.Sprintf("accessor-%s", randomString))
|
||||
|
||||
libIds := vault.TestCredentialLibraries(t, conn, wrapper, vaultStore.GetPublicId(), 2)
|
||||
tcpTarget := tcp.TestTarget(context.Background(), t, conn, projectId, randomString, target.WithHostSources([]string{sets[0].GetPublicId()}))
|
||||
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, projectId)
|
||||
upCreds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, randomString, randomString, staticStore.GetPublicId(), projectId, 2)
|
||||
|
||||
ids := target.CredentialSources{
|
||||
BrokeredCredentialIds: []string{libIds[0].GetPublicId(), libIds[1].GetPublicId(), upCreds[0].GetPublicId(), upCreds[1].GetPublicId()},
|
||||
}
|
||||
_, err = targetRepo.AddTargetCredentialSources(context.Background(), tcpTarget.GetPublicId(), tcpTarget.GetVersion(), ids)
|
||||
require.NoError(t, err)
|
||||
dynamicCreds := []*session.DynamicCredential{
|
||||
session.NewDynamicCredential(libIds[0].GetPublicId(), cred.BrokeredPurpose),
|
||||
session.NewDynamicCredential(libIds[1].GetPublicId(), cred.BrokeredPurpose),
|
||||
}
|
||||
staticCreds := []*session.StaticCredential{
|
||||
session.NewStaticCredential(upCreds[0].GetPublicId(), cred.BrokeredPurpose),
|
||||
session.NewStaticCredential(upCreds[1].GetPublicId(), cred.BrokeredPurpose),
|
||||
}
|
||||
at := authtoken.TestAuthToken(t, conn, kmsCache, globals.GlobalPrefix)
|
||||
uId := at.GetIamUserId()
|
||||
worker := server.TestPkiWorker(t, conn, wrapper,
|
||||
server.WithNewIdFunc(func(ctx context.Context) (string, error) {
|
||||
return server.NewWorkerIdFromScopeAndName(ctx, globals.GlobalPrefix, randomString)
|
||||
}),
|
||||
)
|
||||
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
|
||||
UserId: uId,
|
||||
HostId: hosts[0].GetPublicId(),
|
||||
TargetId: tcpTarget.GetPublicId(),
|
||||
HostSetId: sets[0].GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
ProjectId: projectId,
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
ExpirationTime: timestamp.New(time.Now()),
|
||||
ConnectionLimit: 10,
|
||||
WorkerFilter: "worker",
|
||||
EgressWorkerFilter: "egress",
|
||||
IngressWorkerFilter: "ingress",
|
||||
DynamicCredentials: dynamicCreds,
|
||||
StaticCredentials: staticCreds,
|
||||
ProtocolWorkerId: worker.PublicId,
|
||||
CorrelationId: randomString,
|
||||
})
|
||||
|
||||
switch isActive {
|
||||
case true:
|
||||
session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22, "127.0.0.2", 23, "127.0.0.1")
|
||||
default:
|
||||
_, err := sessRepo.CancelSession(t.Context(), sess.PublicId, sess.Version)
|
||||
require.NoError(t, err)
|
||||
terminated, err := sessRepo.TerminateCompletedSessions(t.Context())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, terminated)
|
||||
}
|
||||
return sess
|
||||
}
|
||||
|
||||
func TestGrants_ReadActions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
rw := db.New(conn)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
sessionRepoFn := func(option ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kmsCache, option...)
|
||||
}
|
||||
iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil }
|
||||
|
||||
s, err := sessions.NewService(ctx, sessionRepoFn, iamRepoFn, 100)
|
||||
require.NoError(t, err)
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true))
|
||||
org2, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true))
|
||||
proj3 := iam.TestProject(t, iamRepo, org2.PublicId, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true))
|
||||
|
||||
sessionRepo, err := sessionRepoFn()
|
||||
require.NoError(t, err)
|
||||
proj1Session := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj1.PublicId, true)
|
||||
proj2Session := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true)
|
||||
proj3Session := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj3.PublicId, false)
|
||||
|
||||
type result struct {
|
||||
wantErr error
|
||||
outputFields []string
|
||||
}
|
||||
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
inputResultMap map[*pbs.GetSessionRequest]result
|
||||
}{
|
||||
{
|
||||
name: "global role descendants grants can read all sessions",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=*;output_fields=id,target_id,scope,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
inputResultMap: map[*pbs.GetSessionRequest]result{
|
||||
{Id: proj1Session.PublicId}: {outputFields: []string{globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
{Id: proj2Session.PublicId}: {outputFields: []string{globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
{Id: proj3Session.PublicId}: {outputFields: []string{globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role individual project grants",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=read;output_fields=expiration_time,auth_token_id,user_id,host_set_id,host_ids"},
|
||||
GrantScopes: []string{proj1.PublicId},
|
||||
},
|
||||
}),
|
||||
inputResultMap: map[*pbs.GetSessionRequest]result{
|
||||
{Id: proj1Session.PublicId}: {outputFields: []string{globals.ExpirationTimeField, globals.AuthTokenIdField, globals.UserIdField, globals.HostSetIdField, globals.HostIdsField}},
|
||||
{Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role children grants",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=read;output_fields=version,type,scope_id,endpoint,states,status,certificate,authorized_actions,connections,termination_reason"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
inputResultMap: map[*pbs.GetSessionRequest]result{
|
||||
{Id: proj1Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj2Session.PublicId}: {outputFields: []string{globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.ConnectionsField}},
|
||||
{Id: proj3Session.PublicId}: {outputFields: []string{globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.TerminationReasonField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "proj role this grants",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj1.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=read;output_fields=version,type,scope_id,endpoint,states,status,certificate,termination_reason,authorized_actions,connections"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputResultMap: map[*pbs.GetSessionRequest]result{
|
||||
{Id: proj1Session.PublicId}: {outputFields: []string{globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.ConnectionsField}},
|
||||
{Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple project roles",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj1.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=read;output_fields=authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=read;output_fields=version"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj3.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=read;output_fields=id"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
inputResultMap: map[*pbs.GetSessionRequest]result{
|
||||
{Id: proj1Session.PublicId}: {outputFields: []string{globals.AuthorizedActionsField}},
|
||||
{Id: proj2Session.PublicId}: {outputFields: []string{globals.VersionField}},
|
||||
{Id: proj3Session.PublicId}: {outputFields: []string{globals.IdField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "role with incorrect resource grant",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=worker;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
inputResultMap: map[*pbs.GetSessionRequest]result{
|
||||
{Id: proj1Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "role with incorrect action grant",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=create,update,delete"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
inputResultMap: map[*pbs.GetSessionRequest]result{
|
||||
{Id: proj1Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
{Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
for input, expect := range tc.inputResultMap {
|
||||
got, err := s.GetSession(fullGrantAuthCtx, input)
|
||||
if expect.wantErr != nil {
|
||||
require.ErrorIs(t, expect.wantErr, err)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
handlers.TestAssertOutputFields(t, got.Item, expect.outputFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListSessionsRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
idOutputFieldsMap map[string][]string
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "global list global role descendants grants can read all sessions",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=*;output_fields=id,target_id,scope,created_time,updated_time"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
proj1Session.PublicId: {globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
proj2Session.PublicId: {globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
proj3Session.PublicId: {globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global list org role individual project grants",
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=list,read;output_fields=id,expiration_time,auth_token_id,user_id,host_set_id,host_ids"},
|
||||
GrantScopes: []string{proj1.PublicId},
|
||||
},
|
||||
}),
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
proj1Session.PublicId: {globals.IdField, globals.ExpirationTimeField, globals.AuthTokenIdField, globals.UserIdField, globals.HostSetIdField, globals.HostIdsField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global list org role children grants",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=list,read;output_fields=id,version,type,scope_id,endpoint,states,status,certificate,authorized_actions,termination_reason"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
proj2Session.PublicId: {globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField},
|
||||
proj3Session.PublicId: {globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.TerminationReasonField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "proj role this grants",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj1.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=list,read;output_fields=id,version,type,scope_id,endpoint,states,status,certificate,termination_reason,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
proj1Session.PublicId: {globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple project roles",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj1.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,version"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj3.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,state"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
proj1Session.PublicId: {globals.IdField, globals.AuthorizedActionsField},
|
||||
proj2Session.PublicId: {globals.IdField, globals.VersionField},
|
||||
proj3Session.PublicId: {globals.IdField, globals.StateField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "role with incorrect resource grant",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=worker;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "role with list permission in global but not projects",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
idOutputFieldsMap: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "non-recursive list on global scope without grant",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
IncludeTerminated: true,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=create,update,delete"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "non-recursive list children grant",
|
||||
input: &pbs.ListSessionsRequest{
|
||||
ScopeId: proj2.PublicId,
|
||||
},
|
||||
userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
idOutputFieldsMap: map[string][]string{
|
||||
proj2Session.PublicId: {globals.IdField, globals.AuthorizedActionsField},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
|
||||
got, err := s.ListSessions(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, tc.wantErr, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Len(t, got.Items, len(tc.idOutputFieldsMap))
|
||||
for _, item := range got.Items {
|
||||
wantOutputFields, ok := tc.idOutputFieldsMap[item.GetId()]
|
||||
require.True(t, ok)
|
||||
handlers.TestAssertOutputFields(t, item, wantOutputFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGrants_Cancel(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
rw := db.New(conn)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
sessionRepoFn := func(option ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kmsCache, option...)
|
||||
}
|
||||
iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil }
|
||||
|
||||
s, err := sessions.NewService(ctx, sessionRepoFn, iamRepoFn, 100)
|
||||
require.NoError(t, err)
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true))
|
||||
org2, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true))
|
||||
proj3 := iam.TestProject(t, iamRepo, org2.PublicId, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true))
|
||||
sessionRepo, err := sessionRepoFn()
|
||||
|
||||
require.NoError(t, err)
|
||||
testcases := []struct {
|
||||
name string
|
||||
setup func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account))
|
||||
wantOutputField []string
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "global role full grants can cancel",
|
||||
setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) {
|
||||
userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,target_id,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
})
|
||||
sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true)
|
||||
return sess, userFn
|
||||
},
|
||||
wantOutputField: []string{globals.IdField, globals.TargetIdField, globals.AuthorizedActionsField},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "org role children grants can cancel",
|
||||
setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) {
|
||||
userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,version,type,scope_id,endpoint,states"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
})
|
||||
sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj1.PublicId, true)
|
||||
return sess, userFn
|
||||
},
|
||||
wantOutputField: []string{globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "proj role children grants can cancel",
|
||||
setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) {
|
||||
userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj3.PublicId,
|
||||
Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,status,certificate,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})
|
||||
sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj3.PublicId, true)
|
||||
return sess, userFn
|
||||
},
|
||||
wantOutputField: []string{globals.IdField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "global role specific scope grants can cancel",
|
||||
setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) {
|
||||
userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,authorized_actions"},
|
||||
GrantScopes: []string{proj2.PublicId},
|
||||
},
|
||||
})
|
||||
sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true)
|
||||
return sess, userFn
|
||||
},
|
||||
wantOutputField: []string{globals.IdField, globals.AuthorizedActionsField},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "global specific scope specific id grants can cancel",
|
||||
setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) {
|
||||
sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true)
|
||||
userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{fmt.Sprintf("id=%s;type=session;actions=cancel;output_fields=id,authorized_actions", sess.PublicId)},
|
||||
GrantScopes: []string{proj2.PublicId},
|
||||
},
|
||||
})
|
||||
return sess, userFn
|
||||
},
|
||||
wantOutputField: []string{globals.IdField, globals.AuthorizedActionsField},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "global role children scope grants cannot cancel",
|
||||
setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) {
|
||||
userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
})
|
||||
sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true)
|
||||
return sess, userFn
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "global role children scope grants cannot cancel",
|
||||
setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) {
|
||||
userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,authorized_actions"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
})
|
||||
sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true)
|
||||
return sess, userFn
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
sess, userFn := tc.setup(t)
|
||||
user, account := userFn()
|
||||
|
||||
mySession, _, err := sessionRepo.LookupSession(ctx, sess.PublicId)
|
||||
require.NoError(t, err)
|
||||
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, err := s.CancelSession(fullGrantAuthCtx, &pbs.CancelSessionRequest{
|
||||
Id: mySession.GetPublicId(),
|
||||
Version: mySession.Version,
|
||||
})
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
handlers.TestAssertOutputFields(t, got.GetItem(), tc.wantOutputField)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (s Service) GetSession(ctx context.Context, req *pbs.GetSessionRequest) (*p
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ReadSelf, false)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ReadSelf, false, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -102,9 +102,10 @@ func (s Service) GetSession(ctx context.Context, req *pbs.GetSessionRequest) (*p
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
outputFields = authResults.FetchOutputFields(perms.Resource{
|
||||
Id: ses.GetPublicId(),
|
||||
ScopeId: ses.ProjectId,
|
||||
Type: resource.Session,
|
||||
Id: ses.GetPublicId(),
|
||||
ScopeId: ses.ProjectId,
|
||||
Type: resource.Session,
|
||||
ParentScopeId: authResults.Scope.ParentScopeId,
|
||||
}, action.Read).SelfOrDefaults(authResults.UserId)
|
||||
} else {
|
||||
var ok bool
|
||||
@@ -139,7 +140,7 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, false)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, false, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -290,7 +291,7 @@ func (s Service) CancelSession(ctx context.Context, req *pbs.CancelSessionReques
|
||||
return nil, err
|
||||
}
|
||||
// Ignore decryption failures to ensure the user can always cancel a session.
|
||||
authResults := s.authResult(ctx, req.GetId(), action.CancelSelf, true)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.CancelSelf, true, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -322,9 +323,10 @@ func (s Service) CancelSession(ctx context.Context, req *pbs.CancelSessionReques
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
outputFields = authResults.FetchOutputFields(perms.Resource{
|
||||
Id: ses.GetPublicId(),
|
||||
ScopeId: ses.ProjectId,
|
||||
Type: resource.Session,
|
||||
Id: ses.GetPublicId(),
|
||||
ScopeId: ses.ProjectId,
|
||||
Type: resource.Session,
|
||||
ParentScopeId: authResults.Scope.ParentScopeId,
|
||||
}, action.Cancel).SelfOrDefaults(authResults.UserId)
|
||||
} else {
|
||||
var ok bool
|
||||
@@ -384,11 +386,11 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*session.Session,
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, ignoreSessionDecryptionFailure bool) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, ignoreSessionDecryptionFailure, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Session), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List:
|
||||
parentId = id
|
||||
@@ -428,7 +430,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type, ignor
|
||||
return res
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Session, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *session.Session, opt ...handlers.Option) (*pb.Session, error) {
|
||||
|
||||
@@ -187,7 +187,7 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -340,7 +340,7 @@ func (s Service) GetTarget(ctx context.Context, req *pbs.GetTargetRequest) (*pbs
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -380,7 +380,7 @@ func (s Service) CreateTarget(ctx context.Context, req *pbs.CreateTargetRequest)
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -427,7 +427,7 @@ func (s Service) UpdateTarget(ctx context.Context, req *pbs.UpdateTargetRequest)
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -467,7 +467,7 @@ func (s Service) DeleteTarget(ctx context.Context, req *pbs.DeleteTargetRequest)
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -485,7 +485,7 @@ func (s Service) AddTargetHostSources(ctx context.Context, req *pbs.AddTargetHos
|
||||
if err := validateAddHostSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddHostSources)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddHostSources, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -527,7 +527,7 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos
|
||||
if err := validateSetHostSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetHostSources)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetHostSources, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -567,7 +567,7 @@ func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTar
|
||||
if err := validateRemoveHostSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSources)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSources, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -607,7 +607,7 @@ func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTar
|
||||
if err := validateAddCredentialSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddCredentialSources)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddCredentialSources, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -648,7 +648,7 @@ func (s Service) SetTargetCredentialSources(ctx context.Context, req *pbs.SetTar
|
||||
if err := validateSetCredentialSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetCredentialSources)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetCredentialSources, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -689,7 +689,7 @@ func (s Service) RemoveTargetCredentialSources(ctx context.Context, req *pbs.Rem
|
||||
if err := validateRemoveCredentialSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialSources)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialSources, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -752,7 +752,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
if err := validateAuthorizeSessionRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AuthorizeSession,
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AuthorizeSession, false,
|
||||
target.WithName(req.GetName()),
|
||||
target.WithProjectId(req.GetScopeId()),
|
||||
target.WithProjectName(req.GetScopeName()),
|
||||
@@ -1555,7 +1555,7 @@ func (s Service) removeCredentialSourcesInRepo(ctx context.Context, targetId str
|
||||
func (s Service) aliasCreateAuthResult(ctx context.Context, parentId string) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
a := action.Create
|
||||
opts := []auth.Option{auth.WithType(resource.Alias), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a)}
|
||||
iamRepo, err := s.iamRepoFn()
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
@@ -1571,16 +1571,16 @@ func (s Service) aliasCreateAuthResult(ctx context.Context, parentId string) aut
|
||||
return res
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
ret := auth.Verify(ctx, opts...)
|
||||
ret := auth.Verify(ctx, resource.Alias, opts...)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, lookupOpt ...target.Option) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool, lookupOpt ...target.Option) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
|
||||
var parentId string
|
||||
var t target.Target
|
||||
opts := []auth.Option{auth.WithType(resource.Target), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -1623,7 +1623,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type, looku
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
ret := auth.Verify(ctx, opts...)
|
||||
ret := auth.Verify(ctx, resource.Target, opts...)
|
||||
ret.RoundTripValue = t
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -5,103 +5,595 @@ package tcp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
|
||||
//
|
||||
// Role - which scope the role is created in
|
||||
// - global level
|
||||
// Scopes [resource]:
|
||||
// - org1
|
||||
// - proj1
|
||||
// - proj2
|
||||
func TestGrants_ReadActions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
s, err := testService(t, ctx, conn, kms, wrapper)
|
||||
s, err := testService(t, ctx, conn, kmsCache, wrapper)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo)
|
||||
_, proj2 := iam.TestScopes(t, iamRepo)
|
||||
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
_, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
noTargetOrg := iam.TestOrg(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true))
|
||||
target1 := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), "test address-1", target.WithAddress("8.8.8.8"))
|
||||
target2 := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), "test address-2", target.WithAddress("8.8.8.8"))
|
||||
target3 := tcp.TestTarget(ctx, t, conn, proj2.GetPublicId(), "test address-2", target.WithAddress("8.8.8.8"))
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListTargetsRequest
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
name string
|
||||
input *pbs.ListTargetsRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantErr error
|
||||
wantIdOutputFields map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "global role grant descendants returns all created targets",
|
||||
input: &pbs.ListTargetsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,authorized_actions,scope_id,address"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIdOutputFields: map[string][]string{
|
||||
target1.GetPublicId(): {globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
target2.GetPublicId(): {globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
target3.GetPublicId(): {globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role grant descendants list at org return all org targets",
|
||||
input: &pbs.ListTargetsRequest{
|
||||
ScopeId: org1.PublicId,
|
||||
Recursive: true,
|
||||
},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,authorized_actions,scope_id,address"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIdOutputFields: map[string][]string{
|
||||
target1.GetPublicId(): {globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
target2.GetPublicId(): {globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant this returns all created targets",
|
||||
input: &pbs.ListTargetsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org1.GetPublicId(),
|
||||
GrantStrings: []string{"ids=*;type=target;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: org1.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,authorized_actions,scope_id,address"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{target1.GetPublicId(), target2.GetPublicId()},
|
||||
wantIdOutputFields: map[string][]string{
|
||||
target1.GetPublicId(): {globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
target2.GetPublicId(): {globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this returns all created targets",
|
||||
input: &pbs.ListTargetsRequest{
|
||||
ScopeId: proj2.GetPublicId(),
|
||||
ScopeId: proj2.GetPublicId(),
|
||||
},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj2.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,name,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIdOutputFields: map[string][]string{
|
||||
target3.GetPublicId(): {globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "recursive list return no result but no error",
|
||||
input: &pbs.ListTargetsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: proj2.GetPublicId(),
|
||||
GrantStrings: []string{"ids=*;type=target;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: noTargetOrg.PublicId,
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
Grants: []string{"ids=*;type=target;actions=list"},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIdOutputFields: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "no grant recursive list return error",
|
||||
input: &pbs.ListTargetsRequest{
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
wantErr: nil,
|
||||
wantIDs: []string{target3.GetPublicId()},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "no grant non-recursive list return error",
|
||||
input: &pbs.ListTargetsRequest{
|
||||
ScopeId: proj2.GetPublicId(),
|
||||
},
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kms, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrapper, tok, iamRepo)
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrapper, tok, iamRepo)
|
||||
got, finalErr := s.ListTargets(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
var gotIDs []string
|
||||
for _, g := range got.Items {
|
||||
gotIDs = append(gotIDs, g.GetId())
|
||||
require.Len(t, got.Items, len(tc.wantIdOutputFields))
|
||||
|
||||
for _, item := range got.Items {
|
||||
wantOutputFields, ok := tc.wantIdOutputFields[item.Id]
|
||||
require.True(t, ok)
|
||||
handlers.TestAssertOutputFields(t, item, wantOutputFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
type readTestResult struct {
|
||||
wantErr error
|
||||
wantOutputFields []string
|
||||
}
|
||||
t.Run("Read", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
wantIdOutputFields map[string]readTestResult
|
||||
}{
|
||||
{
|
||||
name: "global role grant descendants returns all created targets",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,authorized_actions,scope_id,address"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
wantIdOutputFields: map[string]readTestResult{
|
||||
target1.GetPublicId(): {wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField}},
|
||||
target2.GetPublicId(): {wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField}},
|
||||
target3.GetPublicId(): {wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role grant children returns all its created targets",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,authorized_actions,scope_id,address"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
wantIdOutputFields: map[string]readTestResult{
|
||||
target1.GetPublicId(): {wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField}},
|
||||
target2.GetPublicId(): {wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField}},
|
||||
target3.GetPublicId(): {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project role grant this returns all created targets",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj2.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,name,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
wantIdOutputFields: map[string]readTestResult{
|
||||
target1.GetPublicId(): {wantErr: handlers.ForbiddenError()},
|
||||
target2.GetPublicId(): {wantErr: handlers.ForbiddenError()},
|
||||
target3.GetPublicId(): {wantOutputFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role with individual project grant returns all created targets on the project",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=target;actions=list,read;output_fields=id,name,created_time,updated_time,version"},
|
||||
GrantScopes: []string{proj2.PublicId},
|
||||
},
|
||||
}),
|
||||
wantIdOutputFields: map[string]readTestResult{
|
||||
target1.GetPublicId(): {wantErr: handlers.ForbiddenError()},
|
||||
target2.GetPublicId(): {wantErr: handlers.ForbiddenError()},
|
||||
target3.GetPublicId(): {wantOutputFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrapper, tok, iamRepo)
|
||||
|
||||
for targetId, expect := range tc.wantIdOutputFields {
|
||||
got, finalErr := s.GetTarget(fullGrantAuthCtx, &pbs.GetTargetRequest{
|
||||
Id: targetId,
|
||||
})
|
||||
if expect.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, expect.wantErr)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
handlers.TestAssertOutputFields(t, got.Item, expect.wantOutputFields)
|
||||
}
|
||||
require.ElementsMatch(t, tc.wantIDs, gotIDs)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGrants_Create(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
s, err := testService(t, ctx, conn, kmsCache, wrapper)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
_, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
type expectResult struct {
|
||||
wantErr error
|
||||
wantOutputFields []string
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.CreateTargetRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
scopeResultMap map[string]expectResult
|
||||
}{
|
||||
{
|
||||
name: "global role descendants grants can create anywhere",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=target;actions=*;output_fields=id,authorized_actions,scope_id,address"},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
}),
|
||||
scopeResultMap: map[string]expectResult{
|
||||
proj1.PublicId: {wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField}},
|
||||
proj2.PublicId: {wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role children grants can create in children scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=target;actions=create;output_fields=id,name,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
}),
|
||||
scopeResultMap: map[string]expectResult{
|
||||
proj1.PublicId: {wantOutputFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
proj2.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "proj role this grants can create this scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj1.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=target;actions=create;output_fields=id,name,created_time,updated_time,version"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
}),
|
||||
scopeResultMap: map[string]expectResult{
|
||||
proj1.PublicId: {wantOutputFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
proj2.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role specific project grants can create in project scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=target;actions=create;output_fields=id,name,created_time,updated_time,version"},
|
||||
GrantScopes: []string{proj2.PublicId},
|
||||
},
|
||||
}),
|
||||
scopeResultMap: map[string]expectResult{
|
||||
proj1.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
proj2.PublicId: {wantOutputFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org role specific project grants can create in project scopes",
|
||||
userFunc: iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.GetPublicId(),
|
||||
Grants: []string{"ids=*;type=target;actions=create;output_fields=id,name,created_time,updated_time,version"},
|
||||
GrantScopes: []string{proj1.PublicId},
|
||||
},
|
||||
}),
|
||||
scopeResultMap: map[string]expectResult{
|
||||
proj1.PublicId: {wantOutputFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField}},
|
||||
proj2.PublicId: {wantErr: handlers.ForbiddenError()},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrapper, tok, iamRepo)
|
||||
for scope, expect := range tc.scopeResultMap {
|
||||
tgt := validTcpTarget(t, scope)
|
||||
got, finalErr := s.CreateTarget(fullGrantAuthCtx, &pbs.CreateTargetRequest{
|
||||
Item: tgt,
|
||||
})
|
||||
if expect.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, expect.wantErr)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, finalErr)
|
||||
handlers.TestAssertOutputFields(t, got.Item, expect.wantOutputFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type userFn func() (*iam.User, auth.Account)
|
||||
|
||||
func TestGrants_SetTargetCredentialSources(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
s, err := testService(t, ctx, conn, kmsCache, wrapper)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
_, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
proj1Vault := vault.TestCredentialStores(t, conn, wrapper, proj1.GetPublicId(), 1)[0]
|
||||
proj1Cls := vault.TestCredentialLibraries(t, conn, wrapper, proj1Vault.GetPublicId(), 2)
|
||||
|
||||
proj2StoreStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj2.GetPublicId())
|
||||
proj2Creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", proj2StoreStatic.GetPublicId(), proj2.GetPublicId(), 2)
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListTargetsRequest
|
||||
setup func(t *testing.T) (*pbs.SetTargetCredentialSourcesRequest, userFn)
|
||||
wantErr error
|
||||
wantOutputFields []string
|
||||
}{
|
||||
{
|
||||
name: "global role grant descendants returns succeed",
|
||||
setup: func(t *testing.T) (*pbs.SetTargetCredentialSourcesRequest, userFn) {
|
||||
setupUser := iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
Grants: []string{"ids=*;type=target;actions=*;output_fields=id,authorized_actions,scope_id,address"},
|
||||
},
|
||||
})
|
||||
randId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
tgt := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), randId, target.WithAddress("8.8.8.8"))
|
||||
return &pbs.SetTargetCredentialSourcesRequest{
|
||||
Id: tgt.GetPublicId(),
|
||||
Version: tgt.GetVersion(),
|
||||
BrokeredCredentialSourceIds: []string{proj1Cls[0].GetPublicId()},
|
||||
}, setupUser
|
||||
},
|
||||
wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
},
|
||||
{
|
||||
name: "global role grant specific project and pinned id returns succeed",
|
||||
setup: func(t *testing.T) (*pbs.SetTargetCredentialSourcesRequest, userFn) {
|
||||
randId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
tgt := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), randId, target.WithAddress("8.8.8.8"))
|
||||
setupUser := iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
GrantScopes: []string{proj1.PublicId},
|
||||
Grants: []string{fmt.Sprintf("ids=%s;actions=set-credential-sources;output_fields=id,authorized_actions,scope_id,address", tgt.GetPublicId())},
|
||||
},
|
||||
})
|
||||
return &pbs.SetTargetCredentialSourcesRequest{
|
||||
Id: tgt.GetPublicId(),
|
||||
Version: tgt.GetVersion(),
|
||||
BrokeredCredentialSourceIds: []string{proj1Cls[0].GetPublicId()},
|
||||
}, setupUser
|
||||
},
|
||||
wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
},
|
||||
{
|
||||
name: "global role grant specific project and pinned id wrong action error",
|
||||
setup: func(t *testing.T) (*pbs.SetTargetCredentialSourcesRequest, userFn) {
|
||||
randId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
tgt := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), randId, target.WithAddress("8.8.8.8"))
|
||||
setupUser := iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
GrantScopes: []string{proj1.PublicId},
|
||||
Grants: []string{fmt.Sprintf("ids=%s;actions=remove-credential-sources;output_fields=id,authorized_actions,scope_id,address", tgt.GetPublicId())},
|
||||
},
|
||||
})
|
||||
return &pbs.SetTargetCredentialSourcesRequest{
|
||||
Id: tgt.GetPublicId(),
|
||||
Version: tgt.GetVersion(),
|
||||
BrokeredCredentialSourceIds: []string{proj1Cls[0].GetPublicId()},
|
||||
}, setupUser
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "org role grant children returns all succeed",
|
||||
setup: func(t *testing.T) (*pbs.SetTargetCredentialSourcesRequest, userFn) {
|
||||
setupUser := iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.GetPublicId(),
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
Grants: []string{"ids=*;type=target;actions=*;output_fields=id,authorized_actions,scope_id,address"},
|
||||
},
|
||||
})
|
||||
randId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
tgt := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), randId, target.WithAddress("8.8.8.8"))
|
||||
return &pbs.SetTargetCredentialSourcesRequest{
|
||||
Id: tgt.GetPublicId(),
|
||||
Version: tgt.GetVersion(),
|
||||
BrokeredCredentialSourceIds: []string{proj1Cls[0].GetPublicId()},
|
||||
}, setupUser
|
||||
},
|
||||
wantOutputFields: []string{globals.IdField, globals.AuthorizedActionsField, globals.ScopeIdField, globals.AddressField},
|
||||
},
|
||||
{
|
||||
name: "org role grant this returns error",
|
||||
setup: func(t *testing.T) (*pbs.SetTargetCredentialSourcesRequest, userFn) {
|
||||
setupUser := iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: org1.GetPublicId(),
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
Grants: []string{"ids=*;type=target;actions=*;output_fields=id,authorized_actions,scope_id,address"},
|
||||
},
|
||||
})
|
||||
randId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
tgt := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), randId, target.WithAddress("8.8.8.8"))
|
||||
return &pbs.SetTargetCredentialSourcesRequest{
|
||||
Id: tgt.GetPublicId(),
|
||||
Version: tgt.GetVersion(),
|
||||
BrokeredCredentialSourceIds: []string{proj1Cls[0].GetPublicId()},
|
||||
}, setupUser
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "project role grant this succeed",
|
||||
setup: func(t *testing.T) (*pbs.SetTargetCredentialSourcesRequest, userFn) {
|
||||
setupUser := iam.TestUserDirectGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj2.GetPublicId(),
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
Grants: []string{"ids=*;type=target;actions=*;output_fields=id,name,created_time,updated_time,version"},
|
||||
},
|
||||
})
|
||||
randId, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
tgt := tcp.TestTarget(ctx, t, conn, proj2.GetPublicId(), randId, target.WithAddress("8.8.8.8"))
|
||||
return &pbs.SetTargetCredentialSourcesRequest{
|
||||
Id: tgt.GetPublicId(),
|
||||
Version: tgt.GetVersion(),
|
||||
BrokeredCredentialSourceIds: []string{proj2Creds[0].GetPublicId()},
|
||||
}, setupUser
|
||||
},
|
||||
wantOutputFields: []string{globals.IdField, globals.NameField, globals.CreatedTimeField, globals.UpdatedTimeField, globals.VersionField},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
input, userFunc := tc.setup(t)
|
||||
user, account := userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrapper, tok, iamRepo)
|
||||
got, err := s.SetTargetCredentialSources(fullGrantAuthCtx, input)
|
||||
if tc.wantErr != nil {
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
handlers.TestAssertOutputFields(t, got.Item, tc.wantOutputFields)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func validTcpTarget(t *testing.T, scopeId string) *pb.Target {
|
||||
randString, err := uuid.GenerateUUID()
|
||||
require.NoError(t, err)
|
||||
return &pb.Target{
|
||||
ScopeId: scopeId,
|
||||
Name: wrapperspb.String(randString),
|
||||
Description: wrapperspb.String(randString),
|
||||
Type: "tcp",
|
||||
Attrs: &pb.Target_TcpTargetAttributes{
|
||||
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
||||
DefaultPort: wrapperspb.UInt32(2),
|
||||
DefaultClientPort: wrapperspb.UInt32(3),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1295,7 +1295,7 @@ func TestCreate(t *testing.T) {
|
||||
r := iam.TestRole(t, conn, "global")
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
||||
_ = iam.TestRoleGrantScope(t, conn, r.GetPublicId(), globals.GrantScopeDescendants)
|
||||
_ = iam.TestRoleGrantScope(t, conn, r, globals.GrantScopeDescendants)
|
||||
|
||||
// Ensure we are using the OSS worker filter function. This prevents us from
|
||||
// running tests in parallel.
|
||||
@@ -1453,7 +1453,7 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}},
|
||||
errStr: "unable to create target alias",
|
||||
errStr: "PermissionDenied",
|
||||
},
|
||||
{
|
||||
name: "Create a target with duplicate aliasses",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/exp/slices"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// TestAssertOutputFields asserts that the output fields of a group match the expected fields
|
||||
// fields that is nil or empty in the result will throw an error if they are listed in expectedFields
|
||||
// e.g. members when group does not contain any members
|
||||
func TestAssertOutputFields(t *testing.T, p proto.Message, expectFields []string) {
|
||||
msg := p.ProtoReflect()
|
||||
descriptor := msg.Descriptor()
|
||||
for i := 0; i < descriptor.Fields().Len(); i++ {
|
||||
fd := descriptor.Fields().Get(i)
|
||||
fieldName := string(fd.Name())
|
||||
if !slices.Contains(expectFields, fieldName) {
|
||||
require.Falsef(t, msg.Has(fd), "expect field '%s' to be empty but got %+v", fd.Name(), msg.Get(fd).Interface())
|
||||
continue
|
||||
}
|
||||
require.Truef(t, msg.Has(fd), "expect field '%s' NOT be empty but got %+v", fd.Name(), msg.Get(fd).Interface())
|
||||
}
|
||||
}
|
||||
@@ -5,36 +5,27 @@ package users_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
talias "github.com/hashicorp/boundary/internal/alias/target"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/users"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
|
||||
//
|
||||
// Role - which scope the role is created in
|
||||
// - global level
|
||||
// - org level
|
||||
// Grant - what IAM grant scope is set for the permission
|
||||
// - global: descendant
|
||||
// - org: children
|
||||
// Scopes [resource]:
|
||||
// - global [globalUser]
|
||||
// - org1 [org1User]
|
||||
// - org1User1
|
||||
// - org2 [org2User]
|
||||
// - org2User1
|
||||
// - org2User2
|
||||
func TestGrants_ReadActions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -50,6 +41,9 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
}
|
||||
s, err := users.NewService(ctx, repoFn, aliasRepoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
org1, _ := iam.TestScopes(t, iamRepo)
|
||||
org2, _ := iam.TestScopes(t, iamRepo)
|
||||
|
||||
@@ -60,13 +54,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *pbs.ListUsersRequest
|
||||
name string
|
||||
input *pbs.ListUsersRequest
|
||||
userFunc func() (*iam.User, auth.Account)
|
||||
// set this flag when the listing user has permission to list global
|
||||
// AND the test attempts to list users at global scope
|
||||
// users which gets created as a part of token generation
|
||||
includeTestUsers bool
|
||||
rolesToCreate []authtoken.TestRoleGrantsForToken
|
||||
wantErr error
|
||||
wantIDs []string
|
||||
}{
|
||||
@@ -76,13 +70,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
includeTestUsers: true,
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
@@ -102,13 +96,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
Recursive: true,
|
||||
},
|
||||
includeTestUsers: false,
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org2.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
org2User1.PublicId,
|
||||
@@ -122,13 +116,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
Recursive: true,
|
||||
},
|
||||
includeTestUsers: false,
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
org2User1.PublicId,
|
||||
@@ -142,13 +136,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
Recursive: true,
|
||||
},
|
||||
includeTestUsers: false,
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
org1User1.PublicId,
|
||||
@@ -163,13 +157,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
Recursive: true,
|
||||
},
|
||||
includeTestUsers: false,
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org1.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
org1User1.PublicId,
|
||||
@@ -182,18 +176,18 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
Recursive: true,
|
||||
},
|
||||
includeTestUsers: false,
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org1.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeID: org2.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
org1User1.PublicId,
|
||||
@@ -208,13 +202,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
Recursive: true,
|
||||
},
|
||||
includeTestUsers: false,
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{},
|
||||
},
|
||||
@@ -225,13 +219,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
Recursive: true,
|
||||
},
|
||||
includeTestUsers: true,
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: nil,
|
||||
wantIDs: []string{
|
||||
globals.AnyAuthenticatedUserId,
|
||||
@@ -246,13 +240,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: false,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: globals.GlobalPrefix,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
wantIDs: nil,
|
||||
},
|
||||
@@ -262,13 +256,13 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: false,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeID: org2.PublicId,
|
||||
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{"ids=*;type=user;actions=list,read"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
},
|
||||
}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
wantIDs: nil,
|
||||
},
|
||||
@@ -278,20 +272,21 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
ScopeId: globals.GlobalPrefix,
|
||||
Recursive: true,
|
||||
},
|
||||
rolesToCreate: []authtoken.TestRoleGrantsForToken{},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
wantIDs: nil,
|
||||
userFunc: iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{}),
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
wantIDs: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
|
||||
user, account := tc.userFunc()
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
// deleting user to keep assertions clean since we're listing users over and over
|
||||
_, _ = iamRepo.DeleteUser(ctx, tok.IamUserId)
|
||||
})
|
||||
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, finalErr := s.ListUsers(fullGrantAuthCtx, tc.input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, finalErr, tc.wantErr)
|
||||
@@ -310,3 +305,377 @@ func TestGrants_ReadActions(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGrants_ListResolvableAliases(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrap := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
kmsCache := kms.TestKms(t, conn, wrap)
|
||||
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
repoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
sqlDb, err := conn.SqlDB(ctx)
|
||||
require.NoError(t, err)
|
||||
// delete all default roles because they come with list-resolvable-aliases permissions
|
||||
_, err = sqlDb.ExecContext(ctx, `delete from iam_role`)
|
||||
require.NoError(t, err)
|
||||
aliasRepoFn := func() (*talias.Repository, error) {
|
||||
return talias.NewRepository(t.Context(), rw, rw, kmsCache)
|
||||
}
|
||||
s, err := users.NewService(ctx, repoFn, aliasRepoFn, 1000)
|
||||
require.NoError(t, err)
|
||||
org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
proj2 := iam.TestProject(t, iamRepo, org1.GetPublicId(), iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
org2, proj3 := iam.TestScopes(t, iamRepo, iam.WithSkipAdminRoleCreation(true), iam.WithSkipDefaultRoleCreation(true))
|
||||
|
||||
target1 := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), "test address-1", target.WithAddress("8.8.8.8"))
|
||||
target2 := tcp.TestTarget(ctx, t, conn, proj2.GetPublicId(), "test address-2", target.WithAddress("8.8.8.8"))
|
||||
target3 := tcp.TestTarget(ctx, t, conn, proj3.GetPublicId(), "test address-3", target.WithAddress("8.8.8.8"))
|
||||
|
||||
alias1 := talias.TestAlias(t, rw, "target1",
|
||||
talias.WithName("target1"),
|
||||
talias.WithDescription("target1"),
|
||||
talias.WithDestinationId(target1.GetPublicId()),
|
||||
talias.WithHostId(target1.GetPublicId()))
|
||||
alias2 := talias.TestAlias(t, rw, "target2",
|
||||
talias.WithName("target2"),
|
||||
talias.WithDescription("target2"),
|
||||
talias.WithDestinationId(target2.GetPublicId()),
|
||||
talias.WithHostId(target2.GetPublicId()))
|
||||
alias3 := talias.TestAlias(t, rw, "target3",
|
||||
talias.WithName("target3"),
|
||||
talias.WithDescription("target3"),
|
||||
talias.WithDestinationId(target3.GetPublicId()),
|
||||
talias.WithHostId(target3.GetPublicId()))
|
||||
|
||||
fmt.Println(alias1, "\n", alias2, "\n", alias3)
|
||||
testcases := []struct {
|
||||
name string
|
||||
// setting returns user/account of a listing user which may be different than user in ListResolvableAliasesRequest
|
||||
setupInput func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest)
|
||||
expectIdOutputFieldsMap map[string][]string
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "global role this and descendants grants return all aliases list same user",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,name,version,description,value",
|
||||
"ids=*;type=target;actions=*",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: listingUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{
|
||||
alias1.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias2.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias3.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global role this and org2 role children grants return all aliases list same user",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,value",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: org2.PublicId,
|
||||
Grants: []string{
|
||||
"ids=*;type=target;actions=*",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: listingUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{
|
||||
alias3.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list other user without permissions returns nothing",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
testUser, _ := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{})()
|
||||
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,value",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: testUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "list other user with list-resolvable-aliases permissions but no targets permissions returns nothing",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
testUser, _ := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=*",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
})()
|
||||
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,value",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: testUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "list other user with no list-resolvable-aliases permissions but all targets permissions returns nothing",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
testUser, _ := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=target;actions=*",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
|
||||
},
|
||||
})()
|
||||
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,value",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: testUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{
|
||||
alias1.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias2.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias3.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list other user with list-resolvable-aliases permissions and some targets permissions returns allowed targets",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
testUser, _ := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj3.PublicId,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=%s;type=target;actions=authorize-session", target3.GetPublicId()),
|
||||
},
|
||||
GrantScopes: []string{proj3.PublicId},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=%s;type=target;actions=authorize-session", "*"),
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=%s;type=target;actions=authorize-session", target1.GetPublicId()),
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
})()
|
||||
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=%s;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,value", testUser.PublicId),
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: testUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{
|
||||
alias1.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias2.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias3.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list other user with list-resolvable-aliases permissions and some targets permissions returns allowed with children grants granting different resources",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
testUser, _ := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: proj1.PublicId,
|
||||
Grants: []string{"ids=*;type=target;actions=authorize-session"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: proj2.PublicId,
|
||||
Grants: []string{"ids=*;type=target;actions=authorize-session"},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: org1.PublicId,
|
||||
Grants: []string{"ids=*;type=role;actions=*"},
|
||||
GrantScopes: []string{globals.GrantScopeChildren},
|
||||
},
|
||||
})()
|
||||
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=%s;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,value", testUser.PublicId),
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: testUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{
|
||||
alias1.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias2.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list other user with specific list-resolvable-aliases permissions and specific targets permissions returns allowed targets",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
testUser, _ := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=list-resolvable-aliases;output_fields=created_time",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=%s,%s;type=target;actions=authorize-session", target2.GetPublicId(), target3.GetPublicId()),
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeDescendants},
|
||||
},
|
||||
})()
|
||||
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=%s;type=user;actions=list-resolvable-aliases;output_fields=id,created_time,value", testUser.PublicId),
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: testUser.PublicId,
|
||||
}
|
||||
},
|
||||
expectIdOutputFieldsMap: map[string][]string{
|
||||
alias2.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
alias3.PublicId: {globals.IdField, globals.CreatedTimeField, globals.ValueField},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no permissions cannot list resolvable aliases",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
"ids=*;type=user;actions=list;output_fields=id,created_time,value",
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: listingUser.PublicId,
|
||||
}
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
{
|
||||
name: "no permissions cannot list resolvable aliases from another user",
|
||||
setupInput: func(t *testing.T) (*iam.User, auth.Account, *pbs.ListResolvableAliasesRequest) {
|
||||
testUser, _ := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{})()
|
||||
|
||||
listingUser, listingAccount := iam.TestUserGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, password.TestAuthMethodWithAccount, []iam.TestRoleGrantsRequest{
|
||||
{
|
||||
RoleScopeId: globals.GlobalPrefix,
|
||||
Grants: []string{
|
||||
fmt.Sprintf("ids=*;type=user;actions=delete"),
|
||||
},
|
||||
GrantScopes: []string{globals.GrantScopeThis},
|
||||
},
|
||||
})()
|
||||
return listingUser, listingAccount, &pbs.ListResolvableAliasesRequest{
|
||||
Id: testUser.PublicId,
|
||||
}
|
||||
},
|
||||
wantErr: handlers.ForbiddenError(),
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user, account, input := tc.setupInput(t)
|
||||
tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
|
||||
got, err := s.ListResolvableAliases(fullGrantAuthCtx, input)
|
||||
if tc.wantErr != nil {
|
||||
require.ErrorIs(t, tc.wantErr, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
var gotIds []string
|
||||
for _, i := range got.Items {
|
||||
gotIds = append(gotIds, i.Id)
|
||||
}
|
||||
require.ElementsMatch(t, gotIds, maps.Keys(tc.expectIdOutputFieldsMap))
|
||||
require.Len(t, got.Items, len(tc.expectIdOutputFieldsMap))
|
||||
|
||||
for _, item := range got.Items {
|
||||
expectFields, ok := tc.expectIdOutputFieldsMap[item.GetId()]
|
||||
require.True(t, ok)
|
||||
handlers.TestAssertOutputFields(t, item, expectFields)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func (s Service) ListUsers(ctx context.Context, req *pbs.ListUsersRequest) (*pbs
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -240,7 +240,7 @@ func (s Service) GetUser(ctx context.Context, req *pbs.GetUserRequest) (*pbs.Get
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func (s Service) CreateUser(ctx context.Context, req *pbs.CreateUserRequest) (*p
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
|
||||
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -315,7 +315,7 @@ func (s Service) UpdateUser(ctx context.Context, req *pbs.UpdateUserRequest) (*p
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -351,7 +351,7 @@ func (s Service) DeleteUser(ctx context.Context, req *pbs.DeleteUserRequest) (*p
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -369,7 +369,7 @@ func (s Service) AddUserAccounts(ctx context.Context, req *pbs.AddUserAccountsRe
|
||||
if err := validateAddUserAccountsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddAccounts)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddAccounts, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -407,7 +407,7 @@ func (s Service) SetUserAccounts(ctx context.Context, req *pbs.SetUserAccountsRe
|
||||
if err := validateSetUserAccountsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetAccounts)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetAccounts, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -445,7 +445,7 @@ func (s Service) RemoveUserAccounts(ctx context.Context, req *pbs.RemoveUserAcco
|
||||
if err := validateRemoveUserAccountsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveAccounts)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveAccounts, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -483,7 +483,7 @@ func (s Service) ListResolvableAliases(ctx context.Context, req *pbs.ListResolva
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ListResolvableAliases)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.ListResolvableAliases, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -492,18 +492,12 @@ func (s Service) ListResolvableAliases(ctx context.Context, req *pbs.ListResolva
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
|
||||
}
|
||||
acl := authResults.ACL()
|
||||
grantsHash, err := authResults.GrantsHash(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
if req.GetId() != authResults.UserId {
|
||||
var err error
|
||||
acl, grantsHash, err = s.aclAndGrantHashForUser(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithoutEvent())
|
||||
}
|
||||
// fetch ACL and grantsHash for target resource so we can resolve ListResolvableAliasesPermissions
|
||||
// because permissions in authResults only contains permissions relevant to resource.User
|
||||
acl, grantsHash, err := s.aclAndGrantHashForUser(ctx, req.GetId(), []resource.Type{resource.Target})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithoutEvent())
|
||||
}
|
||||
|
||||
permissions := acl.ListResolvableAliasesPermissions(resource.Target, targets.IdActions)
|
||||
@@ -597,13 +591,18 @@ func (s Service) ListResolvableAliases(ctx context.Context, req *pbs.ListResolva
|
||||
|
||||
// aclAndGrantHashForUser returns an ACL from the grants provided to the user and
|
||||
// the hash of those grants.
|
||||
func (s Service) aclAndGrantHashForUser(ctx context.Context, userId string) (perms.ACL, []byte, error) {
|
||||
func (s Service) aclAndGrantHashForUser(ctx context.Context, userId string, resourceType []resource.Type) (perms.ACL, []byte, error) {
|
||||
const op = "users.(Service).aclAndGrantHashForUser"
|
||||
iamRepo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return perms.ACL{}, nil, errors.Wrap(ctx, err, op, errors.WithoutEvent())
|
||||
}
|
||||
grantTuples, err := iamRepo.GrantsForUser(ctx, userId)
|
||||
// Need to resolve all possible permissions for a user on a specific resource type because this request
|
||||
// does not have a request scope. A user may be associated with a role at a higher-level scope
|
||||
// (e.g. user in an org can be a principal of a role in the global scope) so we always have to
|
||||
// look up the user's grants as if the request is a global-scoped to resolve the user's
|
||||
// full permissions tree
|
||||
grantTuples, err := iamRepo.GrantsForUser(ctx, userId, resourceType, globals.GlobalPrefix, iam.WithRecursive(true))
|
||||
if err != nil {
|
||||
return perms.ACL{}, nil, errors.Wrap(ctx, err, op, errors.WithoutEvent())
|
||||
}
|
||||
@@ -786,7 +785,7 @@ func (s Service) removeInRepo(ctx context.Context, userId string, accountIds []s
|
||||
return out, accts, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -795,7 +794,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.User), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
@@ -822,7 +821,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.User, opts...)
|
||||
}
|
||||
|
||||
func toProto(ctx context.Context, in *iam.User, accts []string, opt ...handlers.Option) (*pb.User, error) {
|
||||
@@ -887,6 +886,8 @@ func toProto(ctx context.Context, in *iam.User, accts []string, opt ...handlers.
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// toResolvableAliasProto converts *talias.Alias to *aliaspb.Alias only including fields specified in output_fields.
|
||||
// These fields are not included in the result set: Name, Description, Version
|
||||
func toResolvableAliasProto(a *talias.Alias, opt ...handlers.Option) (*aliaspb.Alias, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
@@ -916,7 +917,6 @@ func toResolvableAliasProto(a *talias.Alias, opt ...handlers.Option) (*aliaspb.A
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
pbItem.Type = "target"
|
||||
}
|
||||
|
||||
return pbItem, nil
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -124,7 +124,7 @@ func (s Service) ListWorkers(ctx context.Context, req *pbs.ListWorkersRequest) (
|
||||
if err := validateListRequest(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.List, req.GetRecursive())
|
||||
if authResults.Error != nil {
|
||||
// If it's forbidden, and it's a recursive request, and they're
|
||||
// successfully authenticated but just not authorized, keep going as we
|
||||
@@ -202,7 +202,7 @@ func (s Service) GetWorker(ctx context.Context, req *pbs.GetWorkerRequest) (*pbs
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -286,7 +286,7 @@ func (s Service) CreateControllerLed(ctx context.Context, req *pbs.CreateControl
|
||||
func (s Service) createCommon(ctx context.Context, in *pb.Worker, act action.Type, opt ...server.Option) (*pb.Worker, error) {
|
||||
const op = "workers.(Service).createCommon"
|
||||
|
||||
authResults := s.authResult(ctx, in.GetScopeId(), act)
|
||||
authResults := s.authResult(ctx, in.GetScopeId(), act, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -323,7 +323,7 @@ func (s Service) DeleteWorker(ctx context.Context, req *pbs.DeleteWorkerRequest)
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -351,7 +351,7 @@ func (s Service) UpdateWorker(ctx context.Context, req *pbs.UpdateWorkerRequest)
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -425,7 +425,7 @@ func (s Service) AddWorkerTags(ctx context.Context, req *pbs.AddWorkerTagsReques
|
||||
if err := validateAddTagsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddWorkerTags)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddWorkerTags, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -462,7 +462,7 @@ func (s Service) SetWorkerTags(ctx context.Context, req *pbs.SetWorkerTagsReques
|
||||
if err := validateSetTagsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetWorkerTags)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetWorkerTags, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -499,7 +499,7 @@ func (s Service) RemoveWorkerTags(ctx context.Context, req *pbs.RemoveWorkerTags
|
||||
if err := validateRemoveTagsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveWorkerTags)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveWorkerTags, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -536,7 +536,7 @@ func (s Service) ReadCertificateAuthority(ctx context.Context, req *pbs.ReadCert
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.ReadCertificateAuthority)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.ReadCertificateAuthority, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -557,7 +557,7 @@ func (s Service) ReinitializeCertificateAuthority(ctx context.Context, req *pbs.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.ReinitializeCertificateAuthority)
|
||||
authResults := s.authResult(ctx, req.GetScopeId(), action.ReinitializeCertificateAuthority, false)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -751,7 +751,7 @@ func (s Service) removeTagsInRepo(ctx context.Context, workerId string, workerVe
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, isRecursive bool) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -760,7 +760,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Worker), auth.WithAction(a)}
|
||||
opts := []auth.Option{auth.WithAction(a), auth.WithRecursive(isRecursive)}
|
||||
switch a {
|
||||
case action.List, action.CreateWorkerLed, action.CreateControllerLed, action.ReadCertificateAuthority, action.ReinitializeCertificateAuthority:
|
||||
parentId = id
|
||||
@@ -778,7 +778,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
return auth.Verify(ctx, opts...)
|
||||
return auth.Verify(ctx, resource.Worker, opts...)
|
||||
}
|
||||
|
||||
func (s Service) listCertificateAuthorityFromRepo(ctx context.Context) (*types.RootCertificates, error) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/server"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/mr-tron/base58"
|
||||
@@ -355,7 +356,9 @@ func Test_unaryCtxInterceptor(t *testing.T) {
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
verifyResults := auth.Verify(retCtx.(context.Context))
|
||||
// Use resource.Scope here but resource type shouldn't matter since we're only validating that auth.Verify
|
||||
// can use the 'context' with additional ctx.Value from the interceptor
|
||||
verifyResults := auth.Verify(retCtx.(context.Context), resource.Scope)
|
||||
assert.NotEmpty(verifyResults)
|
||||
})
|
||||
}
|
||||
@@ -641,7 +644,9 @@ func Test_streamCtxInterceptor(t *testing.T) {
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
verifyResults := auth.Verify(hdCtx.(context.Context))
|
||||
// Use resource.Scope here but resource type shouldn't matter since we're only validating that auth.Verify
|
||||
// can use the 'context' with additional ctx.Value from the interceptor
|
||||
verifyResults := auth.Verify(hdCtx.(context.Context), resource.Scope)
|
||||
assert.NotEmpty(verifyResults)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user