mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
fix(dashboard): creation of new org when user is apart of no orgs
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
@@ -363,21 +364,35 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
organization: OrganizationsSchema
|
||||
organization: OrganizationsSchema,
|
||||
accessToken: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
handler: async (req, res) => {
|
||||
if (req.auth.actor !== ActorType.USER) return;
|
||||
|
||||
const organization = await server.services.org.deleteOrganizationById(
|
||||
req.permission.id,
|
||||
req.params.organizationId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { organization };
|
||||
const cfg = getConfig();
|
||||
|
||||
const { organization, tokens } = await server.services.org.deleteOrganizationById({
|
||||
userId: req.permission.id,
|
||||
orgId: req.params.organizationId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
authorizationHeader: req.headers.authorization,
|
||||
userAgentHeader: req.headers["user-agent"],
|
||||
ipAddress: req.realIp
|
||||
});
|
||||
|
||||
void res.setCookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: cfg.HTTPS_ENABLED
|
||||
});
|
||||
|
||||
return { organization, accessToken: tokens.accessToken };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -54,10 +54,11 @@ export const tokenDALFactory = (db: TDbClient) => {
|
||||
const insertTokenSession = async (
|
||||
userId: string,
|
||||
ip: string,
|
||||
userAgent: string
|
||||
userAgent: string,
|
||||
tx?: Knex
|
||||
): Promise<TAuthTokenSessions | undefined> => {
|
||||
try {
|
||||
const [session] = await db(TableName.AuthTokenSession)
|
||||
const [session] = await (tx || db)(TableName.AuthTokenSession)
|
||||
.insert({
|
||||
userId,
|
||||
ip,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import bcrypt from "bcrypt";
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -123,14 +124,13 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, orgMembershipDAL }: TAu
|
||||
return deletedToken?.[0];
|
||||
};
|
||||
|
||||
const getUserTokenSession = async ({
|
||||
userId,
|
||||
ip,
|
||||
userAgent
|
||||
}: TIssueAuthTokenDTO): Promise<TAuthTokenSessions | undefined> => {
|
||||
const getUserTokenSession = async (
|
||||
{ userId, ip, userAgent }: TIssueAuthTokenDTO,
|
||||
tx?: Knex
|
||||
): Promise<TAuthTokenSessions | undefined> => {
|
||||
let session = await tokenDAL.findOneTokenSession({ userId, ip, userAgent });
|
||||
if (!session) {
|
||||
session = await tokenDAL.insertTokenSession(userId, ip, userAgent);
|
||||
session = await tokenDAL.insertTokenSession(userId, ip, userAgent, tx);
|
||||
}
|
||||
return session;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TUsers, UserDeviceSchema } from "@app/db/schemas";
|
||||
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
|
||||
@@ -50,13 +51,13 @@ export const authLoginServiceFactory = ({
|
||||
* Not exported. This is to update user device list
|
||||
* If new device is found. Will be saved and a mail will be send
|
||||
*/
|
||||
const updateUserDeviceSession = async (user: TUsers, ip: string, userAgent: string) => {
|
||||
const updateUserDeviceSession = async (user: TUsers, ip: string, userAgent: string, tx?: Knex) => {
|
||||
const devices = await UserDeviceSchema.parseAsync(user.devices || []);
|
||||
const isDeviceSeen = devices.some((device) => device.ip === ip && device.userAgent === userAgent);
|
||||
|
||||
if (!isDeviceSeen) {
|
||||
const newDeviceList = devices.concat([{ ip, userAgent }]);
|
||||
await userDAL.updateById(user.id, { devices: JSON.stringify(newDeviceList) });
|
||||
await userDAL.updateById(user.id, { devices: JSON.stringify(newDeviceList) }, tx);
|
||||
if (user.email) {
|
||||
await smtpService.sendMail({
|
||||
template: SmtpTemplates.NewDeviceJoin,
|
||||
@@ -97,30 +98,36 @@ export const authLoginServiceFactory = ({
|
||||
* Check user device and send mail if new device
|
||||
* generate the auth and refresh token. fn shared by mfa verification and login verification with mfa disabled
|
||||
*/
|
||||
const generateUserTokens = async ({
|
||||
user,
|
||||
ip,
|
||||
userAgent,
|
||||
organizationId,
|
||||
authMethod,
|
||||
isMfaVerified,
|
||||
mfaMethod
|
||||
}: {
|
||||
user: TUsers;
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
organizationId?: string;
|
||||
authMethod: AuthMethod;
|
||||
isMfaVerified?: boolean;
|
||||
mfaMethod?: MfaMethod;
|
||||
}) => {
|
||||
const cfg = getConfig();
|
||||
await updateUserDeviceSession(user, ip, userAgent);
|
||||
const tokenSession = await tokenService.getUserTokenSession({
|
||||
userAgent,
|
||||
const generateUserTokens = async (
|
||||
{
|
||||
user,
|
||||
ip,
|
||||
userId: user.id
|
||||
});
|
||||
userAgent,
|
||||
organizationId,
|
||||
authMethod,
|
||||
isMfaVerified,
|
||||
mfaMethod
|
||||
}: {
|
||||
user: TUsers;
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
organizationId?: string;
|
||||
authMethod: AuthMethod;
|
||||
isMfaVerified?: boolean;
|
||||
mfaMethod?: MfaMethod;
|
||||
},
|
||||
tx?: Knex
|
||||
) => {
|
||||
const cfg = getConfig();
|
||||
await updateUserDeviceSession(user, ip, userAgent, tx);
|
||||
const tokenSession = await tokenService.getUserTokenSession(
|
||||
{
|
||||
userAgent,
|
||||
ip,
|
||||
userId: user.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
if (!tokenSession) throw new Error("Failed to create token");
|
||||
|
||||
const accessToken = jwt.sign(
|
||||
|
||||
@@ -77,11 +77,16 @@ export const selectOrganization = async (data: {
|
||||
export const useSelectOrganization = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (details: { organizationId: string; userAgent?: UserAgentType }) => {
|
||||
mutationFn: async (details: {
|
||||
organizationId: string;
|
||||
userAgent?: UserAgentType;
|
||||
forceSetCredentials?: boolean;
|
||||
}) => {
|
||||
const data = await selectOrganization(details);
|
||||
|
||||
// If a custom user agent is set, then this session is meant for another consuming application, not the web application.
|
||||
if (!details.userAgent && !data.isMfaEnabled) {
|
||||
if ((!details.userAgent && !data.isMfaEnabled) || details.forceSetCredentials) {
|
||||
localStorage.setItem("orgData.id", details.organizationId);
|
||||
SecurityClient.setToken(data.token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
|
||||
@@ -67,7 +68,7 @@ export const useCreateOrg = (options: { invalidate: boolean } = { invalidate: tr
|
||||
mutationFn: async ({ name }: { name: string }) => {
|
||||
const {
|
||||
data: { organization }
|
||||
} = await apiRequest.post("/api/v2/organizations", {
|
||||
} = await apiRequest.post<{ organization: { id: string } }>("/api/v2/organizations", {
|
||||
name
|
||||
});
|
||||
|
||||
@@ -437,10 +438,13 @@ export const useDeleteOrgById = () => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ organizationId }: { organizationId: string }) => {
|
||||
const {
|
||||
data: { organization }
|
||||
} = await apiRequest.delete<{ organization: Organization }>(
|
||||
data: { organization, accessToken }
|
||||
} = await apiRequest.delete<{ organization: Organization; accessToken: string }>(
|
||||
`/api/v2/organizations/${organizationId}`
|
||||
);
|
||||
SecurityClient.setToken(accessToken);
|
||||
localStorage.removeItem("orgData.id");
|
||||
|
||||
return organization;
|
||||
},
|
||||
onSuccess(_, dto) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
import { queryClient } from "@app/reactQuery";
|
||||
|
||||
export const navigateUserToOrg = async (router: NextRouter, organizationId?: string) => {
|
||||
const userOrgs = await fetchOrganizations();
|
||||
const userOrgs = await fetchOrganizations().catch(() => []);
|
||||
|
||||
const nonAuthEnforcedOrgs = userOrgs.filter((org) => !org.authEnforced);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import z from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
|
||||
import { useCreateOrg, useSelectOrganization } from "@app/hooks/api";
|
||||
import { useCreateOrg, useGetOrganizations, useSelectOrganization } from "@app/hooks/api";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
const schema = z
|
||||
@@ -23,9 +23,10 @@ interface CreateOrgModalProps {
|
||||
}
|
||||
|
||||
export const CreateOrgModal: FC<CreateOrgModalProps> = ({ isOpen, onClose }) => {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { refetch: refetchOrganizations } = useGetOrganizations();
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -50,19 +51,21 @@ export const CreateOrgModal: FC<CreateOrgModalProps> = ({ isOpen, onClose }) =>
|
||||
});
|
||||
|
||||
await selectOrg({
|
||||
organizationId: organization.id
|
||||
organizationId: organization.id,
|
||||
forceSetCredentials: true
|
||||
});
|
||||
|
||||
await refetchOrganizations();
|
||||
|
||||
createNotification({
|
||||
text: "Successfully created organization",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (router.isReady) router.push(`/org/${organization.id}/${ProjectType.SecretManager}/overview`);
|
||||
if (router.isReady)
|
||||
router.push(`/org/${organization.id}/${ProjectType.SecretManager}/overview`);
|
||||
else window.location.href = `/org/${organization.id}/${ProjectType.SecretManager}/overview`;
|
||||
|
||||
localStorage.setItem("orgData.id", organization.id);
|
||||
|
||||
reset();
|
||||
onClose();
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user