Skip to content

Add impersonate User feature #7988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

mpmadhavig
Copy link
Contributor

@mpmadhavig mpmadhavig commented Apr 1, 2025

Purpose

$subject

Usecase:

  1. User clicks Impersonate User button.
  2. Console sends an impersonation authorize request on behalf of the my account.
    • Iframe is used because redirection cannot be done using 3rd party request managers.
  3. Get the subject token and id token in the response and send back to the parent iframe.
  4. Parent iframe get the tokens and send the token exchange request.
  5. Upon completion of the flow, an impersonated session will be created.
  6. Upon completion of the flow, on a new tab, My Account will be opened.

Note:

  • Impersonation resource will be registered under my account by default - immutable

  • impersonate-myaccount role will be created by default - immutable

  • impersonation application permission will be assigned to the role by default - immutable

  • Admin has to assign users to the role.

Related Issues

Related PRs

  • N/A

Checklist

  • e2e cypress tests locally verified. (for internal contributers)
  • Manual test round performed and verified.
  • UX/UI review done on the final implementation.
  • Documentation provided. (Add links if there are any)
  • Relevant backend changes deployed and verified
  • Unit tests provided. (Add links if there are any)
  • Integration tests provided. (Add links if there are any)

Security checks

Copy link

codecov bot commented Apr 1, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 41.79%. Comparing base (9d957b6) to head (a366f23).
Report is 284 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7988      +/-   ##
==========================================
- Coverage   41.85%   41.79%   -0.07%     
==========================================
  Files          42       42              
  Lines         939      938       -1     
  Branches      238      238              
==========================================
- Hits          393      392       -1     
+ Misses        527      502      -25     
- Partials       19       44      +25     
Flag Coverage Δ
@wso2is/core 41.79% <ø> (-0.07%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

see 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.


function authenticate() {

const requestUrl = authorization_endpoint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are loading the spa-sdk from console/index.html. So this iframe too may have access to that. If it does, we can use the spa sdk methods to build the authorization URL, instead of chaining params manually

"&redirect_uri=" + window.location.origin + window.location.pathname +
"&state=sample_state&scope=internal_user_impersonate&response_type=id_token%20subject_token" +
"&requested_subject=" + userId +
"&nonce=2131236&code_challenge=" + code_challenge + "&code_challenge_method=S256";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's randomize the nonce value

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS: If you are using spa sdk, we wouldn't need to worry about this.

window.location.href = requestUrl;
} else {
if (sessionStorage.getItem("impersonation_artifacts") === null) {
sessionStorage.setItem("impersonation_artifacts", hash);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the id token fragment, isn't it? if it is, let's use a better name for the session storage key

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it contains id_token & subject_token this is why I named it as impersonation_artifacts.

const [ codeChallenge, setCodeChallenge ] = useState<string>(undefined);
const [ idToken, setIdToken ] = useState(undefined);
const [ subjectToken, setSubjectToken ] = useState(undefined);
const [ authenticatedUserRoles, setAuthenticatedUserRoles ] = useState<RolesMemberInterface[]>([]);
Copy link
Member

@pavinduLakshan pavinduLakshan Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we try defining a custom hook for the impersonation logic? otherwise the user profile component file is getting longer and impersonation logic is tightly coupled to the user profile component.

We can move the state variables, useEffects and other logic to this custom hook.

Ref: https://github.com/wso2/identity-apps/blob/master/docs/write-code/CODE_QUALITY.md#use-component-hooks-to-separate-the-component-logic-from-the-presentation-layer

"Content-Type": "application/x-www-form-urlencoded"
},
method: "post",
shouldAttachIDPAccessToken: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wouldn't be necessary.

*
* @returns A generated code verifier.
*/
public static generateCodeVerifier(): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there already a method in @wso2is/core for this?

* @param plain - The code verifier.
* @returns A generated code challange.
*/
public static sha256 = async (plain: string) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's improve the function name. If this is not already available in SDK or @wso2is/core module, let's move this function there.

impersonateUserZone: {
actionTitle: "Impersonate User",
header: "Impersonate User",
subheader: "Once Started impersonating the user, you will no longer be able to login with your identity until you terminated the session."
Copy link
Member

@pavinduLakshan pavinduLakshan Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the third person narrative, if possible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the narrative used for other danger zone items as well.

Copy link
Member

@pavinduLakshan pavinduLakshan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add changeset.
  • Let's update the PR title and describe to accurately describe the improvement.
  • Also, let's add a screencast to get a better idea about the UX.

@mpmadhavig mpmadhavig marked this pull request as ready for review April 2, 2025 05:57
commonPostLogoutUrl : commonPostLogoutUrl,
impersonationRoleName: _config.accountApp.impersonationRoleName,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_config is to get dynamic values. since impersonationrolename is something that wouldn't change, we shouldn't use it here, IMO.

@@ -4,6 +4,8 @@
"homeRealmId": ""
},
"accountApp": {
"clientID": "MY_ACCOUNT",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update the deployment.config.json.j2 and framework as well, since this file is for local development only.

@@ -0,0 +1,45 @@
<!--
~ Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the license header for open source code.

@@ -127,7 +129,8 @@ export const EditRole: FunctionComponent<EditRoleProps> = (props: EditRoleProps)
render: () => (
<ResourceTab.Pane controlledSegmentation attached={ false }>
<BasicRoleDetails
isReadOnly={ isAdminRole || isEveryoneRole || isReadOnly || isSharedRole }
isReadOnly={ isAdminRole || isEveryoneRole || isReadOnly || isSharedRole
|| roleObject?.displayName === accountAppImpersonateRoleName }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of comparing the display name, shall we use role.meta.systemRole property? (I'm assuming the myaccount impersonation role is a system role. correct me if im wrong)

@@ -102,6 +107,7 @@ import {
isMultipleEmailsAndMobileNumbersEnabled,
isSchemaReadOnly
} from "../utils/user-management-utils";
import { log } from "console";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unnecessary, isn't it?

@@ -161,7 +167,7 @@ interface UserProfilePropsInterface extends TestableComponentInterface {
*/
editUserDisclaimerMessage?: ReactNode;
/**
* Admin user type
* Admin user typeisLoading: isAuthenticatedUserFetchRequestLoading
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isloading property is not properly added it seems

Comment on lines 1688 to 1691
// Load My Account Application and check
// 1. If it has skip login consent.
// 2. If it has disabled application.
// 3. If it has shared with the sub org.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be the doc comment of the isMyAccountImpersonatable function

<div>
<iframe
hidden
src={ "https://localhost:9001/console/resources/init-impersonate.html"
Copy link
Member

@pavinduLakshan pavinduLakshan Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded console url wouldn't work in other deployments. There should be a util method to get the console base URL. let's use that here

{
!isUserManagedByParentOrg && (
<DangerZone
data-testid={ `${ testId }-danger-zone-toggle` }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of a toggle, cannot we use a button?

for example, when admin starts impersonating a user, he/she clicks on the impersonate button. once the process started, button text is changed to "Stop impersonation" (or a similar text). When admin decides to finish impersonating, they can do so by clicking on the button.

Also we can show a banner in the myaccount that reminds admin that they are impersonating a user. I understand that these should have suggested during ux reviews, so +1 to track these suggestions as future improvements if applicable.

@wso2-jenkins-bot
Copy link
Contributor

🦋 Changeset detected

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

@@ -0,0 +1,45 @@
<!--
~ Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the license header for open source code. Please check and update other places too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants