[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:
Tessa Thornton
2019-08-20 13:15:59 -04:00
committed by GitHub
co-authored by Wyatt Johnson
parent add5224338
commit 7809cd3d68
17 changed files with 358 additions and 18 deletions
@@ -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.
"""
+11 -3
View File
@@ -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)
);
}
/**
+9 -1
View File
@@ -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,