Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/content/guides/database/tables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ create view <view name> with(security_invoker=true) as (

### When to use views

Views provide the several benefits:
Views provide several benefits:

- Simplicity
- Consistency
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { noop } from 'lodash'
import AlertError from 'components/ui/AlertError'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query'
import { useTableRolesAccessQuery } from 'data/tables/table-roles-access-query'
import { useTablesRolesAccessQuery } from 'data/tables/tables-roles-access-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
Alert_Shadcn_,
Expand Down Expand Up @@ -65,13 +65,13 @@ export const PolicyTableRow = ({
const isRLSEnabled = table.rls_enabled
const isTableExposedThroughAPI = exposedSchemas.includes(table.schema)

const { data: roles = [] } = useTableRolesAccessQuery({
const { data: tablesWithAnonAuthAccess = new Set() } = useTablesRolesAccessQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
schema: table.schema,
table: table.name,
})
const hasAnonAuthenticatedRolesAccess = roles.length !== 0

const hasAnonAuthenticatedRolesAccess = tablesWithAnonAuthAccess.has(table.name)
const isPubliclyReadableWritable =
!isRLSEnabled && isTableExposedThroughAPI && hasAnonAuthenticatedRolesAccess

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ export const NewOrgForm = ({
const paymentRef = useRef<PaymentMethodElementRef | null>(null)

const onSubmit: SubmitHandler<z.infer<typeof formSchema>> = async (formValues) => {
debugger
const hasFreeOrgWithProjects = freeOrgs.some((it) => projectsByOrg[it.slug]?.length > 0)

if (hasFreeOrgWithProjects && formValues.plan !== 'FREE') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import Link from 'next/link'

import { UpgradePlanButton } from 'components/ui/UpgradePlanButton'
import type { MemberWithFreeProjectLimit } from 'data/organizations/free-project-limit-check-query'
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'

interface FreeProjectLimitWarningProps {
membersExceededLimit: MemberWithFreeProjectLimit[]
orgSlug: string
}

const FreeProjectLimitWarning = ({
membersExceededLimit,
orgSlug,
}: FreeProjectLimitWarningProps) => {
export const FreeProjectLimitWarning = ({ membersExceededLimit }: FreeProjectLimitWarningProps) => {
return (
<Admonition
type={'default'}
Expand All @@ -36,19 +30,9 @@ const FreeProjectLimitWarning = ({
projects before you're able to create a free project within this organization.
</p>

<div>
<Button asChild type="default">
<Link
href={`/org/${orgSlug}/billing?panel=subscriptionPlan&source=freeProjectLimitWarning`}
>
Upgrade plan
</Link>
</Button>
</div>
<UpgradePlanButton source="freeProjectLimitWarning">Upgrade plan</UpgradePlanButton>
</div>
}
/>
)
}

export default FreeProjectLimitWarning
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { default as NotOrganizationOwnerWarning } from './NotOrganizationOwnerWarning'
export { default as FreeProjectLimitWarning } from './FreeProjectLimitWarning'
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ export const InviteMemberButton = () => {
>
<FormControl_Shadcn_>
<OrganizationProjectSelector
fetchOnMount
sameWidthAsTrigger
checkPosition="left"
selectedRef={projectRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from 'data/organizations/organization-members-query'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useHasAccessToProjectLevelPermissions } from 'data/subscriptions/org-subscription-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
Expand Down Expand Up @@ -45,13 +46,12 @@ export const MemberActions = ({ member }: MemberActionsProps) => {

const { data: selectedOrganization } = useSelectedOrganizationQuery()
const { data: permissions } = usePermissionsQuery()
const { data: members } = useOrganizationMembersQuery({ slug })

const { data: allRoles } = useOrganizationRolesV2Query({ slug })
const hasProjectScopedRoles = (allRoles?.project_scoped_roles ?? []).length > 0
const { data: members } = useOrganizationMembersQuery({ slug })
const isOptedIntoProjectLevelPermissions = useHasAccessToProjectLevelPermissions(slug as string)

// [Joshen] We only need this data if the org has project scoped roles
const { data } = useProjectsQuery({ enabled: hasProjectScopedRoles })
const { data } = useProjectsQuery({ enabled: isOptedIntoProjectLevelPermissions })
const allProjects = data?.projects ?? []

const memberIsUser = member.gotrue_id == profile?.gotrue_id
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { ArrowRight, Check, Minus, User, X } from 'lucide-react'
import Link from 'next/link'

import { useParams } from 'common'
import PartnerIcon from 'components/ui/PartnerIcon'
import { ProfileImage } from 'components/ui/ProfileImage'
import { useOrganizationRolesV2Query } from 'data/organization-members/organization-roles-query'
import { OrganizationMember } from 'data/organizations/organization-members-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useHasAccessToProjectLevelPermissions } from 'data/subscriptions/org-subscription-query'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { getGitHubProfileImgUrl } from 'lib/github'
import { useProfile } from 'lib/profile'
Expand All @@ -32,16 +34,18 @@ const MEMBER_ORIGIN_TO_MANAGED_BY = {
} as const

export const MemberRow = ({ member }: MemberRowProps) => {
const { slug } = useParams()
const { profile } = useProfile()
const { data: selectedOrganization } = useSelectedOrganizationQuery()
const isOptedIntoProjectLevelPermissions = useHasAccessToProjectLevelPermissions(slug as string)

const { data: roles, isLoading: isLoadingRoles } = useOrganizationRolesV2Query({
slug: selectedOrganization?.slug,
})
const hasProjectScopedRoles = (roles?.project_scoped_roles ?? []).length > 0

// [Joshen] We only need this data if the org has project scoped roles
const { data } = useProjectsQuery({ enabled: hasProjectScopedRoles })
const { data } = useProjectsQuery({ enabled: isOptedIntoProjectLevelPermissions })
const projects = data?.projects ?? []

const orgProjects = projects?.filter((p) => p.organization_id === selectedOrganization?.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { organizationKeys as organizationKeysV1 } from 'data/organizations/keys'
import { OrganizationMember } from 'data/organizations/organization-members-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useHasAccessToProjectLevelPermissions } from 'data/subscriptions/org-subscription-query'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import {
Expand All @@ -39,12 +40,12 @@ export const UpdateRolesConfirmationModal = ({
const { slug } = useParams()
const queryClient = useQueryClient()
const { data: organization } = useSelectedOrganizationQuery()
const isOptedIntoProjectLevelPermissions = useHasAccessToProjectLevelPermissions(slug as string)

const { data: allRoles } = useOrganizationRolesV2Query({ slug: organization?.slug })
const hasProjectScopedRoles = (allRoles?.project_scoped_roles ?? []).length > 0

// [Joshen] We only need this data if the org has project scoped roles
const { data } = useProjectsQuery({ enabled: hasProjectScopedRoles })
const { data } = useProjectsQuery({ enabled: isOptedIntoProjectLevelPermissions && visible })
const projects = data?.projects ?? []

// [Joshen] Separate saving state instead of using RQ due to several successive steps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelP
const isOptedIntoProjectLevelPermissions = useHasAccessToProjectLevelPermissions(slug as string)

const { data: allRoles, isSuccess: isSuccessRoles } = useOrganizationRolesV2Query({ slug })
const hasProjectScopedRoles = (allRoles?.project_scoped_roles ?? []).length > 0

// [Joshen] We only need this data if the org has project scoped roles
const { data } = useProjectsQuery({ enabled: hasProjectScopedRoles })
// Only need to fetch projects for organizations with access to project scoped roles
// which will be Team or Enterprise (and when the panel is visible)
// We still need to use the old projects endpoint instead of org projects due to roles depending on project ID
const { data } = useProjectsQuery({ enabled: isOptedIntoProjectLevelPermissions && visible })
const projects = data?.projects ?? []

const { data: permissions } = usePermissionsQuery()

// [Joshen] We use the org scoped roles as the source for available roles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from 'data/table-editor/table-editor-types'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useDashboardHistory } from 'hooks/misc/useDashboardHistory'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
import { useUrlState } from 'hooks/ui/useUrlState'
import { useIsProtectedSchema } from 'hooks/useProtectedSchemas'
import { TableEditorTableStateContextProvider } from 'state/table-editor-table'
Expand All @@ -38,6 +39,7 @@ export const TableGridEditor = ({
const router = useRouter()
const { ref: projectRef, id } = useParams()
const { setLastVisitedTable } = useDashboardHistory()
const { selectedSchema } = useQuerySchemaState()

const tabs = useTabsStateSnapshot()

Expand All @@ -61,7 +63,9 @@ export const TableGridEditor = ({

const onTableCreated = useCallback(
(table: { id: number }) => {
router.push(`/project/${projectRef}/editor/${table.id}`)
router.push(
`/project/${projectRef}/editor/${table.id}${!!selectedSchema ? `?schema=${selectedSchema}` : ''}`
)
},
[projectRef, router]
)
Expand Down
1 change: 1 addition & 0 deletions apps/studio/csp.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ module.exports.getCSP = function getCSP() {
GOOGLE_MAPS_API_URL,
POSTHOG_URL,
...(!!NIMBUS_PROD_PROJECTS_URL ? [NIMBUS_PROD_PROJECTS_URL, NIMBUS_PROD_PROJECTS_URL_WS] : []),
CLOUDFLARE_CDN_URL,
]
const SCRIPT_SRC_URLS = [
CLOUDFLARE_CDN_URL,
Expand Down
4 changes: 2 additions & 2 deletions apps/studio/data/tables/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ export const tableKeys = {
['projects', projectRef, 'tables', schema, includeColumns].filter(Boolean),
retrieve: (projectRef: string | undefined, name: string, schema: string) =>
['projects', projectRef, 'tables', schema, name].filter(Boolean),
rolesAccess: (projectRef: string | undefined, schema: string, table: string) => [
rolesAccess: (projectRef: string | undefined, schema: string) => [
'projects',
projectRef,
'roles-access',
{ schema, table },
{ schema },
],
}
70 changes: 0 additions & 70 deletions apps/studio/data/tables/table-roles-access-query.ts

This file was deleted.

52 changes: 52 additions & 0 deletions apps/studio/data/tables/tables-roles-access-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { getTablesWithAnonAuthenticatedAccessSQL } from '@supabase/pg-meta/src/sql/studio/check-tables-anon-authenticated-access'
import { useQuery, UseQueryOptions } from '@tanstack/react-query'

import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
import { tableKeys } from './keys'

type TablesRolesAccessArgs = {
schema: string
}

export type TablesRolesAccessVariables = TablesRolesAccessArgs & {
projectRef?: string
connectionString?: string | null
}

export async function getTablesWithAnonAuthenticatedAccess(
{ schema, projectRef, connectionString }: TablesRolesAccessVariables,
signal?: AbortSignal
) {
if (!schema) throw new Error('schema is required')

const sql = getTablesWithAnonAuthenticatedAccessSQL({ schema })

const { result } = (await executeSql(
{ projectRef, connectionString, sql, queryKey: ['TablesRolesAccess', schema] },
signal
)) as { result: { table_name: string }[] }

return new Set(result.map((r) => r.table_name))
}

export type TablesRolesAccessData = Awaited<ReturnType<typeof getTablesWithAnonAuthenticatedAccess>>
export type TablesRolesAccessError = ExecuteSqlError

export const useTablesRolesAccessQuery = <TData = TablesRolesAccessData>(
{ projectRef, connectionString, schema }: TablesRolesAccessVariables,
{
enabled = true,
...options
}: UseQueryOptions<TablesRolesAccessData, TablesRolesAccessError, TData> = {}
) =>
useQuery<TablesRolesAccessData, TablesRolesAccessError, TData>({
queryKey: tableKeys.rolesAccess(projectRef, schema),
queryFn: ({ signal }) =>
getTablesWithAnonAuthenticatedAccess({ projectRef, connectionString, schema }, signal),
enabled:
enabled &&
typeof projectRef !== 'undefined' &&
typeof schema !== 'undefined' &&
!!connectionString,
...options,
})
4 changes: 2 additions & 2 deletions apps/studio/lib/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { LOCAL_STORAGE_KEYS } from 'common'
import { makeRandomString } from './helpers'

const GITHUB_INTEGRATION_APP_NAME =
process.env.NIMBUS_PROD_PROJECTS_URL !== undefined
process.env.NEXT_PUBLIC_IS_NIMBUS !== undefined
? 'supabase-snap'
: process.env.NEXT_PUBLIC_ENVIRONMENT === 'prod'
? `supabase`
Expand All @@ -11,7 +11,7 @@ const GITHUB_INTEGRATION_APP_NAME =
: `supabase-local-testing`

const GITHUB_INTEGRATION_CLIENT_ID =
process.env.NIMBUS_PROD_PROJECTS_URL !== undefined
process.env.NEXT_PUBLIC_IS_NIMBUS !== undefined
? 'Iv23li2pAiqDGgaSrP8q'
: process.env.NEXT_PUBLIC_ENVIRONMENT === 'prod'
? `Iv1.b91a6d8eaa272168`
Expand Down
Loading
Loading