mirror of
https://github.com/wassname/talk.git
synced 2026-07-28 11:27:05 +08:00
[CORL-476] Add badges to user from SSO token (#2470)
* fix: bug in lookup not checking object properly before accessing * fix: Recursive types not handling optional arrays * add user badges component * create user badges from sso token * update badges type * revert src/core/client/embed/index.html * remove duplicated line * add user badges component * create user badges from sso token * revert src/core/client/embed/index.html * remove duplicated line * fix types * fix tests and snaps * add user badges to user drawer * update snaps * update readme * [CORL-476] add user role from SSO token (#2475) * add role from token * use joi to validate role values Co-Authored-By: Wyatt Johnson <wyattjoh@gmail.com> * simplify sso.role validation * add test for invalid role in sso token
This commit is contained in:
co-authored by
Wyatt Johnson
parent
add5224338
commit
7809cd3d68
@@ -6,7 +6,14 @@ import { validate } from "coral-server/app/request/body";
|
||||
|
||||
describe("isSSOToken", () => {
|
||||
it("understands valid sso tokens", () => {
|
||||
const token = { user: { id: "id", email: "email", username: "username" } };
|
||||
const token = {
|
||||
user: {
|
||||
id: "id",
|
||||
email: "email",
|
||||
username: "username",
|
||||
role: "COMMENTER",
|
||||
},
|
||||
};
|
||||
expect(isSSOToken(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -20,6 +27,15 @@ describe("isSSOToken", () => {
|
||||
expect(
|
||||
isSSOToken({ user: { email: "email", username: "username" } } as object)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isSSOToken({
|
||||
user: {
|
||||
email: "email",
|
||||
username: "username",
|
||||
role: "SUPERADMIN",
|
||||
},
|
||||
} as object)
|
||||
).toBeFalsy();
|
||||
expect(isSSOToken({})).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,8 @@ export interface SSOUserProfile {
|
||||
id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
badges?: string[];
|
||||
role?: GQLUSER_ROLE;
|
||||
}
|
||||
|
||||
export interface SSOToken {
|
||||
@@ -51,13 +53,17 @@ export function isSSOToken(token: SSOToken | object): token is SSOToken {
|
||||
return isNil(error);
|
||||
}
|
||||
|
||||
export const SSOUserProfileSchema = Joi.object().keys({
|
||||
id: Joi.string().required(),
|
||||
email: Joi.string()
|
||||
.lowercase()
|
||||
.required(),
|
||||
username: Joi.string().required(),
|
||||
});
|
||||
export const SSOUserProfileSchema = Joi.object()
|
||||
.keys({
|
||||
id: Joi.string().required(),
|
||||
email: Joi.string()
|
||||
.lowercase()
|
||||
.required(),
|
||||
username: Joi.string().required(),
|
||||
badges: Joi.array().items(Joi.string()),
|
||||
role: Joi.string().only(Object.values(GQLUSER_ROLE)),
|
||||
})
|
||||
.optionalKeys(["badges", "role"]);
|
||||
|
||||
export const SSOTokenSchema = Joi.object()
|
||||
.keys({
|
||||
@@ -88,7 +94,7 @@ export async function findOrCreateSSOUser(
|
||||
const {
|
||||
jti,
|
||||
exp,
|
||||
user: { id, email, username },
|
||||
user: { id, email, username, badges, role },
|
||||
iat,
|
||||
} = decodedToken;
|
||||
|
||||
@@ -127,7 +133,8 @@ export async function findOrCreateSSOUser(
|
||||
{
|
||||
id,
|
||||
username,
|
||||
role: GQLUSER_ROLE.COMMENTER,
|
||||
role: role || GQLUSER_ROLE.COMMENTER,
|
||||
badges,
|
||||
email,
|
||||
profiles: [profile],
|
||||
},
|
||||
@@ -144,7 +151,7 @@ export async function findOrCreateSSOUser(
|
||||
mongo,
|
||||
tenant.id,
|
||||
user.id,
|
||||
{ email, username },
|
||||
{ email, username, badges, role: role || user.role },
|
||||
lastIssuedAt
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1525,6 +1525,10 @@ type User {
|
||||
"""
|
||||
avatar: String
|
||||
|
||||
"""
|
||||
badges are user display badges
|
||||
"""
|
||||
badges: [String!]
|
||||
"""
|
||||
email is the current email address for the User.
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { isEqual } from "lodash";
|
||||
|
||||
import { GQLUSER_ROLE } from "coral-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import { SSOUserProfile } from "coral-server/app/middleware/passport/strategies/verifiers/sso";
|
||||
import { STAFF_ROLES } from "./constants";
|
||||
import { LocalProfile, SSOProfile, User } from "./user";
|
||||
|
||||
@@ -22,10 +25,15 @@ export function getSSOProfile(user: Pick<User, "profiles">) {
|
||||
}
|
||||
|
||||
export function needsSSOUpdate(
|
||||
token: Pick<User, "email" | "username">,
|
||||
user: Pick<User, "email" | "username">
|
||||
token: SSOUserProfile,
|
||||
user: Pick<User, "email" | "username" | "badges" | "role">
|
||||
) {
|
||||
return user.email !== token.email || user.username !== token.username;
|
||||
return (
|
||||
user.email !== token.email ||
|
||||
user.username !== token.username ||
|
||||
(token.role && user.role !== token.role) ||
|
||||
!isEqual(user.badges, token.badges)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -292,6 +292,12 @@ export interface User extends TenantResource {
|
||||
*/
|
||||
email?: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* badges are user display badges
|
||||
*/
|
||||
badges?: string[];
|
||||
|
||||
/**
|
||||
* emailVerificationID is used to store state regarding the verification state
|
||||
* of an email address to prevent replay attacks.
|
||||
@@ -668,6 +674,8 @@ export async function updateUserPassword(
|
||||
export interface UpdateUserInput {
|
||||
email?: string;
|
||||
username?: string;
|
||||
badges?: string[];
|
||||
role?: GQLUSER_ROLE;
|
||||
}
|
||||
|
||||
export async function updateUserFromSSO(
|
||||
@@ -677,7 +685,7 @@ export async function updateUserFromSSO(
|
||||
update: UpdateUserInput,
|
||||
lastIssuedAt: Date
|
||||
) {
|
||||
// Update the user with the new password.
|
||||
// Update the user with the new properties.
|
||||
const result = await collection(mongo).findOneAndUpdate(
|
||||
{
|
||||
tenantID,
|
||||
|
||||
Reference in New Issue
Block a user