feat: environment/tenant security principal and role management commands#171
feat: environment/tenant security principal and role management commands#171TomProkop wants to merge 27 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…/roles Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ole) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… build errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mmands - Register user/app/team/role under EnvironmentCliCommand.Children - Register the new tenant command tree (TenantCliCommand) under TxcCliCommand.Children and add the TALXIS.CLI.Features.Tenant project reference to TALXIS.CLI.csproj Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Registers App.AppCliCommand, User.UserCliCommand, and Group.GroupCliCommand under TenantCliCommand.Children alongside the existing Role.RoleCliCommand. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Document environment user/app/team/role commands in security-roles.md - Document tenant role/app/user/group commands and their routing between BAP admin-application registration and Power Platform RBAC - Update architecture.md project map with the new Features.Tenant project and expanded scope of Features.Environment - Cross-link security-roles.md from environment-management.md troubleshooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…parsing
Live-testing against a real tenant surfaced two real bugs, both invisible
to mocked unit tests:
1. Wrong endpoint shape: used '/scopes/admin/adminApplications' with
api-version=2020-10-01, which 404s. Verified against the decompiled
pac CLI (Microsoft.PowerApps.CLI ClientPrimitives.PerformApplicationAction)
that the real endpoint is '/adminApplications' (no /scopes/admin/ segment)
with api-version=2021-04-01, for both list and register/unregister.
2. Wrong response shape: the list endpoint returns an OData-wrapped payload
({ "value": [...] }), not a bare JSON array. Now unwraps 'value' when
present, falling back to a bare array for forward-compatibility.
Verified live against pct24002-sit: 'tenant role list', 'environment
role/user/app/team list/get', and 'tenant app list' all return real data.
'tenant app role list' (which exercises the fixed BAP path) got past the
404/parse errors after these fixes; further live validation is blocked
only by an expired interactive sign-in unrelated to this change.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reviewed every new list/read code path for truncation risk on large tenants/environments and fixed three real gaps: 1. PowerPlatformRbacClient (tenant role list, and role assignment listing that backs every 'tenant */role list' command): only read the first page of 'authorization/roleDefinitions' and 'authorization/roleAssignments'. Both endpoints are OData-paged via '@odata.nextLink' (confirmed against the Power Platform API docs) — now follows the link until exhausted, same pattern as MicrosoftGraphClient. 2. BapAdminApiClient.ListAdminApplicationsAsync (backs the synthetic 'admin-application' role check in 'tenant app role'): same '@odata.nextLink' gap, now fixed identically. 3. DataverseSecurityPrincipalManager.RetrieveRelatedEntitiesAsync (backs 'environment user/app/team role list' and 'environment team member list'): used a single RetrieveRequest without following MoreRecords/PagingCookie, unlike the sibling RetrieveAllAsync used for top-level list queries. Low real-world likelihood (would need >5000 roles/members on one principal) but was a silent-truncation risk and inconsistent with the rest of the codebase — now loops pages the same way RetrieveAllAsync does. MicrosoftGraphClient (tenant app/user/group list) already paginated correctly via '@odata.nextLink' — added a regression test since none previously existed, matching the new tests added for the two fixed clients. Verified: full test suite 671/671 passing (4 new pagination regression tests). No FakeXrmEasy-style harness exists yet for IOrganizationServiceAsync2 in this test project, so fix #3 is validated by code review + the existing RetrieveAllAsync precedent rather than a new integration test — flagging this as a coverage gap worth closing with a proper Dataverse SDK test double in a future pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ssignment
Closes a gap where a user who has never signed in to an environment could
not be provisioned (no systemuser record) without first receiving JIT
provisioning via sign-in. Adds a BAP-backed provisioning path matching
pac admin assign-user's addUser call.
- BapAdminApiClient.AddUserToEnvironmentAsync: POST .../addUser with
{"ObjectId": <guid>} body.
- New IEnvironmentUserProvisioningService / EnvironmentUserProvisioningService:
resolves --user (UPN or GUID) via Microsoft Graph, then provisions via BAP.
- New environment user add --user <upn-or-object-id> [--role <csv>] command,
following the same create+role-assign partial-failure pattern as
environment app create.
- Kept separate from environment user self-elevate (different privilege
model/endpoint - BAP addUser requires existing environment-admin access,
self-elevate is a tenant-admin bootstrap path) with reciprocal
cross-referencing help text on both commands.
- Updated security-roles skill doc to document environment user add as the
provisioning step before role add/self-elevate.
- Unit tests: BapAdminApiClientTests (+2), new
EnvironmentUserProvisioningServiceTests (+3). Full suite: 676/676 passing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Discovered live against production while capturing baseline for tenant user role list: Microsoft Graph rejects an entire $filter expression with 400 Bad Request if any clause compares a Guid-typed property (id, appId) to a value that is not a valid GUID literal, even when that clause is combined with "or" against a valid string clause (e.g. userPrincipalName, displayName). TenantRoleResolver.BuildUserFilter/BuildGroupFilter/ BuildServicePrincipalFilter always emitted both clauses regardless of the identifier shape, so every tenant user/group/app role list/add/ remove call with a UPN, display name, or client-id string (the overwhelmingly common case) failed outright. Only raw-GUID lookups worked by accident. Fixed by only including the id/appId eq clause when the supplied value actually parses as a Guid, mirroring the pattern already used correctly in EnvironmentUserProvisioningService. Added 4 regression tests to TenantRoleResolverTests. Full suite: 680/680 passing. Re-verified live against production Graph after the fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ole help text - DataverseSecurityPrincipalManager.CreateTeamAsync assigned raw int values to the teamtype/membershiptype OptionSet attributes instead of wrapping them in OptionSetValue, causing 'Incorrect attribute value type System.Int32' errors on team create. Found via live production testing. - environment team role add/remove help text incorrectly claimed roles can be assigned to any team type. Dataverse rejects role assignment for access teams (they exist only for record sharing) — corrected the description to state role assignment is valid for owner, aad-security-group, and aad-office-group teams only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
txc tenant group role add/list/remove previously resolved --group by calling Microsoft Graph (list + filter by displayName/id), which requires the Group.Read.All delegated permission. That permission is not pre-consented for this CLI's Entra app registration (the same public client id pac cli uses) in most tenants, and this CLI intentionally never prompts tenant admins for extra consent. Investigated how pac admin assign-group avoids this: it never calls Graph for groups at all - it requires the caller to already supply the group's raw object id (and display name, for the Dataverse team record it creates). Adopted the same approach here: - TenantRoleResolver no longer calls Graph to resolve groups. --group must be a valid GUID; a display name now throws a clear validation error explaining why (no consent prompts) and how to find the object id (Entra admin center or az ad group show). - Removed tenant group list/tenant group get, which fundamentally required Graph group search/enumeration and have no pac cli equivalent. - Removed the now-dead Graph group list/parse code (GraphGroup record, MicrosoftGraphClient.ListGroupsAsync) and TenantRoleResolver's BuildGroupFilter/MatchesGroup helpers. - Updated command help text and the security-roles skill doc accordingly. - Updated/added tests confirming zero Graph HTTP calls occur for group operations, and that a non-GUID --group value fails fast with a clear error instead of attempting a Graph call. Live-verified against production: tenant group role add/list/remove now succeed end-to-end with the existing delegated sign-in that previously hit a 403 on Graph's groups endpoint, since no Graph call is made anymore. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds new txc tenant ... command group for tenant-wide Power Platform RBAC role discovery/assignment (users, groups, apps), and expands txc environment ... with Dataverse security-principal management (users, application users, teams) including role assignment and self-elevation/user provisioning. This fits into the CLI by extending the existing command hierarchy and introducing Power Platform control-layer strategies/services plus corresponding test coverage and docs updates.
Changes:
- Introduces
TALXIS.CLI.Features.Tenantwithrole,user,group, andappsubcommands for tenant-scope RBAC. - Adds Power Platform role-assignment strategies (PP-RBAC + synthetic
admin-application) and BAP/Graph/BAP admin transport enhancements. - Expands
txc environmentwith Dataverse principal CRUD-ish workflows (user/app/team/role), includinguser self-elevateand environment user provisioning via Graph + BAP.
Reviewed changes
Copilot reviewed 115 out of 115 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/TALXIS.CLI.Tests/Tenant/User/UserCliCommandTests.cs | Adds CLI-level tests for tenant user list/get and role add validation behavior. |
| tests/TALXIS.CLI.Tests/Tenant/TenantPrincipalCommandTestHost.cs | Introduces shared test host wiring for tenant principal CLI tests (Graph + TenantRoleResolver). |
| tests/TALXIS.CLI.Tests/Tenant/Role/TenantRoleCommandTestHost.cs | Test host wiring for tenant role catalog CLI tests. |
| tests/TALXIS.CLI.Tests/Tenant/Role/RoleListCliCommandTests.cs | Tests tenant role catalog listing and filtering. |
| tests/TALXIS.CLI.Tests/Tenant/Role/RoleGetCliCommandTests.cs | Tests tenant role catalog “get” behavior and validation. |
| tests/TALXIS.CLI.Tests/Tenant/Group/GroupCliCommandTests.cs | Adds CLI-level tests for tenant group list and role remove flow. |
| tests/TALXIS.CLI.Tests/Tenant/App/TenantAppCommandTestHost.cs | Test host wiring for tenant app CLI commands (Graph + TenantRoleResolver). |
| tests/TALXIS.CLI.Tests/Tenant/App/TenantAppCommandSupportTests.cs | Unit-tests tenant app validation exception handling output. |
| tests/TALXIS.CLI.Tests/Tenant/App/AppRoleRemoveCliCommandTests.cs | Tests removing the synthetic admin-application assignment. |
| tests/TALXIS.CLI.Tests/Tenant/App/AppRoleListCliCommandTests.cs | Tests listing merged tenant role + synthetic admin-application assignments. |
| tests/TALXIS.CLI.Tests/Tenant/App/AppRoleAddCliCommandTests.cs | Tests ambiguous role selection behavior for tenant app role add. |
| tests/TALXIS.CLI.Tests/Tenant/App/AppListCliCommandTests.cs | Tests tenant app listing and Graph filter shaping. |
| tests/TALXIS.CLI.Tests/Tenant/App/AppGetCliCommandTests.cs | Tests tenant app get behavior and ambiguity handling. |
| tests/TALXIS.CLI.Tests/TALXIS.CLI.Tests.csproj | Adds project reference to the new Tenant feature project. |
| tests/TALXIS.CLI.Tests/Environment/Platforms/Dataverse/DataverseSecurityPrincipalManagerTests.cs | Adds unit tests for Dataverse security principal helper logic (ambiguity + option mapping). |
| tests/TALXIS.CLI.Tests/Config/Providers/PowerPlatform/PowerPlatformRoleStrategiesTests.cs | Adds tests for PP-RBAC and BAP strategy validation behavior and synthetic assignment listing. |
| tests/TALXIS.CLI.Tests/Config/Providers/PowerPlatform/PowerPlatformRbacClientTests.cs | Adds PP-RBAC client tests for audience usage, paging, and assignment body shape. |
| tests/TALXIS.CLI.Tests/Config/Providers/PowerPlatform/MicrosoftGraphClientTests.cs | Adds Graph client tests (audience, paging, and permission error shaping). |
| tests/TALXIS.CLI.Tests/Architecture/LayeringTests.cs | Extends destructive-command architecture checks to include the Tenant feature assembly. |
| tests/TALXIS.CLI.Tests/Architecture/CommandConventionTests.cs | Extends command convention checks to include the Tenant feature assembly. |
| TALXIS.CLI.sln | Adds the new TALXIS.CLI.Features.Tenant project to the solution. |
| src/TALXIS.CLI/TxcCliCommand.cs | Registers txc tenant as a top-level command group. |
| src/TALXIS.CLI/TALXIS.CLI.csproj | References the new Tenant feature project from the main CLI tool. |
| src/TALXIS.CLI.Platform.PowerPlatform.Control/Strategies/PowerPlatformRbacRoleStrategy.cs | Implements PP-RBAC role assignment strategy (list/add/remove + tenant-role catalog resolution). |
| src/TALXIS.CLI.Platform.PowerPlatform.Control/Strategies/BapAdminApplicationRoleStrategy.cs | Adds synthetic admin-application role support via BAP adminApplications registration. |
| src/TALXIS.CLI.Platform.PowerPlatform.Control/EnvironmentUserProvisioningService.cs | Adds environment user provisioning via Graph lookup + BAP addUser call. |
| src/TALXIS.CLI.Platform.PowerPlatform.Control/EnvironmentSettingsClient.cs | Adds environment self-elevate call via the usermanagement applyAdminRole endpoint. |
| src/TALXIS.CLI.Platform.PowerPlatform.Control/Bap/BapAdminApiClient.cs | Extends BAP client with adminApplications list/register/unregister and addUser support (incl. paging). |
| src/TALXIS.CLI.Platform.Dataverse.Runtime/DependencyInjection/DataverseProviderServiceCollectionExtensions.cs | Wires Graph/RBAC/BAP strategies + TenantRoleResolver + environment user provisioning into DI. |
| src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseUserService.cs | Adds Dataverse user service façade over DataverseSecurityPrincipalManager. |
| src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseTeamService.cs | Adds Dataverse team service façade (CRUD + membership + role assignment). |
| src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseRoleService.cs | Adds Dataverse role lookup façade. |
| src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseAppUserService.cs | Adds Dataverse application user service façade (CRUD + role assignment). |
| src/TALXIS.CLI.Platform.Dataverse.Application/DependencyInjection/DataverseApplicationServiceCollectionExtensions.cs | Registers new Dataverse principal services in DI. |
| src/TALXIS.CLI.Features.Tenant/User/UserRoleRemoveCliCommand.cs | Adds txc tenant user role remove destructive command. |
| src/TALXIS.CLI.Features.Tenant/User/UserRoleListCliCommand.cs | Adds txc tenant user role list. |
| src/TALXIS.CLI.Features.Tenant/User/UserRoleAddCliCommand.cs | Adds txc tenant user role add. |
| src/TALXIS.CLI.Features.Tenant/User/UserListCliCommand.cs | Adds txc tenant user list. |
| src/TALXIS.CLI.Features.Tenant/User/UserGetCliCommand.cs | Adds txc tenant user get. |
| src/TALXIS.CLI.Features.Tenant/User/UserCommandSupport.cs | Implements Graph-backed user discovery + TenantRoleResolver role assignment helpers. |
| src/TALXIS.CLI.Features.Tenant/User/UserCliCommand.cs | Defines tenant user and user role routing commands. |
| src/TALXIS.CLI.Features.Tenant/User/TenantPrincipalCommandSupport.cs | Shared helpers for tenant principal commands (escaping, validation error handling, tabular output). |
| src/TALXIS.CLI.Features.Tenant/TenantCliCommand.cs | Defines the top-level txc tenant routing command and registers subcommands. |
| src/TALXIS.CLI.Features.Tenant/TALXIS.CLI.Features.Tenant.csproj | Adds the new Tenant feature project. |
| src/TALXIS.CLI.Features.Tenant/Role/TenantRoleCommandSupport.cs | Implements tenant role catalog list/get helpers via TenantRoleResolver. |
| src/TALXIS.CLI.Features.Tenant/Role/RoleOutput.cs | Implements text output formatting for tenant role list/get. |
| src/TALXIS.CLI.Features.Tenant/Role/RoleListCliCommand.cs | Adds txc tenant role list. |
| src/TALXIS.CLI.Features.Tenant/Role/RoleGetCliCommand.cs | Adds txc tenant role get. |
| src/TALXIS.CLI.Features.Tenant/Role/RoleCliCommand.cs | Defines tenant role routing command. |
| src/TALXIS.CLI.Features.Tenant/Group/GroupRoleRemoveCliCommand.cs | Adds txc tenant group role remove destructive command. |
| src/TALXIS.CLI.Features.Tenant/Group/GroupRoleListCliCommand.cs | Adds txc tenant group role list. |
| src/TALXIS.CLI.Features.Tenant/Group/GroupRoleAddCliCommand.cs | Adds txc tenant group role add. |
| src/TALXIS.CLI.Features.Tenant/Group/GroupListCliCommand.cs | Adds txc tenant group list. |
| src/TALXIS.CLI.Features.Tenant/Group/GroupGetCliCommand.cs | Adds txc tenant group get. |
| src/TALXIS.CLI.Features.Tenant/Group/GroupCommandSupport.cs | Implements Graph-backed group discovery + TenantRoleResolver role assignment helpers. |
| src/TALXIS.CLI.Features.Tenant/Group/GroupCliCommand.cs | Defines tenant group and group role routing commands. |
| src/TALXIS.CLI.Features.Tenant/App/AssemblyInfo.cs | Exposes tenant app internals to the test project. |
| src/TALXIS.CLI.Features.Tenant/App/AppRoleRemoveCliCommand.cs | Adds txc tenant app role remove destructive command (incl. synthetic role handling). |
| src/TALXIS.CLI.Features.Tenant/App/AppRoleListCliCommand.cs | Adds txc tenant app role list. |
| src/TALXIS.CLI.Features.Tenant/App/AppRoleAddCliCommand.cs | Adds txc tenant app role add. |
| src/TALXIS.CLI.Features.Tenant/App/AppListCliCommand.cs | Adds txc tenant app list. |
| src/TALXIS.CLI.Features.Tenant/App/AppGetCliCommand.cs | Adds txc tenant app get. |
| src/TALXIS.CLI.Features.Tenant/App/AppCliCommand.cs | Defines tenant app and app role routing commands. |
| src/TALXIS.CLI.Features.Environment/User/UserUpdateCliCommand.cs | Adds txc environment user update (enable/disable) command. |
| src/TALXIS.CLI.Features.Environment/User/UserSelfElevateCliCommand.cs | Adds txc environment user self-elevate command. |
| src/TALXIS.CLI.Features.Environment/User/UserRoleRemoveCliCommand.cs | Adds txc environment user role remove. |
| src/TALXIS.CLI.Features.Environment/User/UserRoleListCliCommand.cs | Adds txc environment user role list. |
| src/TALXIS.CLI.Features.Environment/User/UserRoleAddCliCommand.cs | Adds txc environment user role add. |
| src/TALXIS.CLI.Features.Environment/User/UserListCliCommand.cs | Adds txc environment user list. |
| src/TALXIS.CLI.Features.Environment/User/UserGetCliCommand.cs | Adds txc environment user get. |
| src/TALXIS.CLI.Features.Environment/User/UserCliCommand.cs | Adds environment user routing updates (list/get/add/update/role/self-elevate). |
| src/TALXIS.CLI.Features.Environment/Team/TeamRoleRemoveCliCommand.cs | Adds txc environment team role remove. |
| src/TALXIS.CLI.Features.Environment/Team/TeamRoleListCliCommand.cs | Adds txc environment team role list. |
| src/TALXIS.CLI.Features.Environment/Team/TeamRoleAddCliCommand.cs | Adds txc environment team role add. |
| src/TALXIS.CLI.Features.Environment/Team/TeamMemberRemoveCliCommand.cs | Adds txc environment team member remove. |
| src/TALXIS.CLI.Features.Environment/Team/TeamMemberListCliCommand.cs | Adds txc environment team member list. |
| src/TALXIS.CLI.Features.Environment/Team/TeamMemberAddCliCommand.cs | Adds txc environment team member add. |
| src/TALXIS.CLI.Features.Environment/Team/TeamListCliCommand.cs | Adds txc environment team list. |
| src/TALXIS.CLI.Features.Environment/Team/TeamGetCliCommand.cs | Adds txc environment team get. |
| src/TALXIS.CLI.Features.Environment/Team/TeamDeleteCliCommand.cs | Adds txc environment team delete. |
| src/TALXIS.CLI.Features.Environment/Team/TeamCreateCliCommand.cs | Adds txc environment team create. |
| src/TALXIS.CLI.Features.Environment/Team/TeamCliCommand.cs | Adds environment team routing command (list/get/create/delete/member/role). |
| src/TALXIS.CLI.Features.Environment/Role/RoleListCliCommand.cs | Adds txc environment role list. |
| src/TALXIS.CLI.Features.Environment/Role/RoleGetCliCommand.cs | Adds txc environment role get. |
| src/TALXIS.CLI.Features.Environment/Role/RoleCliCommand.cs | Adds environment role routing command. |
| src/TALXIS.CLI.Features.Environment/EnvironmentCliCommand.cs | Registers new environment subcommands (user/app/team/role). |
| src/TALXIS.CLI.Features.Environment/App/AppUpdateCliCommand.cs | Adds txc environment app update (enable/disable). |
| src/TALXIS.CLI.Features.Environment/App/AppRoleRemoveCliCommand.cs | Adds txc environment app role remove. |
| src/TALXIS.CLI.Features.Environment/App/AppRoleListCliCommand.cs | Adds txc environment app role list. |
| src/TALXIS.CLI.Features.Environment/App/AppRoleAddCliCommand.cs | Adds txc environment app role add. |
| src/TALXIS.CLI.Features.Environment/App/AppListCliCommand.cs | Adds txc environment app list. |
| src/TALXIS.CLI.Features.Environment/App/AppGetCliCommand.cs | Adds txc environment app get. |
| src/TALXIS.CLI.Features.Environment/App/AppDeleteCliCommand.cs | Adds txc environment app delete. |
| src/TALXIS.CLI.Features.Environment/App/AppCreateCliCommand.cs | Adds txc environment app create (with optional initial role assignment). |
| src/TALXIS.CLI.Features.Environment/App/AppCliCommand.cs | Adds environment app routing command. |
| src/TALXIS.CLI.Features.Docs/Skills/security-roles.md | Documents environment and tenant role assignment workflows and command sequences. |
| src/TALXIS.CLI.Features.Docs/Skills/environment-management.md | Links environment-management troubleshooting to the security-role workflow doc. |
| src/TALXIS.CLI.Core/Platforms/PowerPlatform/IEnvironmentUserProvisioningService.cs | Adds provisioning service contract + result model. |
| src/TALXIS.CLI.Core/Contracts/PowerPlatform/IPowerPlatformRoleAssignmentStrategy.cs | Adds tenant role strategy abstractions + principal/role/assignment contracts. |
| src/TALXIS.CLI.Core/Contracts/Dataverse/IDataverseUserService.cs | Adds Dataverse user service contract. |
| src/TALXIS.CLI.Core/Contracts/Dataverse/IDataverseTeamService.cs | Adds Dataverse team service contract. |
| src/TALXIS.CLI.Core/Contracts/Dataverse/IDataverseRoleService.cs | Adds Dataverse role service contract. |
| src/TALXIS.CLI.Core/Contracts/Dataverse/IDataverseAppUserService.cs | Adds Dataverse application user service contract. |
| src/TALXIS.CLI.Core/Contracts/Dataverse/DataverseSecurityPrincipalContracts.cs | Adds Dataverse security principal/role/team/user contracts and exception model. |
| docs/architecture.md | Updates architecture documentation to include Tenant feature layer and expanded Environment scope. |
Comments suppressed due to low confidence (1)
src/TALXIS.CLI.Features.Tenant/Group/GroupCommandSupport.cs:156
- Graph $filter currently always includes an
id eq '<input>'clause even when--groupis not a GUID. Microsoft Graph rejects the entire filter with 400 when a GUID-typed property (id) is compared to a non-GUID literal, sotxc tenant group getwill fail for display-name inputs. Align this with TenantRoleResolver.BuildGroupFilter by only emitting theid eqclause when the input parses as a GUID.
| private static string BuildGetFilter(string user) | ||
| { | ||
| ArgumentException.ThrowIfNullOrWhiteSpace(user); | ||
| var escaped = TenantPrincipalCommandSupport.EscapeODataString(user.Trim()); | ||
| return $"id eq '{escaped}' or userPrincipalName eq '{escaped}'"; | ||
| } |
There was a problem hiding this comment.
Fixed in 0d83122 — this call site now guards the id/appId clauses to only appear when the input parses as a GUID, mirroring TenantRoleResolver's pattern. Added regression tests covering both GUID and non-GUID inputs, and live-verified against production that the fixed commands no longer 400.
Code review on PR #171 caught that the GUID-typed-filter bug I'd already fixed in TenantRoleResolver (used by role add/remove/list) was still present in the separate filter-building code backing "tenant user get" and "tenant app get" (UserCommandSupport.BuildGetFilter and TenantAppCommandSupport.BuildExactAppFilter) - I had missed these two call sites when fixing the bug originally. Both unconditionally included an "id eq"/"appId eq" clause even when the input wasn't a GUID (e.g. a UPN or display name), and Microsoft Graph rejects the entire $filter with 400 when any clause compares a GUID-typed property to a non-GUID literal, even inside an "or". Fixed both to only include the GUID-typed clauses when the input actually parses as a GUID, mirroring TenantRoleResolver.BuildUserFilter/BuildServicePrincipalFilter. Updated the existing test that asserted the buggy filter shape, and added regression tests confirming: GUID input still includes the id/appId clauses, and non-GUID input omits them. Live-verified against production: "tenant user get --user <upn>" and "tenant app get --app <display-name>" both resolve correctly (no more 400). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The security-roles skill doc only said "--role admin-application authorizes the app to call environment admin commands" without explaining that this is implemented completely differently from every other tenant role (BAP's adminApplications endpoint instead of Power Platform RBAC), that role list marks it with "isSynthetic": true so scripted consumers can tell it apart from real RBAC assignments, or that it's application-only. Expanded that bullet with all three points and a link to the Microsoft doc this mirrors (Create a service principal to create and manage environments and other resources for Power Platform). Added a matching XML doc summary to BapAdminApplicationRoleStrategy linking the same Microsoft doc, so the code and skill markdown stay consistent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
UserRoleAddCliCommand and UserRoleRemoveCliCommand resolve the user and role once, then call userService.AddRoleAsync/RemoveRoleAsync, which re-resolve both by string identifier internally and can throw DataverseAmbiguousMatchException again (e.g. a race between resolve and mutate). The App/Team role commands already wrap this call in a try/catch routed through TryHandleValidationException (exit code 2, candidate listing) but the User equivalents did not, so the exception fell through to the generic exit-code-1 handler with no candidate list. Wrapped the final AddRoleAsync/RemoveRoleAsync calls in the same try/catch pattern, moving the ExecuteAsync body into a private helper method (ExecuteAddRoleAsync/ExecuteRemoveRoleAsync) since the TXC006 analyzer forbids try/catch directly inside ExecuteAsync — mirroring how AppRoleAddCliCommand already structures this. Added UserRoleCliCommandTests.cs (Environment/User) with fake IDataverseUserService/IDataverseRoleService, since no CLI-level test coverage existed for these commands previously. Full suite: 684/684. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…port classes GraphODataFilterSupport (Platform.PowerPlatform.Control/Graph/) now owns the "only include a GUID-typed OData clause when the input actually parses as a GUID" rule and OData string escaping, previously copy-pasted independently in TenantRoleResolver.BuildServicePrincipalFilter/BuildUserFilter, TenantAppCommandSupport.BuildExactAppFilter, and UserCommandSupport.BuildGetFilter (one comment even admitted it "mirrors" another without sharing code). All four call sites now delegate to the single implementation. TenantAppCommandSupport and TenantRoleCommandSupport also redefined their own private ResolveContextAsync/TryHandleValidationException/Truncate instead of reusing the existing shared TenantPrincipalCommandSupport, unlike GroupCommandSupport which already reused it correctly. Migrated both to the shared class, widening TenantPrincipalCommandSupport. TryHandleValidationException with the ArgumentException/InvalidOperationException fallback that only TenantAppCommandSupport previously had (a strict superset - no existing caller's behavior changes for the exceptions they already handled). Moved TenantAppCommandSupportTests.cs to TenantPrincipalCommandSupportTests.cs targeting the now-shared method, and added coverage for the ArgumentException/InvalidOperationException/unrelated-exception branches that had no direct test before. Full suite: 692/692. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract `EnvironmentPrincipalCommandSupport` (mirroring the existing `TenantPrincipalCommandSupport` on the tenant side) with the state-filter parsing, role-identifier parsing, and column-truncation logic that was duplicated across `UserCliCommandSupport`, `AppCommandSupport`, and `TeamCommandSupport`. All three now delegate to the shared implementation instead of keeping private copies. `TryHandleValidationException`/`LogAmbiguousMatch` were deliberately left as-is in each class: their candidate-logging output format genuinely differs between User and App/Team, and unifying it would change user-visible CLI output for a should-consider (non-bug) finding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`UserCliCommandSupport.SelfElevateAsync` used `Type.GetType` + `MethodInfo.Invoke` to reach `EnvironmentSettingsClient` in `Platform.PowerPlatform.Control`, because `Features.Environment` had no project reference to that assembly. This bypassed compile-time checking and was untested. Add `SelfElevateAsync` to `IEnvironmentUserProvisioningService` (Core) and implement it in `EnvironmentUserProvisioningService` (Control) by injecting `EnvironmentSettingsClient`, mirroring how the service already injects `MicrosoftGraphClient`/`BapAdminApiClient`. `UserCliCommandSupport` now resolves the interface via `TxcServices.Get<T>()` like every other environment command, with no new project reference and no reflection. Add direct unit test coverage for `SelfElevateAsync` (success + failure paths) — there was previously zero coverage for this code path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract `ODataPagingSupport.FetchAllPagesAsync`/`TryReadNextLink` implementing the `@odata.nextLink`-following loop and "value" array unwrap once, instead of four independent copies: - `BapAdminApiClient.ListAdminApplicationsAsync` - `MicrosoftGraphClient.GetPagedAsync` - `PowerPlatformRbacClient.ListRoleDefinitionsAsync` - `PowerPlatformRbacClient.ListTenantRoleAssignmentsAsync` Each client now supplies only its own page-fetch delegate (token acquisition, headers, and HTTP-level error handling) and item projector; the shared helper owns the page-following loop, "value"/bare-array unwrapping, and next-link parsing. Behavior is unchanged — verified by the existing multi-page pagination tests for all three clients, which continue to pass unmodified. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Collapse `TenantAppCommandTestHost`, `TenantRoleCommandTestHost`, and `TenantPrincipalCommandTestHost` — three ~90%-identical HTTP-mocked test host fixtures — into a single `TenantCommandTestHost` in `tests/TALXIS.CLI.Tests/Tenant/`. All tenant-scope CLI command tests (user, group, app, role) now share the same fake `MicrosoftGraphClient`/ `TenantRoleResolver` wiring instead of each maintaining its own copy of the fake token service, HTTP handler queue, and connection fixture. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Adds
txc environment user/app/team/roleandtxc tenant role/app/user/groupcommands for managing Dataverse security principals (systemusers, application users/service principals, teams) and their security role assignments, at both environment (Dataverse) and tenant (Power Platform admin) scope.By design this CLI does not mutate Entra ID — no create/delete of users, groups, or app registrations. It assumes those identities already exist and only manages the Dataverse/Power Platform side (provisioning systemusers/application users, team membership, role assignment).
Command groups
txc environment ...user list/get/add/update/role list/role add/role remove/self-elevateapp list/get/create/update/delete/role list/role add/role removeteam list/get/create/delete/member list/member add/member remove/role list/role add/role removerole list/gettxc tenant ...role list/getuser role list/add/removeapp role list/add/removegroup role list/add/remove(group identified by Entra object id only, not display name — see below)Notable decisions
tenant groupnever calls Microsoft Graph. Graph group search requiresGroup.Read.All, which isn't pre-consented for this CLI's Entra app registration in most tenants (confirmed via a real 403 in live testing). Followingpac admin assign-group's approach,--groupis always a raw object id;tenant group list/getwere removed since they have no non-Graph equivalent.$filterentirely (400) if any clause compares a GUID-typed property to a non-GUID literal — filters are now built to only includeid/appIdclauses when the input parses as a GUID.environment team role add/removeonly supports owner/AAD-security-group/AAD-office-group teams, not access teams (Dataverse limitation, now reflected in help text).Out of scope
Entra ID mutation (create/delete of users, groups, app registrations) — this CLI assumes those identities already exist.