UI – Clarify activity items for JIT provisioned SSO user initial logins (#15192)
## Addresses #14345  - [x] Changes file added for user-visible changes in `changes/` - [x] Added/updated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
co-authored by
Jacob Shandling
parent
9984bc6894
commit
32706a732b
@@ -0,0 +1,2 @@
|
||||
- Update activity feed to elegantly communicate when a JIT-provisioned user logs in for the first
|
||||
time, thereby creating their account.
|
||||
@@ -80,6 +80,7 @@ export interface IActivityDetails {
|
||||
specs?: IQuery[] | IPolicy[];
|
||||
global?: boolean;
|
||||
public_ip?: string;
|
||||
user_id?: number;
|
||||
user_email?: string;
|
||||
email?: string;
|
||||
role?: UserRole;
|
||||
|
||||
+156
-2
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { render, screen, getDefaultNormalizer } from "@testing-library/react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import createMockActivity from "__mocks__/activityMock";
|
||||
import createMockQuery from "__mocks__/queryMock";
|
||||
@@ -232,6 +232,10 @@ describe("Activity Feed", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// // // // // // // // // // // //
|
||||
// created_user tests
|
||||
// // // // // //// // // // // //
|
||||
|
||||
it("renders a created_user type activity globally", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.UserCreated,
|
||||
@@ -245,6 +249,24 @@ describe("Activity Feed", () => {
|
||||
expect(screen.getByText("newuser@example.com")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("correctly renders a created_user type activity for a premium SSO user created by JIT provisioning", () => {
|
||||
const activity = createMockActivity({
|
||||
actor_full_name: "Jit Sso",
|
||||
actor_id: 3,
|
||||
type: ActivityType.UserCreated,
|
||||
details: {
|
||||
user_id: 3,
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
// If actor_id is the same as user_id:
|
||||
// "<name> activated their account."
|
||||
expect(screen.getByText("Jit Sso")).toBeInTheDocument();
|
||||
expect(screen.getByText(/activated their account\./)).toBeInTheDocument();
|
||||
});
|
||||
// // // // // //// // // // // //
|
||||
|
||||
it("renders a deleted_user type activity globally", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.UserDeleted,
|
||||
@@ -258,6 +280,10 @@ describe("Activity Feed", () => {
|
||||
expect(screen.getByText("newuser@example.com")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// // // // // // // // // // // //
|
||||
// changed_user_global_role tests
|
||||
// // // // // //// // // // // //
|
||||
|
||||
it("renders a changed_user_global_role type activity globally for premium users", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.UserChangedGlobalRole,
|
||||
@@ -287,7 +313,78 @@ describe("Activity Feed", () => {
|
||||
expect(forAllTeams).toBeNull();
|
||||
});
|
||||
|
||||
it("renders a changed_user_team_role type activity globally", () => {
|
||||
it("correctly renders a changed_user_global_role type activity for a premium SSO user created by JIT provisioning", () => {
|
||||
const activity = createMockActivity({
|
||||
actor_id: 3,
|
||||
type: ActivityType.UserChangedGlobalRole,
|
||||
details: {
|
||||
user_id: 3,
|
||||
user_email: "jit@sso.com",
|
||||
role: "observer",
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
// If actor_id is the same as user_id:
|
||||
// "<user_email> was assigned the <role> for all teams."
|
||||
expect(screen.getByText("jit@sso.com")).toBeInTheDocument();
|
||||
expect(screen.getByText(/was assigned the/)).toBeInTheDocument();
|
||||
expect(screen.getByText("observer")).toBeInTheDocument();
|
||||
expect(screen.getByText(/role for all teams./)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("correctly renders a changed_user_global_role type activity when changing an existing user's global role, premium", () => {
|
||||
const activity = createMockActivity({
|
||||
actor_id: 1,
|
||||
actor_full_name: "Ally Admin",
|
||||
type: ActivityType.UserChangedGlobalRole,
|
||||
details: {
|
||||
user_id: 3,
|
||||
user_email: "user@example.com",
|
||||
role: "maintainer",
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
// If actor_id is different from user_id on premium:
|
||||
// "<actor_full_name> changed <user_email> to <role> for all teams."
|
||||
expect(screen.getByText("Ally Admin")).toBeInTheDocument();
|
||||
expect(screen.getByText(/changed/)).toBeInTheDocument();
|
||||
expect(screen.getByText("user@example.com")).toBeInTheDocument();
|
||||
expect(screen.getByText(/to/)).toBeInTheDocument();
|
||||
expect(screen.getByText("maintainer")).toBeInTheDocument();
|
||||
expect(screen.getByText(/for all teams/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("correctly renders a changed_user_global_role type activity when changing an existing user's global role, free", () => {
|
||||
const activity = createMockActivity({
|
||||
actor_id: 1,
|
||||
actor_full_name: "Ally Admin",
|
||||
type: ActivityType.UserChangedGlobalRole,
|
||||
details: {
|
||||
user_id: 3,
|
||||
user_email: "user@example.com",
|
||||
role: "maintainer",
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} isPremiumTier={false} />);
|
||||
|
||||
// If actor_id is different from user_id on free:
|
||||
// "<actor_full_name> changed <user_email> to <role>."
|
||||
expect(screen.getByText("Ally Admin")).toBeInTheDocument();
|
||||
expect(screen.getByText("changed", { exact: false })).toBeInTheDocument();
|
||||
expect(screen.getByText("user@example.com")).toBeInTheDocument();
|
||||
expect(screen.getByText("to", { exact: false })).toBeInTheDocument();
|
||||
expect(screen.getByText("maintainer")).toBeInTheDocument();
|
||||
const forAllTeams = screen.queryByText("for all teams.");
|
||||
expect(forAllTeams).toBeNull();
|
||||
});
|
||||
|
||||
// // // // // // // // // // // //
|
||||
// changed_user_team_role tests
|
||||
// // // // // //// // // // // //
|
||||
|
||||
it("renders a changed_user_team_role type activity", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.UserChangedTeamRole,
|
||||
details: {
|
||||
@@ -304,6 +401,63 @@ describe("Activity Feed", () => {
|
||||
expect(screen.getByText("Test Team")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("correctly renders a changed_user_team_role type activity when a new SSO team user is created via JIT provisioning", () => {
|
||||
const activity = createMockActivity({
|
||||
actor_id: 1,
|
||||
actor_full_name: "Ally Admin",
|
||||
type: ActivityType.UserChangedTeamRole,
|
||||
details: {
|
||||
user_id: 1,
|
||||
user_email: "jit@sso.com",
|
||||
role: "maintainer",
|
||||
team_name: "Test Team",
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
// If actor_id is the same as user_id:
|
||||
// "<user_email> was assigned the <role> role for the <team_name> team."
|
||||
expect(screen.getByText("jit@sso.com")).toBeInTheDocument();
|
||||
expect(screen.getByText(/was assigned the/)).toBeInTheDocument();
|
||||
expect(screen.getByText("maintainer")).toBeInTheDocument();
|
||||
expect(screen.getByText(/role for the/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Test Team/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/team\./)).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText("Ally Admin")).toBeNull();
|
||||
const forAllTeams = screen.queryByText("for all teams.");
|
||||
expect(forAllTeams).toBeNull();
|
||||
});
|
||||
|
||||
it("correctly renders a changed_user_team_role type activity when changing an existing user's team role", () => {
|
||||
const activity = createMockActivity({
|
||||
actor_id: 1,
|
||||
actor_full_name: "Ally Admin",
|
||||
type: ActivityType.UserChangedTeamRole,
|
||||
details: {
|
||||
user_id: 3,
|
||||
user_email: "user@example.com",
|
||||
role: "maintainer",
|
||||
team_name: "Test Team",
|
||||
},
|
||||
});
|
||||
render(<ActivityItem activity={activity} isPremiumTier />);
|
||||
|
||||
// If actor_id is different from user_id:
|
||||
// "<actor_full_name> changed <user_email> to <role> for the <team_name> team."
|
||||
expect(screen.getByText("Ally Admin")).toBeInTheDocument();
|
||||
expect(screen.getByText(/changed/)).toBeInTheDocument();
|
||||
expect(screen.getByText("user@example.com")).toBeInTheDocument();
|
||||
expect(screen.getByText(/to/)).toBeInTheDocument();
|
||||
expect(screen.getByText("maintainer")).toBeInTheDocument();
|
||||
expect(screen.getByText(/for the/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Test Team/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/team\./)).toBeInTheDocument();
|
||||
expect(screen.queryByText("for all teams.")).toBeNull();
|
||||
});
|
||||
|
||||
// // // // // // // // // // // //
|
||||
|
||||
it("renders a deleted_user_team_role type activity globally", () => {
|
||||
const activity = createMockActivity({
|
||||
type: ActivityType.UserDeletedTeamRole,
|
||||
|
||||
@@ -173,7 +173,9 @@ const TAGGED_TEMPLATES = {
|
||||
);
|
||||
},
|
||||
userCreated: (activity: IActivity) => {
|
||||
return (
|
||||
return activity.actor_id === activity.details?.user_id ? (
|
||||
<>activated their account.</>
|
||||
) : (
|
||||
<>
|
||||
created a user <b> {activity.details?.user_email}</b>.
|
||||
</>
|
||||
@@ -187,10 +189,22 @@ const TAGGED_TEMPLATES = {
|
||||
);
|
||||
},
|
||||
userChangedGlobalRole: (activity: IActivity, isPremiumTier: boolean) => {
|
||||
const { actor_id } = activity;
|
||||
const { user_id, user_email, role } = activity.details || {};
|
||||
|
||||
if (actor_id === user_id) {
|
||||
// this is the case when SSO user is crated via JIT provisioning
|
||||
// should only be possible for premium tier, but check anyway
|
||||
return (
|
||||
<>
|
||||
was assigned the <b>{role}</b> role{isPremiumTier && " for all teams"}
|
||||
.
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
changed <b>{activity.details?.user_email}</b> to{" "}
|
||||
<b>{activity.details?.role}</b>
|
||||
changed <b>{user_email}</b> to <b>{activity.details?.role}</b>
|
||||
{isPremiumTier && " for all teams"}.
|
||||
</>
|
||||
);
|
||||
@@ -205,11 +219,22 @@ const TAGGED_TEMPLATES = {
|
||||
);
|
||||
},
|
||||
userChangedTeamRole: (activity: IActivity) => {
|
||||
const { actor_id } = activity;
|
||||
const { user_id, user_email, role, team_name } = activity.details || {};
|
||||
|
||||
const varText =
|
||||
actor_id === user_id ? (
|
||||
<>
|
||||
was assigned the <b>{role}</b> role
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
changed <b>{user_email}</b> to <b>{role}</b>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
changed <b>{activity.details?.user_email}</b> to{" "}
|
||||
<b>{activity.details?.role}</b> for the{" "}
|
||||
<b>{activity.details?.team_name}</b> team.
|
||||
{varText} for the <b>{team_name}</b> team.
|
||||
</>
|
||||
);
|
||||
},
|
||||
@@ -751,6 +776,19 @@ const ActivityItem = ({
|
||||
const indicatePremiumFeature =
|
||||
isSandboxMode && PREMIUM_ACTIVITIES.has(activity.type);
|
||||
|
||||
const renderActivityPrefix = () => {
|
||||
if (activity.type === ActivityType.UserLoggedIn) {
|
||||
return <b>{activity.actor_email} </b>;
|
||||
}
|
||||
if (
|
||||
(activity.type === ActivityType.UserChangedGlobalRole ||
|
||||
activity.type === ActivityType.UserChangedTeamRole) &&
|
||||
activity.actor_id === activity.details?.user_id
|
||||
) {
|
||||
return <b>{activity.details?.user_email} </b>;
|
||||
}
|
||||
return <b>{activity.actor_full_name} </b>;
|
||||
};
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<Avatar
|
||||
@@ -763,11 +801,7 @@ const ActivityItem = ({
|
||||
<div className={"activity-details"}>
|
||||
{indicatePremiumFeature && <PremiumFeatureIconWithTooltip />}
|
||||
<span className={`${baseClass}__details-topline`}>
|
||||
{activity.type === ActivityType.UserLoggedIn ? (
|
||||
<b>{activity.actor_email} </b>
|
||||
) : (
|
||||
<b>{activity.actor_full_name} </b>
|
||||
)}
|
||||
{renderActivityPrefix()}
|
||||
{getDetail(activity, isPremiumTier, onDetailsClick)}
|
||||
</span>
|
||||
<br />
|
||||
|
||||
Reference in New Issue
Block a user