diff --git a/install/config/crowdsec/profiles.yaml b/install/config/crowdsec/profiles.yaml index 0632f51d..5781cf62 100644 --- a/install/config/crowdsec/profiles.yaml +++ b/install/config/crowdsec/profiles.yaml @@ -1,4 +1,4 @@ -iame: captcha_remediation +name: captcha_remediation filters: - Alert.Remediation == true && Alert.GetScope() == "Ip" && Alert.GetScenario() contains "http" decisions: @@ -22,4 +22,4 @@ filters: decisions: - type: ban duration: 4h -on_success: break \ No newline at end of file +on_success: break diff --git a/messages/en-US.json b/messages/en-US.json index e48c9f05..ff0ca4e6 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1162,7 +1162,7 @@ "selectDomainTypeCnameName": "Single Domain (CNAME)", "selectDomainTypeCnameDescription": "Just this specific domain. Use this for individual subdomains or specific domain entries.", "selectDomainTypeWildcardName": "Wildcard Domain", - "selectDomainTypeWildcardDescription": "This domain and its first level of subdomains.", + "selectDomainTypeWildcardDescription": "This domain and its subdomains.", "domainDelegation": "Single Domain", "selectType": "Select a type", "actions": "Actions", diff --git a/server/emails/index.ts b/server/emails/index.ts index 46d1df69..42cfa39c 100644 --- a/server/emails/index.ts +++ b/server/emails/index.ts @@ -18,10 +18,10 @@ function createEmailClient() { host: emailConfig.smtp_host, port: emailConfig.smtp_port, secure: emailConfig.smtp_secure || false, - auth: { + auth: (emailConfig.smtp_user && emailConfig.smtp_pass) ? { user: emailConfig.smtp_user, pass: emailConfig.smtp_pass - } + } : null } as SMTPTransport.Options; if (emailConfig.smtp_tls_reject_unauthorized !== undefined) { diff --git a/server/lib/consts.ts b/server/lib/consts.ts index 2fffc282..70d4404a 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,7 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -export const APP_VERSION = "1.7.0"; +export const APP_VERSION = "1.7.3"; export const __FILENAME = fileURLToPath(import.meta.url); export const __DIRNAME = path.dirname(__FILENAME); diff --git a/server/routers/external.ts b/server/routers/external.ts index e64d3456..6f0b04dc 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -620,8 +620,6 @@ authenticated.post( authenticated.delete("/idp/:idpId", verifyUserIsServerAdmin, idp.deleteIdp); -authenticated.get("/idp", verifyUserIsServerAdmin, idp.listIdps); - authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp); authenticated.put( diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index 6003c63d..9991ba9c 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -162,6 +162,12 @@ export async function validateOidcCallback( ); } + logger.debug("State verified", { + urL: ensureTrailingSlash(existingIdp.idpOidcConfig.tokenUrl), + expectedState, + state + }); + const tokens = await client.validateAuthorizationCode( ensureTrailingSlash(existingIdp.idpOidcConfig.tokenUrl), code, diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 1c4ace3b..8f16b198 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -261,14 +261,6 @@ async function createHttpResource( ) ); } - if (parsedSubdomain.data.includes(".")) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - "Subdomain cannot contain a dot when using wildcard domains" - ) - ); - } fullDomain = `${subdomain}.${domainRes.domains.baseDomain}`; } else { fullDomain = domainRes.domains.baseDomain; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index fda24f47..a20a7024 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -297,14 +297,6 @@ async function updateHttpResource( ) ); } - if (parsedSubdomain.data.includes(".")) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - "Subdomain cannot contain a dot when using wildcard domains" - ) - ); - } fullDomain = `${updateData.subdomain}.${domainRes.domains.baseDomain}`; } else { fullDomain = domainRes.domains.baseDomain; diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index a91fd7b9..b3ce8984 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -9,7 +9,7 @@ import { SettingsSectionHeader, SettingsSectionTitle } from "@app/components/Settings"; -import { StrategySelect } from "@app/components/StrategySelect"; +import { StrategyOption, StrategySelect } from "@app/components/StrategySelect"; import HeaderTitle from "@app/components/SettingsSectionTitle"; import { Button } from "@app/components/ui/button"; import { useParams, useRouter } from "next/navigation"; @@ -45,15 +45,10 @@ import { createApiClient } from "@app/lib/api"; import { Checkbox } from "@app/components/ui/checkbox"; import { ListIdpsResponse } from "@server/routers/idp"; import { useTranslations } from "next-intl"; +import { build } from "@server/build"; type UserType = "internal" | "oidc"; -interface UserTypeOption { - id: UserType; - title: string; - description: string; -} - interface IdpOption { idpId: number; name: string; @@ -147,13 +142,20 @@ export default function Page() { } }, [userType, env.email.emailEnabled, internalForm, externalForm]); - const userTypes: UserTypeOption[] = [ + const [userTypes, setUserTypes] = useState[]>([ { id: "internal", title: t("userTypeInternal"), - description: t("userTypeInternalDescription") + description: t("userTypeInternalDescription"), + disabled: false + }, + { + id: "oidc", + title: t("userTypeExternal"), + description: t("userTypeExternalDescription"), + disabled: true } - ]; + ]); useEffect(() => { if (!userType) { @@ -177,9 +179,6 @@ export default function Page() { if (res?.status === 200) { setRoles(res.data.data.roles); - if (userType === "internal") { - setDataLoaded(true); - } } } @@ -200,24 +199,32 @@ export default function Page() { if (res?.status === 200) { setIdps(res.data.data.idps); - setDataLoaded(true); if (res.data.data.idps.length) { - userTypes.push({ - id: "oidc", - title: t("userTypeExternal"), - description: t("userTypeExternalDescription") - }); + setUserTypes((prev) => + prev.map((type) => { + if (type.id === "oidc") { + return { + ...type, + disabled: false + }; + } + return type; + }) + ); } } } - setDataLoaded(false); - fetchRoles(); - if (userType !== "internal") { - fetchIdps(); + async function fetchInitialData() { + setDataLoaded(false); + await fetchRoles(); + await fetchIdps(); + setDataLoaded(true); } - }, [userType]); + + fetchInitialData(); + }, []); async function onSubmitInternal( values: z.infer @@ -323,7 +330,7 @@ export default function Page() {
- {!inviteLink && userTypes.length > 1 ? ( + {!inviteLink && build !== "saas" ? ( @@ -610,7 +617,7 @@ export default function Page() { idp || null ); }} - cols={3} + cols={2} /> diff --git a/src/app/page.tsx b/src/app/page.tsx index 91ca5686..5c150c58 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -12,6 +12,7 @@ import { cleanRedirect } from "@app/lib/cleanRedirect"; import { Layout } from "@app/components/Layout"; import { InitialSetupCompleteResponse } from "@server/routers/auth"; import { cookies } from "next/headers"; +import { build } from "@server/build"; export const dynamic = "force-dynamic"; @@ -83,25 +84,27 @@ export default async function Page(props: { if (ownedOrg) { redirect(`/${ownedOrg.orgId}`); } else { - redirect("/setup"); + if (!env.flags.disableUserCreateOrg || user.serverAdmin) { + redirect("/setup"); + } } } - // return ( - // - // - //
- // ({ - // name: org.name, - // id: org.orgId - // }))} - // /> - //
- //
- //
- // ); + return ( + + +
+ ({ + name: org.name, + id: org.orgId + }))} + /> +
+
+
+ ); } diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index d7b38794..98ae6b6a 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -179,7 +179,7 @@ export default function DomainPicker({ }); } } else if (orgDomain.type === "wildcard") { - // For wildcard domains, allow the base domain or one level up + // For wildcard domains, allow the base domain or multiple levels up const userInputLower = userInput.toLowerCase(); const baseDomainLower = orgDomain.baseDomain.toLowerCase(); @@ -194,24 +194,22 @@ export default function DomainPicker({ domainId: orgDomain.domainId }); } - // Check if user input is one level up (subdomain.baseDomain) + // Check if user input ends with the base domain (allows multiple level subdomains) else if (userInputLower.endsWith(`.${baseDomainLower}`)) { const subdomain = userInputLower.slice( 0, -(baseDomainLower.length + 1) ); - // Only allow one level up (no dots in subdomain) - if (!subdomain.includes(".")) { - options.push({ - id: `org-${orgDomain.domainId}`, - domain: userInput, - type: "organization", - verified: orgDomain.verified, - domainType: "wildcard", - domainId: orgDomain.domainId, - subdomain: subdomain - }); - } + // Allow multiple levels (subdomain can contain dots) + options.push({ + id: `org-${orgDomain.domainId}`, + domain: userInput, + type: "organization", + verified: orgDomain.verified, + domainType: "wildcard", + domainId: orgDomain.domainId, + subdomain: subdomain + }); } } }); @@ -320,7 +318,7 @@ export default function DomainPicker({ setUserInput(validInput); }} /> -

+

{build === "saas" ? t("domainPickerDescriptionSaas") : t("domainPickerDescription")} @@ -328,42 +326,44 @@ export default function DomainPicker({

{/* Tabs and Sort Toggle */} -
- - setActiveTab( - value as "all" | "organization" | "provided" - ) - } - > - - - {t("domainPickerTabAll")} - - - {t("domainPickerTabOrganization")} - - {build == "saas" && ( - - {t("domainPickerTabProvided")} + {build === "saas" && ( +
+ + setActiveTab( + value as "all" | "organization" | "provided" + ) + } + > + + + {t("domainPickerTabAll")} - )} - - - -
+ + {t("domainPickerTabOrganization")} + + {build == "saas" && ( + + {t("domainPickerTabProvided")} + + )} +
+
+ +
+ )} {/* Loading State */} {isChecking && ( diff --git a/src/components/OrganizationLanding.tsx b/src/components/OrganizationLanding.tsx index 2f3125b0..2d235c6d 100644 --- a/src/components/OrganizationLanding.tsx +++ b/src/components/OrganizationLanding.tsx @@ -41,35 +41,31 @@ export default function OrganizationLanding({ function getDescriptionText() { if (organizations.length === 0) { if (!disableCreateOrg) { - return t('componentsErrorNoMemberCreate'); + return t("componentsErrorNoMemberCreate"); } else { - return t('componentsErrorNoMember'); + return t("componentsErrorNoMember"); } } - return t('componentsMember', {count: organizations.length}); + return t("componentsMember", { count: organizations.length }); } return ( - {t('welcome')} + {t("welcome")} {getDescriptionText()} {organizations.length === 0 ? ( - disableCreateOrg ? ( -

- t('componentsErrorNoMember') -

- ) : ( + !disableCreateOrg && ( ) diff --git a/src/components/SecurityKeyForm.tsx b/src/components/SecurityKeyForm.tsx index d4970947..e343fc5f 100644 --- a/src/components/SecurityKeyForm.tsx +++ b/src/components/SecurityKeyForm.tsx @@ -103,8 +103,10 @@ export default function SecurityKeyForm({ }); useEffect(() => { - loadSecurityKeys(); - }, []); + if (open) { + loadSecurityKeys(); + } + }, [open]); const registerSchema = z.object({ name: z.string().min(1, { message: t("securityKeyNameRequired") }), diff --git a/src/components/StrategySelect.tsx b/src/components/StrategySelect.tsx index f6a899f8..b431b96c 100644 --- a/src/components/StrategySelect.tsx +++ b/src/components/StrategySelect.tsx @@ -4,7 +4,7 @@ import { cn } from "@app/lib/cn"; import { RadioGroup, RadioGroupItem } from "./ui/radio-group"; import { useState } from "react"; -interface StrategyOption { +export interface StrategyOption { id: TValue; title: string; description: string; diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index 07cdc3f5..df9f0dff 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -18,7 +18,7 @@ function PopoverTrigger({ function PopoverContent({ className, - align = "center", + align = "start", sideOffset = 4, ...props }: React.ComponentProps) {