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
49 changes: 49 additions & 0 deletions supabase/migrations/20250217100252_restrict_accounts_and_orgs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
-- Only allow authenticated users to view their own accounts.
alter policy accounts_select_policy
on app.accounts
to authenticated
using (id = auth.uid());

-- Only allow organization maintainers to view their own organizations.
alter policy organizations_select_policy
on app.organizations
to authenticated
using (app.is_organization_maintainer(auth.uid(), id));

-- Allow authenticated users to get an account by handle.
create or replace function public.get_account(
handle text
)
returns setof public.accounts
language sql
security definer
strict
as $$
select id, handle, avatar_path, display_name, bio, created_at
from public.accounts a
where a.handle = get_account.handle
and auth.uid() is not null;
$$;

-- Allow authenticated users to get an organization by handle.
create or replace function public.get_organization(
handle text
)
returns setof public.organizations
language sql
security definer
strict
as $$
select id, handle, avatar_path, display_name, bio, created_at
from public.organizations o
where o.handle = get_organization.handle
and auth.uid() is not null;
$$;

-- Allow service role to read all accounts and organizations.
grant select on app.accounts to service_role;
grant select on app.organizations to service_role;

-- Allow service role to read all packages.
grant select on app.packages to service_role;
grant select on app.package_versions to service_role;
1 change: 1 addition & 0 deletions website/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
NEXT_PUBLIC_SUPABASE_URL=""
NEXT_PUBLIC_SUPABASE_ANON_KEY=""
SUPABASE_SERVICE_ROLE_KEY=""
106 changes: 53 additions & 53 deletions website/data/database.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,7 @@ export type Database = {
handle: string | null
id: string | null
}
Relationships: [
{
foreignKeyName: 'accounts_id_fkey'
columns: ['id']
isOneToOne: true
referencedRelation: 'users'
referencedColumns: ['id']
},
]
Relationships: []
}
download_metrics: {
Row: {
Expand All @@ -72,13 +64,6 @@ export type Database = {
referencedRelation: 'packages'
referencedColumns: ['id']
},
{
foreignKeyName: 'downloads_package_id_fkey'
columns: ['package_id']
isOneToOne: false
referencedRelation: 'packages'
referencedColumns: ['id']
},
]
}
members: {
Expand All @@ -101,13 +86,6 @@ export type Database = {
role?: 'maintainer' | null
}
Relationships: [
{
foreignKeyName: 'members_account_id_fkey'
columns: ['account_id']
isOneToOne: false
referencedRelation: 'accounts'
referencedColumns: ['id']
},
{
foreignKeyName: 'members_account_id_fkey'
columns: ['account_id']
Expand All @@ -122,13 +100,6 @@ export type Database = {
referencedRelation: 'organizations'
referencedColumns: ['id']
},
{
foreignKeyName: 'members_organization_id_fkey'
columns: ['organization_id']
isOneToOne: false
referencedRelation: 'organizations'
referencedColumns: ['id']
},
]
}
organizations: {
Expand Down Expand Up @@ -161,13 +132,6 @@ export type Database = {
referencedRelation: 'packages'
referencedColumns: ['id']
},
{
foreignKeyName: 'package_upgrades_package_id_fkey'
columns: ['package_id']
isOneToOne: false
referencedRelation: 'packages'
referencedColumns: ['id']
},
]
}
package_versions: {
Expand All @@ -191,13 +155,6 @@ export type Database = {
referencedRelation: 'packages'
referencedColumns: ['id']
},
{
foreignKeyName: 'package_versions_package_id_fkey'
columns: ['package_id']
isOneToOne: false
referencedRelation: 'packages'
referencedColumns: ['id']
},
]
}
packages: {
Expand All @@ -214,15 +171,7 @@ export type Database = {
package_name: string | null
partial_name: string | null
}
Relationships: [
{
foreignKeyName: 'packages_handle_fkey'
columns: ['handle']
isOneToOne: false
referencedRelation: 'handle_registry'
referencedColumns: ['handle']
},
]
Relationships: []
}
}
Functions: {
Expand All @@ -242,6 +191,32 @@ export type Database = {
Args: Record<PropertyKey, never>
Returns: unknown[]
}
get_account: {
Args: {
handle: string
}
Returns: {
avatar_path: string | null
bio: string | null
created_at: string | null
display_name: string | null
handle: string | null
id: string | null
}[]
}
get_organization: {
Args: {
handle: string
}
Returns: {
avatar_path: string | null
bio: string | null
created_at: string | null
display_name: string | null
handle: string | null
id: string | null
}[]
}
new_access_token: {
Args: {
token_name: string
Expand Down Expand Up @@ -421,6 +396,7 @@ export type Database = {
owner_id: string | null
path_tokens: string[] | null
updated_at: string | null
user_metadata: Json | null
version: string | null
}
Insert: {
Expand All @@ -434,6 +410,7 @@ export type Database = {
owner_id?: string | null
path_tokens?: string[] | null
updated_at?: string | null
user_metadata?: Json | null
version?: string | null
}
Update: {
Expand All @@ -447,6 +424,7 @@ export type Database = {
owner_id?: string | null
path_tokens?: string[] | null
updated_at?: string | null
user_metadata?: Json | null
version?: string | null
}
Relationships: [
Expand All @@ -468,6 +446,7 @@ export type Database = {
key: string
owner_id: string | null
upload_signature: string
user_metadata: Json | null
version: string
}
Insert: {
Expand All @@ -478,6 +457,7 @@ export type Database = {
key: string
owner_id?: string | null
upload_signature: string
user_metadata?: Json | null
version: string
}
Update: {
Expand All @@ -488,6 +468,7 @@ export type Database = {
key?: string
owner_id?: string | null
upload_signature?: string
user_metadata?: Json | null
version?: string
}
Relationships: [
Expand Down Expand Up @@ -624,6 +605,10 @@ export type Database = {
updated_at: string
}[]
}
operation: {
Args: Record<PropertyKey, never>
Returns: string
}
search: {
Args: {
prefix: string
Expand Down Expand Up @@ -735,3 +720,18 @@ export type Enums<
: PublicEnumNameOrOptions extends keyof PublicSchema['Enums']
? PublicSchema['Enums'][PublicEnumNameOrOptions]
: never

export type CompositeTypes<
PublicCompositeTypeNameOrOptions extends
| keyof PublicSchema['CompositeTypes']
| { schema: keyof Database },
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
schema: keyof Database
}
? keyof Database[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes']
: never = never,
> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
? Database[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName]
: PublicCompositeTypeNameOrOptions extends keyof PublicSchema['CompositeTypes']
? PublicSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions]
: never
18 changes: 4 additions & 14 deletions website/data/profiles/profile-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@ import {
import { useCallback } from 'react'
import { getAvatarUrl } from '~/lib/avatars'
import supabase from '~/lib/supabase'
import { NonNullableObject } from '~/lib/types'
import { Database } from '../database.types'
import { NotFoundError } from '../utils'

export type ProfileVariables = {
handle?: string
}

export type ProfileResponse =
| NonNullableObject<Database['public']['Views']['accounts']['Row']>
| NonNullableObject<Database['public']['Views']['organizations']['Row']>

export async function getProfile(
{ handle }: ProfileVariables,
signal?: AbortSignal
Expand All @@ -28,12 +22,8 @@ export async function getProfile(
throw new Error('handle is required')
}

let accountQuery = supabase.from('accounts').select('*').eq('handle', handle)

let organizationQuery = supabase
.from('organizations')
.select('*')
.eq('handle', handle)
let accountQuery = supabase.rpc('get_account', { handle })
let organizationQuery = supabase.rpc('get_organization', { handle })

if (signal) {
accountQuery = accountQuery.abortSignal(signal)
Expand All @@ -44,8 +34,8 @@ export async function getProfile(
{ data: account, error: accountError },
{ data: organization, error: organizationError },
] = await Promise.all([
accountQuery.maybeSingle<ProfileResponse>(),
organizationQuery.maybeSingle<ProfileResponse>(),
accountQuery.maybeSingle(),
organizationQuery.maybeSingle(),
])

if (accountError) {
Expand Down
12 changes: 8 additions & 4 deletions website/data/static-path-queries.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import supabase from '~/lib/supabase'
import supabaseAdmin from '~/lib/supabase-admin'

// [Alaister]: These functions are to be called server side only
// as they bypass RLS. They will not work client side.

export async function getAllProfiles() {
const [{ data: organizations }, { data: accounts }] = await Promise.all([
supabase
supabaseAdmin
.from('organizations')
.select('handle')
.order('created_at', { ascending: false })
.limit(500)
.returns<{ handle: string }[]>(),
supabase
supabaseAdmin
.from('accounts')
.select('handle')
.order('created_at', { ascending: false })
Expand All @@ -20,7 +23,8 @@ export async function getAllProfiles() {
}

export async function getAllPackages() {
const { data } = await supabase
const { data } = await supabaseAdmin

.from('packages')
.select('handle,partial_name')
.order('created_at', { ascending: false })
Expand Down
17 changes: 17 additions & 0 deletions website/lib/supabase-admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createClient } from '@supabase/supabase-js'
import { Database } from '~/data/database.types'

if (!process.env.NEXT_PUBLIC_SUPABASE_URL) {
throw new Error('Missing NEXT_PUBLIC_SUPABASE_URL environment variable')
}

if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
throw new Error('Missing SUPABASE_SERVICE_ROLE_KEY environment variable')
}

const supabaseAdmin = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY
)

export default supabaseAdmin
2 changes: 1 addition & 1 deletion website/next.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** @type {import('next').NextConfig} */

const cspHeader = `
default-src 'self' ${process.env.NEXT_PUBLIC_SUPABASE_URL};
default-src 'self' 'unsafe-eval' ${process.env.NEXT_PUBLIC_SUPABASE_URL};
style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com/ https://fonts.google.com/;
img-src 'self' data: ${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/;
object-src 'none';
Expand Down
2 changes: 1 addition & 1 deletion website/pages/[handle]/_/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const EditAccountPage: NextPageWithLayout = () => {
displayName: string
bio: string
}) => {
if (!profile?.id) return console.error('Profile is required')
if (!profile?.handle) return console.error('A profile handle is required')

try {
if (uploadedFile) {
Expand Down