mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-21 11:28:43 +00:00
feat(infisical-pg): test first milestone one flow
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { FastifyRequest } from "fastify";
|
||||
import fp from "fastify-plugin";
|
||||
import jwt, { JwtPayload } from "jsonwebtoken";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -27,7 +28,7 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
|
||||
case AuthMode.SERVICE_ACCESS_TOKEN:
|
||||
return { authMode: AuthMode.SERVICE_ACCESS_TOKEN, token: decodedToken } as const;
|
||||
default:
|
||||
throw new UnauthorizedError({ name: "Invalid token type" });
|
||||
return { authMode: null, token: null } as const;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,13 +42,13 @@ const getJwtIdentity = async (server: FastifyZodProvider, token: AuthModeJwtToke
|
||||
if (token.accessVersion !== session.accessVersion)
|
||||
throw new UnauthorizedError({ name: "Stale session" });
|
||||
|
||||
const user = await server.store.user.getUserById(session.userId);
|
||||
const user = await server.store.user.findById(session.userId);
|
||||
if (!user || !user.isAccepted) throw new UnauthorizedError({ name: "Token user not found" });
|
||||
|
||||
return user;
|
||||
return { user, tokenVersionId: token.tokenVersionId };
|
||||
};
|
||||
|
||||
export const injectIdentity = (server: FastifyZodProvider) => {
|
||||
export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
||||
server.decorateRequest("auth", null);
|
||||
server.addHook("onRequest", async (req) => {
|
||||
const appCfg = getConfig();
|
||||
@@ -56,8 +57,11 @@ export const injectIdentity = (server: FastifyZodProvider) => {
|
||||
// TODO(akhilmhdh-pg): fill in rest of auth mode logic
|
||||
switch (authMode) {
|
||||
case AuthMode.JWT: {
|
||||
const user = await getJwtIdentity(server, token as AuthModeJwtTokenPayload);
|
||||
req.auth = { authMode: AuthMode.JWT, user, userId: user.id };
|
||||
const { user, tokenVersionId } = await getJwtIdentity(
|
||||
server,
|
||||
token as AuthModeJwtTokenPayload
|
||||
);
|
||||
req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId };
|
||||
break;
|
||||
}
|
||||
case AuthMode.SERVICE_TOKEN:
|
||||
@@ -72,4 +76,4 @@ export const injectIdentity = (server: FastifyZodProvider) => {
|
||||
throw new UnauthorizedError({ name: "Unknown token strategy" });
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { FastifyRequest } from "fastify";
|
||||
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
|
||||
export const verifySuperAdmin = async <T extends FastifyRequest>(req: T) => {
|
||||
if (!req.auth.user.superAdmin)
|
||||
throw new UnauthorizedError({
|
||||
name: "Unauthorized access",
|
||||
message: "Requires superadmin access"
|
||||
});
|
||||
};
|
||||
@@ -5,7 +5,7 @@ import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const verifyAuth =
|
||||
<T extends FastifyRequest>(authStrats: AuthMode[]) =>
|
||||
(req: T) => {
|
||||
async (req: T) => {
|
||||
if (!Array.isArray(authStrats)) throw new Error("Auth strategy must be array");
|
||||
if (!req.auth)
|
||||
throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" });
|
||||
|
||||
@@ -1,47 +1,142 @@
|
||||
import { Knex } from "knex";
|
||||
import { z } from "zod";
|
||||
|
||||
import { registerV1EERoutes } from "@app/ee/routes/v1";
|
||||
import { permissionDalFactory } from "@app/ee/services/permission/permission-dal";
|
||||
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { apiKeyDalFactory } from "@app/services/api-key/api-key-dal";
|
||||
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||
import { authDalFactory } from "@app/services/auth/auth-dal";
|
||||
import { authLoginServiceFactory } from "@app/services/auth/auth-login-service";
|
||||
import { authPaswordServiceFactory } from "@app/services/auth/auth-password-service";
|
||||
import { authSignupServiceFactory } from "@app/services/auth/auth-signup-service";
|
||||
import { incidentContactDalFactory } from "@app/services/org/incident-contacts-dal";
|
||||
import { orgDalFactory } from "@app/services/org/org-dal";
|
||||
import { orgRoleDalFactory } from "@app/services/org/org-role-dal";
|
||||
import { orgRoleServiceFactory } from "@app/services/org/org-role-service";
|
||||
import { orgServiceFactory } from "@app/services/org/org-service";
|
||||
import { serverCfgDalFactory } from "@app/services/server-cfg/server-cfg-dal";
|
||||
import { serverCfgServiceFactory } from "@app/services/server-cfg/server-cfg-service";
|
||||
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
import { tokenDalFactory } from "@app/services/token/token-dal";
|
||||
import { tokenServiceFactory } from "@app/services/token/token-service";
|
||||
import { userDalFactory } from "@app/services/user/user-dal";
|
||||
import { userServiceFactory } from "@app/services/user/user-service";
|
||||
|
||||
import { injectIdentity } from "../plugins/auth/inject-identity";
|
||||
import { registerV1Routes } from "./v1";
|
||||
import { registerV2Routes } from "./v2";
|
||||
import { registerV3Routes } from "./v3";
|
||||
import { injectIdentity } from "../plugins/auth/inject-identity";
|
||||
|
||||
export const registerRoutes = async (
|
||||
server: FastifyZodProvider,
|
||||
{ db, smtp }: { db: Knex; smtp: TSmtpService }
|
||||
{ db, smtp: smtpService }: { db: Knex; smtp: TSmtpService }
|
||||
) => {
|
||||
// db layers
|
||||
const userDal = userDalFactory(db);
|
||||
const authDal = authDalFactory(db);
|
||||
const authTokenDal = tokenDalFactory(db);
|
||||
const orgDal = orgDalFactory(db);
|
||||
const incidentContactDal = incidentContactDalFactory(db);
|
||||
const orgRoleDal = orgRoleDalFactory(db);
|
||||
const serverCfgDal = serverCfgDalFactory(db);
|
||||
const apiKeyDal = apiKeyDalFactory(db);
|
||||
|
||||
// ee db layer ops
|
||||
const permissionDal = permissionDalFactory(db);
|
||||
|
||||
// ee services
|
||||
const permissionService = permissionServiceFactory({ permissionDal });
|
||||
|
||||
// service layers
|
||||
const tokenService = tokenServiceFactory({ tokenDal: authTokenDal });
|
||||
const loginService = authLoginServiceFactory({ authDal, smtpService: smtp, tokenService });
|
||||
const passwordService = authPaswordServiceFactory({ tokenService, smtpService: smtp, authDal });
|
||||
const signupService = authSignupServiceFactory({ tokenService, smtpService: smtp, authDal });
|
||||
const userService = userServiceFactory({ userDal });
|
||||
const loginService = authLoginServiceFactory({ userDal, smtpService, tokenService });
|
||||
const passwordService = authPaswordServiceFactory({
|
||||
tokenService,
|
||||
smtpService,
|
||||
authDal,
|
||||
userDal
|
||||
});
|
||||
const orgService = orgServiceFactory({
|
||||
orgRoleDal,
|
||||
permissionService,
|
||||
orgDal,
|
||||
incidentContactDal,
|
||||
tokenService,
|
||||
smtpService,
|
||||
userDal
|
||||
});
|
||||
const signupService = authSignupServiceFactory({
|
||||
tokenService,
|
||||
smtpService,
|
||||
authDal,
|
||||
userDal,
|
||||
orgDal,
|
||||
orgService
|
||||
});
|
||||
const orgRoleService = orgRoleServiceFactory({ permissionService, orgRoleDal });
|
||||
const serverCfgService = serverCfgServiceFactory({
|
||||
userDal,
|
||||
authService: loginService,
|
||||
serverCfgDal
|
||||
});
|
||||
const apiKeyService = apiKeyServiceFactory({ apiKeyDal });
|
||||
|
||||
await serverCfgService.initServerCfg();
|
||||
// inject all services
|
||||
server.decorate("services", {
|
||||
login: loginService,
|
||||
password: passwordService,
|
||||
signup: signupService
|
||||
signup: signupService,
|
||||
user: userService,
|
||||
permission: permissionService,
|
||||
org: orgService,
|
||||
orgRole: orgRoleService,
|
||||
serverCfg: serverCfgService,
|
||||
apiKey: apiKeyService,
|
||||
authToken: tokenService
|
||||
} as FastifyZodProvider["services"]);
|
||||
|
||||
server.decorate("store", {
|
||||
user: authDal
|
||||
user: userDal
|
||||
} as FastifyZodProvider["store"]);
|
||||
|
||||
await server.register(injectIdentity);
|
||||
|
||||
server.route({
|
||||
url: "/status",
|
||||
method: "GET",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
date: z.date(),
|
||||
message: z.literal("Ok"),
|
||||
emailConfigured: z.boolean().optional(),
|
||||
inviteOnlySignup: z.boolean().optional(),
|
||||
redisConfigured: z.boolean().optional(),
|
||||
secretScanningConfigured: z.boolean().optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: () => {
|
||||
const appCfg = getConfig();
|
||||
return {
|
||||
date: new Date(),
|
||||
message: "Ok" as const,
|
||||
emailConfigured: appCfg.isSmtpConfigured,
|
||||
inviteOnlySignup: false,
|
||||
redisConfigured: false,
|
||||
secretScanningConfigured: false
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// register routes for v1
|
||||
await server.register(registerV1Routes, { prefix: "/v1" });
|
||||
await server.register(registerV2Routes, { prefix: "/v2" });
|
||||
await server.register(registerV3Routes, { prefix: "/v3" });
|
||||
|
||||
await server.register(registerV1EERoutes, { prefix: "/ee/v1" });
|
||||
};
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ServerConfigSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "GET",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
config: ServerConfigSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: () => {
|
||||
const config = server.services.serverCfg.getServerCfg();
|
||||
return { config };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "PATCH",
|
||||
schema: {
|
||||
body: z.object({
|
||||
allowSignUp: z.boolean().optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
config: ServerConfigSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
preHandler: (req) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req);
|
||||
verifySuperAdmin(req);
|
||||
},
|
||||
handler: async (req) => {
|
||||
const config = await server.services.serverCfg.updateServerCfg(req.body);
|
||||
return { config };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/signup",
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().email().trim(),
|
||||
firstName: z.string().trim(),
|
||||
lastName: z.string().trim().optional(),
|
||||
protectedKey: z.string().trim(),
|
||||
protectedKeyIV: z.string().trim(),
|
||||
protectedKeyTag: z.string().trim(),
|
||||
publicKey: z.string().trim(),
|
||||
encryptedPrivateKey: z.string().trim(),
|
||||
encryptedPrivateKeyIV: z.string().trim(),
|
||||
encryptedPrivateKeyTag: z.string().trim(),
|
||||
salt: z.string().trim(),
|
||||
verifier: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
user: UsersSchema,
|
||||
token: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req, res) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = server.services.serverCfg.getServerCfg();
|
||||
if (serverCfg.initialized)
|
||||
throw new UnauthorizedError({ name: "Admin sign up", message: "Admin has been created" });
|
||||
const { user, token } = await server.services.serverCfg.adminSignUp({
|
||||
...req.body,
|
||||
ip: req.realIp,
|
||||
userAgent: req.headers["user-agent"] || ""
|
||||
});
|
||||
|
||||
res.setCookie("jid", token.refresh, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: appCfg.HTTPS_ENABLED
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Successfully set up admin account",
|
||||
user: user.user,
|
||||
token: token.access
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import {
|
||||
AuthMode,
|
||||
AuthModeRefreshJwtTokenPayload,
|
||||
AuthTokenType
|
||||
} from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/logout",
|
||||
method: "POST",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req, res) => {
|
||||
const appCfg = getConfig();
|
||||
await server.services.login.logout(req.auth.userId, req.auth.tokenVersionId);
|
||||
res.cookie("jid", "", {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: appCfg.HTTPS_ENABLED
|
||||
});
|
||||
return { message: "Successfully logged out" };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/token",
|
||||
method: "POST",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
token: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const refreshToken = req.cookies.jid;
|
||||
const appCfg = getConfig();
|
||||
if (!refreshToken)
|
||||
throw new BadRequestError({
|
||||
name: "Auth token route",
|
||||
message: "Failed to find refresh token"
|
||||
});
|
||||
|
||||
const decodedToken = jwt.verify(
|
||||
refreshToken,
|
||||
appCfg.JWT_AUTH_SECRET
|
||||
) as AuthModeRefreshJwtTokenPayload;
|
||||
if (decodedToken.authTokenType !== AuthTokenType.REFRESH_TOKEN)
|
||||
throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" });
|
||||
|
||||
const tokenVersion = await server.services.authToken.getUserTokenSessionById(
|
||||
decodedToken.tokenVersionId,
|
||||
decodedToken.userId
|
||||
);
|
||||
if (!tokenVersion)
|
||||
throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" });
|
||||
|
||||
if (decodedToken.refreshVersion !== tokenVersion.refreshVersion)
|
||||
throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" });
|
||||
|
||||
const token = jwt.sign(
|
||||
{
|
||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||
userId: decodedToken.userId,
|
||||
tokenVersionId: tokenVersion.id,
|
||||
accessVersion: tokenVersion.accessVersion
|
||||
},
|
||||
appCfg.JWT_AUTH_SECRET,
|
||||
{ expiresIn: appCfg.JWT_AUTH_LIFETIME }
|
||||
);
|
||||
|
||||
return { token };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,17 @@
|
||||
import { registerAdminRouter } from "./admin";
|
||||
import { registerAuthRoutes } from "./auth";
|
||||
import { registerInviteOrgRouter } from "./invite-org";
|
||||
import { registerOrgRouter } from "./organization-router";
|
||||
import { registerPasswordRouter } from "./password-router";
|
||||
import { registerUserActionRouter } from "./user-action-router";
|
||||
import { registerUserRouter } from "./user-router";
|
||||
|
||||
export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerAuthRoutes, { prefix: "/auth" });
|
||||
await server.register(registerPasswordRouter, { prefix: "/password" });
|
||||
await server.register(registerOrgRouter, { prefix: "/organization" });
|
||||
await server.register(registerAdminRouter, { prefix: "/admin" });
|
||||
await server.register(registerUserRouter, { prefix: "/user" });
|
||||
await server.register(registerInviteOrgRouter, { prefix: "/invite-org" });
|
||||
await server.register(registerUserActionRouter, { prefix: "/user-action" });
|
||||
};
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UsersSchema } from "@app/db/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/signup",
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
inviteeEmail: z.string().trim().email(),
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
completeInviteLink: z.string().optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const completeInviteLink = await server.services.org.inviteUserToOrganization({
|
||||
orgId: req.body.organizationId,
|
||||
userId: req.auth.userId,
|
||||
inviteeEmail: req.body.inviteeEmail
|
||||
});
|
||||
|
||||
return {
|
||||
completeInviteLink,
|
||||
message: `Send an invite link to ${req.body.inviteeEmail}`
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/verify",
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().trim().email(),
|
||||
organizationId: z.string().trim(),
|
||||
code: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
token: z.string().optional(),
|
||||
user: UsersSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { user, token } = await server.services.org.verifyUserToOrg({
|
||||
orgId: req.body.organizationId,
|
||||
code: req.body.code,
|
||||
email: req.body.email
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Successfully verified email",
|
||||
user,
|
||||
token
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
IncidentContactsSchema,
|
||||
OrganizationsSchema,
|
||||
OrgMembershipsSchema,
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
organizations: OrganizationsSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const organizations = await server.services.org.findAllOrganizationOfUser(req.auth.userId);
|
||||
return { organizations };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId",
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
organization: OrganizationsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const organization = await server.services.org.findOrganizationById(
|
||||
req.auth.userId,
|
||||
req.params.organizationId
|
||||
);
|
||||
return { organization };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/users",
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
users: OrgMembershipsSchema.merge(
|
||||
z.object({
|
||||
user: UsersSchema.pick({
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
id: true
|
||||
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true }))
|
||||
})
|
||||
)
|
||||
.omit({ createdAt: true, updatedAt: true })
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const users = await server.services.org.findAllOrgMembers(
|
||||
req.auth.userId,
|
||||
req.params.organizationId
|
||||
);
|
||||
return { users };
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(akhilmhdh-pg): missing my-workspace list
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:organizationId/name",
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
body: z.object({ name: z.string().trim() }),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
organization: OrganizationsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const organization = await server.services.org.updateOrgName(
|
||||
req.auth.userId,
|
||||
req.params.organizationId,
|
||||
req.body.name
|
||||
);
|
||||
return {
|
||||
message: "Successfully changed organization name",
|
||||
organization
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/incidentContactOrg",
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
200: z.object({
|
||||
incidentContactsOrg: IncidentContactsSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const incidentContactsOrg = await req.server.services.org.findIncidentContacts(
|
||||
req.auth.userId,
|
||||
req.params.organizationId
|
||||
);
|
||||
return { incidentContactsOrg };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:organizationId/incidentContactOrg",
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
body: z.object({ email: z.string().email().trim() }),
|
||||
response: {
|
||||
200: z.object({
|
||||
incidentContactsOrg: IncidentContactsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const incidentContactsOrg = await req.server.services.org.createIncidentContact(
|
||||
req.auth.userId,
|
||||
req.params.organizationId,
|
||||
req.body.email
|
||||
);
|
||||
return { incidentContactsOrg };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:organizationId/incidentContactOrg/:incidentContactId",
|
||||
schema: {
|
||||
// TODO(akhilmhdh-pg): change accept id instead of email
|
||||
params: z.object({ organizationId: z.string().trim(), incidentContactId: z.string().trim() }),
|
||||
response: {
|
||||
200: z.object({
|
||||
incidentContactsOrg: IncidentContactsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const incidentContactsOrg = await req.server.services.org.deleteIncidentContact(
|
||||
req.auth.userId,
|
||||
req.params.organizationId,
|
||||
req.params.incidentContactId
|
||||
);
|
||||
return { incidentContactsOrg };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UserActionsSchema } from "@app/db/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerUserActionRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
action: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
userAction: UserActionsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const userAction = await server.services.user.createUserAction(
|
||||
req.auth.userId,
|
||||
req.body.action
|
||||
);
|
||||
return { userAction, message: "Successfully recorded user action" };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
action: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
userAction: UserActionsSchema.optional().nullable()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const userAction = await server.services.user.getUserAction(
|
||||
req.auth.userId,
|
||||
req.query.action
|
||||
);
|
||||
return { userAction };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UsersSchema } from "@app/db/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.getMe(req.auth.userId);
|
||||
return { user };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
import { registerMfaRouter } from "./mfa-router";
|
||||
import { registerUserRouter } from "./user-router";
|
||||
|
||||
export const registerV2Routes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerMfaRouter, { prefix: "/auth" });
|
||||
await server.register(registerUserRouter, { prefix: "/users" });
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN)
|
||||
throw new Error("Unauthorized access");
|
||||
|
||||
const user = await server.store.user.getUserById(decodedToken.userId);
|
||||
const user = await server.store.user.findById(decodedToken.userId);
|
||||
if (!user) throw new Error("User not found");
|
||||
req.mfa = { userId: user.id, user };
|
||||
});
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
OrganizationsSchema,
|
||||
OrgMembershipsSchema,
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/memberships",
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
users: OrgMembershipsSchema.merge(
|
||||
z.object({
|
||||
user: UsersSchema.pick({
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
id: true
|
||||
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true }))
|
||||
})
|
||||
)
|
||||
.omit({ createdAt: true, updatedAt: true })
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const users = await server.services.org.findAllOrgMembers(
|
||||
req.auth.userId,
|
||||
req.params.organizationId
|
||||
);
|
||||
return { users };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:organizationId/memberships/:membershipId",
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
|
||||
body: z.object({
|
||||
role: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
membership: OrgMembershipsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const membership = await server.services.org.updateOrgMembership({
|
||||
userId: req.auth.userId,
|
||||
role: req.body.role,
|
||||
orgId: req.params.organizationId,
|
||||
membershipId: req.params.membershipId
|
||||
});
|
||||
return { membership };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:organizationId/memberships/:membershipId",
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
|
||||
response: {
|
||||
200: z.object({
|
||||
membership: OrgMembershipsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const membership = await server.services.org.deleteOrgMembership({
|
||||
userId: req.auth.userId,
|
||||
orgId: req.params.organizationId,
|
||||
membershipId: req.params.membershipId
|
||||
});
|
||||
return { membership };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
schema: {
|
||||
body: z.object({
|
||||
name: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
organization: OrganizationsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const organization = await server.services.org.createOrganization(
|
||||
req.auth.userId,
|
||||
req.body.name
|
||||
);
|
||||
return { organization };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:organizationId",
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
organization: OrganizationsSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const organization = await server.services.org.deleteOrganizationById(
|
||||
req.auth.userId,
|
||||
req.params.organizationId
|
||||
);
|
||||
return { organization };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,225 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AuthTokenSessionsSchema, OrganizationsSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { ApiKeysSchema } from "@app/db/schemas/api-keys";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMethod, AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/me/mfa",
|
||||
method: "PATCH",
|
||||
schema: {
|
||||
body: z.object({
|
||||
isMfaEnabled: z.boolean()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.toggleUserMfa(req.auth.userId, req.body.isMfaEnabled);
|
||||
return { user };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/me/name",
|
||||
method: "PATCH",
|
||||
schema: {
|
||||
body: z.object({
|
||||
firstName: z.string().trim(),
|
||||
lastName: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.updateUserName(
|
||||
req.auth.userId,
|
||||
req.body.firstName,
|
||||
req.body.lastName
|
||||
);
|
||||
return { user };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/me/auth-methods",
|
||||
method: "PUT",
|
||||
schema: {
|
||||
body: z.object({
|
||||
authMethods: z.nativeEnum(AuthMethod).array().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.updateAuthMethods(
|
||||
req.auth.userId,
|
||||
req.body.authMethods
|
||||
);
|
||||
return { user };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/organizations",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
organizations: OrganizationsSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const organizations = await server.services.org.findAllOrganizationOfUser(req.auth.userId);
|
||||
return { organizations };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/api-keys",
|
||||
schema: {
|
||||
response: {
|
||||
200: ApiKeysSchema.omit({ secretHash: true }).array()
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const apiKeys = await server.services.apiKey.getMyApiKeys(req.auth.userId);
|
||||
return apiKeys;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/me/api-keys",
|
||||
schema: {
|
||||
body: z.object({
|
||||
name: z.string().trim(),
|
||||
expiresIn: z.number()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
apiKey: z.string(),
|
||||
apiKeyData: ApiKeysSchema.omit({ secretHash: true })
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const apiKeys = await server.services.apiKey.createApiKey(
|
||||
req.auth.userId,
|
||||
req.body.name,
|
||||
req.body.expiresIn
|
||||
);
|
||||
return apiKeys;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/me/api-keys/:apiKeyDataId",
|
||||
schema: {
|
||||
params: z.object({
|
||||
apiKeyDataId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
apiKeyData: ApiKeysSchema.omit({ secretHash: true })
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const apiKeyData = await server.services.apiKey.deleteApiKey(
|
||||
req.auth.userId,
|
||||
req.params.apiKeyDataId
|
||||
);
|
||||
return { apiKeyData };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/sessions",
|
||||
schema: {
|
||||
response: {
|
||||
200: AuthTokenSessionsSchema.array()
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const sessions = await server.services.authToken.getTokenSessionByUser(req.auth.userId);
|
||||
return sessions;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/me/sessions",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
await server.services.authToken.revokeAllMySessions(req.auth.userId);
|
||||
return {
|
||||
message: "Successfully revoked all sessions"
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.getMe(req.auth.userId);
|
||||
return { user };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/me",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const user = await server.services.user.deleteMe(req.auth.userId);
|
||||
return { user };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
import { registerLoginRouter } from "./login-router";
|
||||
import { registerSignupRouter } from "./signup-router";
|
||||
import { registerUserRouter } from "./user-router";
|
||||
|
||||
export const registerV3Routes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerSignupRouter, { prefix: "/signup" });
|
||||
await server.register(registerLoginRouter, { prefix: "/auth" });
|
||||
await server.register(registerUserRouter, { prefix: "/users" });
|
||||
};
|
||||
|
||||
@@ -100,4 +100,54 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
|
||||
return { message: "Successfully set up account", user, token: accessToken };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/complete-account/invite",
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().email().trim(),
|
||||
firstName: z.string().trim(),
|
||||
lastName: z.string().trim().optional(),
|
||||
protectedKey: z.string().trim(),
|
||||
protectedKeyIV: z.string().trim(),
|
||||
protectedKeyTag: z.string().trim(),
|
||||
publicKey: z.string().trim(),
|
||||
encryptedPrivateKey: z.string().trim(),
|
||||
encryptedPrivateKeyIV: z.string().trim(),
|
||||
encryptedPrivateKeyTag: z.string().trim(),
|
||||
salt: z.string().trim(),
|
||||
verifier: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
user: UsersSchema,
|
||||
token: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req, res) => {
|
||||
const userAgent = req.headers["user-agent"];
|
||||
if (!userAgent) throw new Error("user agent header is required");
|
||||
const appCfg = getConfig();
|
||||
|
||||
const { user, accessToken, refreshToken } =
|
||||
await server.services.signup.completeAccountInvite({
|
||||
...req.body,
|
||||
ip: req.realIp,
|
||||
userAgent
|
||||
});
|
||||
|
||||
res.setCookie("jid", refreshToken, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: appCfg.HTTPS_ENABLED
|
||||
});
|
||||
// TODO(akhilmhdh-pg): add telemetry service
|
||||
|
||||
return { message: "Successfully set up account", user, token: accessToken };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ApiKeysSchema } from "@app/db/schemas/api-keys";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/api-keys",
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
apiKeyData: ApiKeysSchema.omit({ secretHash: true }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const apiKeyData = await server.services.apiKey.getMyApiKeys(req.auth.userId);
|
||||
return { apiKeyData };
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user