Implement ingress

This commit is contained in:
fflorent
2024-09-03 23:53:25 +02:00
parent 98f50d3c81
commit c38107aadc
4 changed files with 80 additions and 14 deletions

View File

@@ -1,5 +1,7 @@
import { normalizeEmail } from "app/common/emails";
import { UserProfile } from "app/common/LoginSessionAPI";
import { User } from "app/gen-server/entity/User.js";
import { SCIMMY } from "scimmy-routers";
import SCIMMY from "scimmy";
/**
* Converts a user from your database to a SCIMMY user
@@ -8,7 +10,7 @@ export function toSCIMMYUser(user: User) {
if (!user.logins) {
throw new Error("User must have at least one login");
}
const preferredLanguage = user.options?.locale ?? "en";
const locale = user.options?.locale ?? "en";
return new SCIMMY.Schemas.User({
id: String(user.id),
userName: user.loginEmail,
@@ -16,16 +18,29 @@ export function toSCIMMYUser(user: User) {
name: {
formatted: user.name,
},
preferredLanguage,
locale: preferredLanguage, // Assume locale is the same as preferredLanguage
locale,
preferredLanguage: locale, // Assume preferredLanguage is the same as locale
photos: user.picture ? [{
value: user.picture,
type: "photo",
primary: true
}] : undefined,
emails: [{
value: user.loginEmail,
value: user.logins[0].displayEmail,
primary: true,
}],
});
}
export function toUserProfile(scimUser: any, existingUser?: User): UserProfile {
const emailValue = scimUser.emails?.[0]?.value;
if (emailValue && normalizeEmail(emailValue) !== normalizeEmail(scimUser.userName)) {
throw new SCIMMY.Types.Error(400, 'invalidValue', 'Email and userName must be the same');
}
return {
name: scimUser.displayName ?? existingUser?.name,
picture: scimUser.photos?.[0]?.value,
locale: scimUser.locale,
email: emailValue ?? scimUser.userName ?? existingUser?.loginEmail,
};
}

View File

@@ -4,12 +4,13 @@ import SCIMMY from "scimmy";
import SCIMMYRouters from "scimmy-routers";
import { RequestWithLogin } from '../../Authorizer';
import { InstallAdmin } from '../../InstallAdmin';
import { toSCIMMYUser } from './ScimUserUtils';
import { toSCIMMYUser, toUserProfile } from './ScimUserUtils';
import { ApiError } from 'app/common/ApiError';
const WHITELISTED_PATHS_FOR_NON_ADMINS = [ "/Me", "/Schemas", "/ResourceTypes", "/ServiceProviderConfig" ];
async function isAuthorizedAction(mreq: RequestWithLogin, installAdmin: InstallAdmin): Promise<boolean> {
const isAdmin = await installAdmin.isAdminReq(mreq)
const isAdmin = await installAdmin.isAdminReq(mreq);
const isScimUser = Boolean(process.env.GRIST_SCIM_EMAIL && mreq.user?.loginEmail === process.env.GRIST_SCIM_EMAIL);
return isAdmin || isScimUser || WHITELISTED_PATHS_FOR_NON_ADMINS.includes(mreq.path);
}
@@ -30,18 +31,39 @@ const buildScimRouterv2 = (dbManager: HomeDBManager, installAdmin: InstallAdmin)
const scimmyUsers = (await dbManager.getUsers()).map(user => toSCIMMYUser(user));
return filter ? filter.match(scimmyUsers) : scimmyUsers;
},
ingress: async (resource: any) => {
ingress: async (resource: any, data: any) => {
try {
const { id } = resource;
if (id) {
return null;
const updatedUser = await dbManager.overrideUser(id, toUserProfile(data));
return toSCIMMYUser(updatedUser);
}
return [];
const userProfileToInsert = toUserProfile(data);
const maybeExistingUser = await dbManager.getExistingUserByLogin(userProfileToInsert.email);
if (maybeExistingUser !== undefined) {
throw new SCIMMY.Types.Error(409, 'uniqueness', 'An existing user with the passed email exist.');
}
const newUser = await dbManager.getUserByLoginWithRetry(userProfileToInsert.email, {
profile: userProfileToInsert
});
return toSCIMMYUser(newUser!);
} catch (ex) {
// FIXME: remove this
if (Math.random() > 1) {
return null;
if (ex instanceof ApiError) {
if (ex.status === 409) {
throw new SCIMMY.Types.Error(ex.status, 'uniqueness', ex.message);
}
throw new SCIMMY.Types.Error(ex.status, null!, ex.message);
}
if (ex.code?.startsWith('SQLITE')) {
switch (ex.code) {
case 'SQLITE_CONSTRAINT':
throw new SCIMMY.Types.Error(409, 'uniqueness', ex.message);
default:
throw new SCIMMY.Types.Error(500, 'serverError', ex.message);
}
}
throw ex;
}
},