diff --git a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
index c819cd9ba86..886653b93d6 100644
--- a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
+++ b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx
@@ -31,7 +31,13 @@ import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
-import { useCheckPermissions, useSelectedOrganization, useStore, useFlag } from 'hooks'
+import { useCheckPermissions, useFlag, useSelectedOrganization, useStore } from 'hooks'
+
+// Use a const string to represent no chars option. Represented as empty string on the backend side.
+const NO_REQUIRED_CHARACTERS = 'NO_REQUIRED_CHARS'
+const LETTERS_AND_DIGITS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
+const LOWER_UPPER_DIGITS = 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
+const LOWER_UPPER_DIGITS_SYMBOLS = LOWER_UPPER_DIGITS + ':!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~'
const schema = object({
DISABLE_SIGNUP: boolean().required(),
@@ -50,6 +56,8 @@ const schema = object({
SESSIONS_TIMEBOX: number().min(0, 'Must be a positive number'),
SESSIONS_INACTIVITY_TIMEOUT: number().min(0, 'Must be a positive number'),
SESSIONS_SINGLE_PER_USER: boolean(),
+ PASSWORD_MIN_LENGTH: number().min(6, 'Must be greater or equal to 6.'),
+ PASSWORD_REQUIRED_CHARACTERS: string(),
})
function HoursOrNeverText({ value }: { value: number }) {
@@ -85,6 +93,7 @@ const BasicAuthSettingsForm = observer(() => {
const isProPlanAndUp = isSuccessSubscription && subscription?.plan?.id !== 'free'
const singlePerUserReleased = useFlag('authSingleSessionPerUserReleased')
+ const passwordStrengthReleased = useFlag('authPasswordStrengthReleased')
const INITIAL_VALUES = {
DISABLE_SIGNUP: !authConfig?.DISABLE_SIGNUP,
@@ -100,11 +109,23 @@ const BasicAuthSettingsForm = observer(() => {
SESSIONS_SINGLE_PER_USER: authConfig?.SESSIONS_SINGLE_PER_USER || false,
}
: null),
+
+ ...(passwordStrengthReleased
+ ? {
+ PASSWORD_MIN_LENGTH: authConfig?.PASSWORD_MIN_LENGTH || 6,
+ PASSWORD_REQUIRED_CHARACTERS:
+ authConfig?.PASSWORD_REQUIRED_CHARACTERS || NO_REQUIRED_CHARACTERS,
+ }
+ : null),
}
const onSubmit = (values: any, { resetForm }: any) => {
const payload = { ...values }
payload.DISABLE_SIGNUP = !values.DISABLE_SIGNUP
+ // The backend uses empty string to represent no required characters in the password
+ if (payload.PASSWORD_REQUIRED_CHARACTERS === NO_REQUIRED_CHARACTERS) {
+ payload.PASSWORD_REQUIRED_CHARACTERS = ''
+ }
updateAuthConfig(
{ projectRef: projectRef!, config: payload },
@@ -184,6 +205,56 @@ const BasicAuthSettingsForm = observer(() => {
+ {passwordStrengthReleased && (
+ <>
+ Passwords}>
+
+ characters}
+ disabled={!canUpdateConfig}
+ />
+ <>
+
+
+
+
+
+
+ >
+
+
+
+ >
+ )}
User Sessions}>
{isProPlanAndUp ? (
diff --git a/apps/studio/data/api.d.ts b/apps/studio/data/api.d.ts
index 2f233aa45de..b10bbe98fbd 100644
--- a/apps/studio/data/api.d.ts
+++ b/apps/studio/data/api.d.ts
@@ -168,9 +168,13 @@ export interface paths {
/** Gets daily organization stats */
get: operations['OrgDailyStatsController_getDailyStats']
}
+ '/platform/organizations/{slug}/daily-stats/compute': {
+ /** Gets daily organization stats for compute */
+ get: operations['OrgDailyStatsController_getDailyStatsCompute']
+ }
'/platform/organizations/{slug}/usage': {
/** Gets usage stats */
- get: operations['OrgUsageController_getDailyStats']
+ get: operations['OrgUsageController_getOrgUsage']
}
'/platform/organizations/{slug}/documents/standard-security-questionnaire': {
/** Get standard security questionnaire URL */
@@ -238,7 +242,7 @@ export interface paths {
}
'/platform/organizations/{slug}/billing/subscription/preview': {
/** Preview subscription changes */
- post: operations['SubscriptionController_previewSubscriptionChange']
+ post: operations['SubscriptionController_previewSubscriptionChangeV2']
}
'/platform/organizations/{slug}/billing/subscription/schedule': {
/** Deletes any upcoming subscription schedule */
@@ -250,7 +254,7 @@ export interface paths {
}
'/platform/organizations/{slug}/billing/invoices/upcoming': {
/** Gets the upcoming invoice */
- get: operations['OrgInvoicesController_getUpcomingInvoice']
+ get: operations['OrgInvoicesController_getUpcomingInvoiceV2']
}
'/platform/pg-meta/{ref}/column-privileges': {
/** Retrieve column privileges */
@@ -888,7 +892,7 @@ export interface paths {
}
'/system/organizations/{slug}/usage': {
/** Gets usage stats */
- get: operations['OrgUsageSystemController_getDailyStats']
+ get: operations['OrgUsageSystemController_getOrgUsage']
}
'/system/organizations/{slug}/billing/subscription': {
/** Gets the current subscription */
@@ -1668,14 +1672,14 @@ export interface paths {
/** Starts Fly single sign on */
get: operations['ExtensionController_startFlyioSSO']
}
- '/partners/flyio/extensions/{extension_id}/billing': {
- /** Gets resource billing */
- get: operations['ExtensionController_getResourceBilling']
- }
'/partners/flyio/extensions': {
/** Creates a database */
post: operations['ExtensionsController_provisionResource']
}
+ '/partners/flyio/organizations/{organization_id}': {
+ /** Gets information about the organization */
+ get: operations['OrganizationsController_getOrganization']
+ }
'/partners/flyio/organizations/{organization_id}/extensions': {
/** Gets all databases that belong to the Fly organization */
get: operations['OrganizationsController_getOrgExtensions']
@@ -1684,6 +1688,10 @@ export interface paths {
/** Starts Fly single sign on */
get: operations['OrganizationsController_startFlyioSSO']
}
+ '/partners/flyio/organizations/{organization_id}/billing': {
+ /** Gets the organizations current unbilled charges */
+ get: operations['FlyBillingController_getResourceBilling']
+ }
}
export type webhooks = Record
@@ -1813,13 +1821,15 @@ export interface components {
SESSIONS_TIMEBOX?: number
SESSIONS_INACTIVITY_TIMEOUT?: number
SESSIONS_SINGLE_PER_USER?: boolean
+ SESSIONS_TAGS?: string
RATE_LIMIT_EMAIL_SENT: number
RATE_LIMIT_SMS_SENT: number
RATE_LIMIT_VERIFY?: number
RATE_LIMIT_TOKEN_REFRESH?: number
MAILER_SECURE_EMAIL_CHANGE_ENABLED: boolean
REFRESH_TOKEN_ROTATION_ENABLED: boolean
- PASSWORD_MIN_LENGTH: number
+ PASSWORD_MIN_LENGTH?: number
+ PASSWORD_REQUIRED_CHARACTERS?: string
SECURITY_UPDATE_PASSWORD_REQUIRE_REAUTHENTICATION: boolean
SECURITY_REFRESH_TOKEN_REUSE_INTERVAL: number
MAILER_OTP_EXP: number
@@ -1947,6 +1957,7 @@ export interface components {
SESSIONS_TIMEBOX?: number | null
SESSIONS_INACTIVITY_TIMEOUT?: number | null
SESSIONS_SINGLE_PER_USER?: boolean
+ SESSIONS_TAGS?: string | null
RATE_LIMIT_EMAIL_SENT?: number
RATE_LIMIT_SMS_SENT?: number
RATE_LIMIT_VERIFY?: number
@@ -1954,6 +1965,12 @@ export interface components {
MAILER_SECURE_EMAIL_CHANGE_ENABLED?: boolean
REFRESH_TOKEN_ROTATION_ENABLED?: boolean
PASSWORD_MIN_LENGTH?: number
+ /** @enum {string} */
+ PASSWORD_REQUIRED_CHARACTERS?:
+ | 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
+ | 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
+ | 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};\'\\:"|<>?,./`~'
+ | ''
SECURITY_UPDATE_PASSWORD_REQUIRE_REAUTHENTICATION?: boolean
SECURITY_REFRESH_TOKEN_REUSE_INTERVAL?: number
MAILER_OTP_EXP?: number
@@ -2082,13 +2099,15 @@ export interface components {
SESSIONS_TIMEBOX?: number
SESSIONS_INACTIVITY_TIMEOUT?: number
SESSIONS_SINGLE_PER_USER?: boolean
+ SESSIONS_TAGS?: string
RATE_LIMIT_EMAIL_SENT: number
RATE_LIMIT_SMS_SENT: number
RATE_LIMIT_VERIFY?: number
RATE_LIMIT_TOKEN_REFRESH?: number
MAILER_SECURE_EMAIL_CHANGE_ENABLED: boolean
REFRESH_TOKEN_ROTATION_ENABLED: boolean
- PASSWORD_MIN_LENGTH: number
+ PASSWORD_MIN_LENGTH?: number
+ PASSWORD_REQUIRED_CHARACTERS?: string
SECURITY_UPDATE_PASSWORD_REQUIRE_REAUTHENTICATION: boolean
SECURITY_REFRESH_TOKEN_REUSE_INTERVAL: number
MAILER_OTP_EXP: number
@@ -2426,18 +2445,22 @@ export interface components {
member_id: number
org_id: number
}
+ ProjectAllocation: {
+ ref: string
+ usage: number
+ }
OrgMetricUsage: {
usage: number
+ usage_original: number
cost: number
+ unit_price_desc: string
available_in_plan: boolean
unlimited: boolean
capped: boolean
/** @enum {string} */
metric:
| 'EGRESS'
- | 'DATABASE_EGRESS'
| 'DATABASE_SIZE'
- | 'STORAGE_EGRESS'
| 'STORAGE_SIZE'
| 'MONTHLY_ACTIVE_USERS'
| 'MONTHLY_ACTIVE_SSO_USERS'
@@ -2463,6 +2486,7 @@ export interface components {
pricing_package_price?: number
pricing_package_size?: number
pricing_per_unit_price?: number
+ project_allocations: components['schemas']['ProjectAllocation'][]
}
OrgUsageResponse: {
usage_billing_enabled: boolean
@@ -2708,9 +2732,7 @@ export interface components {
/** @enum {string} */
metric:
| 'EGRESS'
- | 'DATABASE_EGRESS'
| 'DATABASE_SIZE'
- | 'STORAGE_EGRESS'
| 'STORAGE_SIZE'
| 'MONTHLY_ACTIVE_USERS'
| 'MONTHLY_ACTIVE_SSO_USERS'
@@ -2775,6 +2797,7 @@ export interface components {
/** @enum {string} */
tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise'
}
+ UpcomingInvoice: Record
ColumnPrivilege: {
grantor: string
grantee: string
@@ -4703,6 +4726,74 @@ export interface components {
| 'RESTORING'
| 'UPGRADING'
| 'PAUSING'
+ /**
+ * @description Supabase organization id
+ * @example fly_123456789
+ */
+ supabase_org_id: string
+ }
+ ResourceProvisioningConfigResponse: {
+ /**
+ * @description PSQL connection string
+ * @example postgresql://postgres:dbpass@db.abcdefghijklmnop.supabase.co:5432/postgres
+ */
+ DATABASE_URL: string
+ }
+ ResourceProvisioningResponse: {
+ /** @description Supabase envs config */
+ config: components['schemas']['ResourceProvisioningConfigResponse']
+ /**
+ * @description The target Fly application for internal traffic
+ * @example ext-db-pgshhamktpsgnptvcadw
+ */
+ fly_app_name: string
+ /**
+ * @description Supabase project id
+ * @example pgshhamktpsgnptvcadw
+ */
+ id: string
+ /** @description Welcome message */
+ message: string
+ /**
+ * @description Supabase organization id
+ * @example fly_123456789
+ */
+ supabase_org_id: string
+ }
+ FlyOrganization: {
+ /** @enum {string} */
+ plan: 'free' | 'pro' | 'team' | 'enterprise'
+ id: string
+ supabase_org_id: string
+ name: string
+ }
+ OrganizationExtensionStatus: {
+ /** @description Supabase project instance compute size */
+ compute: string
+ /** @description Unique ID representing the fly extension */
+ id: string
+ /**
+ * @description Supabase project status
+ * @example ACTIVE_HEALTHY
+ * @enum {string}
+ */
+ status:
+ | 'REMOVED'
+ | 'COMING_UP'
+ | 'INACTIVE'
+ | 'ACTIVE_HEALTHY'
+ | 'ACTIVE_UNHEALTHY'
+ | 'UNKNOWN'
+ | 'GOING_DOWN'
+ | 'INIT_FAILED'
+ | 'RESTORING'
+ | 'UPGRADING'
+ | 'PAUSING'
+ /**
+ * @description Supabase organization id
+ * @example fly_123456789
+ */
+ supabase_org_id: string
}
ResourceBillingItem: {
/**
@@ -4743,55 +4834,8 @@ export interface components {
exceedsPlanLimits: boolean
/** @description Whether the user is can have over-usage, which will be billed - this will be false on usage-capped plans. */
overusageAllowed: boolean
- extensionId: string
items: components['schemas']['ResourceBillingItem'][]
}
- ResourceProvisioningConfigResponse: {
- /**
- * @description PSQL connection string
- * @example postgresql://postgres:dbpass@db.abcdefghijklmnop.supabase.co:5432/postgres
- */
- POSTGRES_URL: string
- }
- ResourceProvisioningResponse: {
- /** @description Supabase envs config */
- config: components['schemas']['ResourceProvisioningConfigResponse']
- /**
- * @description The target Fly application for internal traffic
- * @example ext-db-pgshhamktpsgnptvcadw
- */
- fly_app_name: string
- /**
- * @description Supabase project id
- * @example pgshhamktpsgnptvcadw
- */
- id: string
- /** @description Welcome message */
- message: string
- }
- OrganizationExtensionStatus: {
- /** @description Supabase project instance compute size */
- compute: string
- /** @description Unique ID representing the fly extension */
- id: string
- /**
- * @description Supabase project status
- * @example ACTIVE_HEALTHY
- * @enum {string}
- */
- status:
- | 'REMOVED'
- | 'COMING_UP'
- | 'INACTIVE'
- | 'ACTIVE_HEALTHY'
- | 'ACTIVE_UNHEALTHY'
- | 'UNKNOWN'
- | 'GOING_DOWN'
- | 'INIT_FAILED'
- | 'RESTORING'
- | 'UPGRADING'
- | 'PAUSING'
- }
}
responses: never
parameters: never
@@ -5757,9 +5801,7 @@ export interface operations {
query: {
metric:
| 'EGRESS'
- | 'DATABASE_EGRESS'
| 'DATABASE_SIZE'
- | 'STORAGE_EGRESS'
| 'STORAGE_SIZE'
| 'MONTHLY_ACTIVE_USERS'
| 'MONTHLY_ACTIVE_SSO_USERS'
@@ -5801,9 +5843,37 @@ export interface operations {
}
}
}
- /** Gets usage stats */
- OrgUsageController_getDailyStats: {
+ /** Gets daily organization stats for compute */
+ OrgDailyStatsController_getDailyStatsCompute: {
parameters: {
+ query: {
+ endDate: string
+ startDate: string
+ projectRef?: string
+ }
+ path: {
+ /** @description Organization slug */
+ slug: string
+ }
+ }
+ responses: {
+ 200: {
+ content: never
+ }
+ /** @description Failed to get daily organization stats for compute */
+ 500: {
+ content: never
+ }
+ }
+ }
+ /** Gets usage stats */
+ OrgUsageController_getOrgUsage: {
+ parameters: {
+ query?: {
+ project_ref?: string
+ start?: string
+ end?: string
+ }
path: {
/** @description Organization slug */
slug: string
@@ -6228,7 +6298,7 @@ export interface operations {
}
}
/** Preview subscription changes */
- SubscriptionController_previewSubscriptionChange: {
+ SubscriptionController_previewSubscriptionChangeV2: {
parameters: {
path: {
/** @description Organization slug */
@@ -6298,7 +6368,7 @@ export interface operations {
}
}
/** Gets the upcoming invoice */
- OrgInvoicesController_getUpcomingInvoice: {
+ OrgInvoicesController_getUpcomingInvoiceV2: {
parameters: {
path: {
/** @description Organization slug */
@@ -6308,7 +6378,7 @@ export interface operations {
responses: {
200: {
content: {
- 'application/json': Record
+ 'application/json': components['schemas']['UpcomingInvoice']
}
}
403: {
@@ -10677,8 +10747,13 @@ export interface operations {
}
}
/** Gets usage stats */
- OrgUsageSystemController_getDailyStats: {
+ OrgUsageSystemController_getOrgUsage: {
parameters: {
+ query?: {
+ project_ref?: string
+ start?: string
+ end?: string
+ }
path: {
/** @description Organization slug */
slug: string
@@ -12247,21 +12322,6 @@ export interface operations {
}
}
}
- /** Gets resource billing */
- ExtensionController_getResourceBilling: {
- parameters: {
- path: {
- extension_id: string
- }
- }
- responses: {
- 200: {
- content: {
- 'application/json': components['schemas']['ResourceBillingResponse']
- }
- }
- }
- }
/** Creates a database */
ExtensionsController_provisionResource: {
responses: {
@@ -12272,6 +12332,21 @@ export interface operations {
}
}
}
+ /** Gets information about the organization */
+ OrganizationsController_getOrganization: {
+ parameters: {
+ path: {
+ organization_id: string
+ }
+ }
+ responses: {
+ 200: {
+ content: {
+ 'application/json': components['schemas']['FlyOrganization']
+ }
+ }
+ }
+ }
/** Gets all databases that belong to the Fly organization */
OrganizationsController_getOrgExtensions: {
parameters: {
@@ -12300,4 +12375,19 @@ export interface operations {
}
}
}
+ /** Gets the organizations current unbilled charges */
+ FlyBillingController_getResourceBilling: {
+ parameters: {
+ path: {
+ organization_id: string
+ }
+ }
+ responses: {
+ 200: {
+ content: {
+ 'application/json': components['schemas']['ResourceBillingResponse']
+ }
+ }
+ }
+ }
}