diff options
| author | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
| commit | 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch) | |
| tree | b9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/permissions | |
| download | umami-396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b.tar.xz umami-396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b.zip | |
Created from https://vercel.com/new
Diffstat (limited to 'src/permissions')
| -rw-r--r-- | src/permissions/index.ts | 6 | ||||
| -rw-r--r-- | src/permissions/link.ts | 64 | ||||
| -rw-r--r-- | src/permissions/pixel.ts | 64 | ||||
| -rw-r--r-- | src/permissions/report.ts | 27 | ||||
| -rw-r--r-- | src/permissions/team.ts | 68 | ||||
| -rw-r--r-- | src/permissions/user.ts | 29 | ||||
| -rw-r--r-- | src/permissions/website.ts | 128 |
7 files changed, 386 insertions, 0 deletions
diff --git a/src/permissions/index.ts b/src/permissions/index.ts new file mode 100644 index 0000000..a70808e --- /dev/null +++ b/src/permissions/index.ts @@ -0,0 +1,6 @@ +export * from './link'; +export * from './pixel'; +export * from './report'; +export * from './team'; +export * from './user'; +export * from './website'; diff --git a/src/permissions/link.ts b/src/permissions/link.ts new file mode 100644 index 0000000..c027a0b --- /dev/null +++ b/src/permissions/link.ts @@ -0,0 +1,64 @@ +import { hasPermission } from '@/lib/auth'; +import { PERMISSIONS } from '@/lib/constants'; +import type { Auth } from '@/lib/types'; +import { getLink, getTeamUser } from '@/queries/prisma'; + +export async function canViewLink({ user }: Auth, linkId: string) { + if (user?.isAdmin) { + return true; + } + + const link = await getLink(linkId); + + if (link.userId) { + return user.id === link.userId; + } + + if (link.teamId) { + const teamUser = await getTeamUser(link.teamId, user.id); + + return !!teamUser; + } + + return false; +} + +export async function canUpdateLink({ user }: Auth, linkId: string) { + if (user.isAdmin) { + return true; + } + + const link = await getLink(linkId); + + if (link.userId) { + return user.id === link.userId; + } + + if (link.teamId) { + const teamUser = await getTeamUser(link.teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteUpdate); + } + + return false; +} + +export async function canDeleteLink({ user }: Auth, linkId: string) { + if (user.isAdmin) { + return true; + } + + const link = await getLink(linkId); + + if (link.userId) { + return user.id === link.userId; + } + + if (link.teamId) { + const teamUser = await getTeamUser(link.teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteDelete); + } + + return false; +} diff --git a/src/permissions/pixel.ts b/src/permissions/pixel.ts new file mode 100644 index 0000000..2131874 --- /dev/null +++ b/src/permissions/pixel.ts @@ -0,0 +1,64 @@ +import { hasPermission } from '@/lib/auth'; +import { PERMISSIONS } from '@/lib/constants'; +import type { Auth } from '@/lib/types'; +import { getPixel, getTeamUser } from '@/queries/prisma'; + +export async function canViewPixel({ user }: Auth, pixelId: string) { + if (user?.isAdmin) { + return true; + } + + const pixel = await getPixel(pixelId); + + if (pixel.userId) { + return user.id === pixel.userId; + } + + if (pixel.teamId) { + const teamUser = await getTeamUser(pixel.teamId, user.id); + + return !!teamUser; + } + + return false; +} + +export async function canUpdatePixel({ user }: Auth, pixelId: string) { + if (user.isAdmin) { + return true; + } + + const pixel = await getPixel(pixelId); + + if (pixel.userId) { + return user.id === pixel.userId; + } + + if (pixel.teamId) { + const teamUser = await getTeamUser(pixel.teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteUpdate); + } + + return false; +} + +export async function canDeletePixel({ user }: Auth, pixelId: string) { + if (user.isAdmin) { + return true; + } + + const pixel = await getPixel(pixelId); + + if (pixel.userId) { + return user.id === pixel.userId; + } + + if (pixel.teamId) { + const teamUser = await getTeamUser(pixel.teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteDelete); + } + + return false; +} diff --git a/src/permissions/report.ts b/src/permissions/report.ts new file mode 100644 index 0000000..01b5476 --- /dev/null +++ b/src/permissions/report.ts @@ -0,0 +1,27 @@ +import type { Report } from '@/generated/prisma/client'; +import type { Auth } from '@/lib/types'; +import { canViewWebsite } from './website'; + +export async function canViewReport(auth: Auth, report: Report) { + if (auth.user.isAdmin) { + return true; + } + + if (auth.user.id === report.userId) { + return true; + } + + return !!(await canViewWebsite(auth, report.websiteId)); +} + +export async function canUpdateReport({ user }: Auth, report: Report) { + if (user.isAdmin) { + return true; + } + + return user.id === report.userId; +} + +export async function canDeleteReport(auth: Auth, report: Report) { + return canUpdateReport(auth, report); +} diff --git a/src/permissions/team.ts b/src/permissions/team.ts new file mode 100644 index 0000000..0f07c1a --- /dev/null +++ b/src/permissions/team.ts @@ -0,0 +1,68 @@ +import { hasPermission } from '@/lib/auth'; +import { PERMISSIONS } from '@/lib/constants'; +import type { Auth } from '@/lib/types'; +import { getTeamUser } from '@/queries/prisma'; + +export async function canViewTeam({ user }: Auth, teamId: string) { + if (user.isAdmin) { + return true; + } + + return getTeamUser(teamId, user.id); +} + +export async function canCreateTeam({ user }: Auth) { + if (user.isAdmin) { + return true; + } + + return !!user; +} + +export async function canUpdateTeam({ user }: Auth, teamId: string) { + if (user.isAdmin) { + return true; + } + + const teamUser = await getTeamUser(teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.teamUpdate); +} + +export async function canDeleteTeam({ user }: Auth, teamId: string) { + if (user.isAdmin) { + return true; + } + + const teamUser = await getTeamUser(teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.teamDelete); +} + +export async function canDeleteTeamUser({ user }: Auth, teamId: string, removeUserId: string) { + if (user.isAdmin) { + return true; + } + + if (removeUserId === user.id) { + return true; + } + + const teamUser = await getTeamUser(teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.teamUpdate); +} + +export async function canCreateTeamWebsite({ user }: Auth, teamId: string) { + if (user.isAdmin) { + return true; + } + + const teamUser = await getTeamUser(teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteCreate); +} + +export async function canViewAllTeams({ user }: Auth) { + return user.isAdmin; +} diff --git a/src/permissions/user.ts b/src/permissions/user.ts new file mode 100644 index 0000000..2ed8f27 --- /dev/null +++ b/src/permissions/user.ts @@ -0,0 +1,29 @@ +import type { Auth } from '@/lib/types'; + +export async function canCreateUser({ user }: Auth) { + return user.isAdmin; +} + +export async function canViewUser({ user }: Auth, viewedUserId: string) { + if (user.isAdmin) { + return true; + } + + return user.id === viewedUserId; +} + +export async function canViewUsers({ user }: Auth) { + return user.isAdmin; +} + +export async function canUpdateUser({ user }: Auth, viewedUserId: string) { + if (user.isAdmin) { + return true; + } + + return user.id === viewedUserId; +} + +export async function canDeleteUser({ user }: Auth) { + return user.isAdmin; +} diff --git a/src/permissions/website.ts b/src/permissions/website.ts new file mode 100644 index 0000000..97952ee --- /dev/null +++ b/src/permissions/website.ts @@ -0,0 +1,128 @@ +import { hasPermission } from '@/lib/auth'; +import { PERMISSIONS } from '@/lib/constants'; +import type { Auth } from '@/lib/types'; +import { getLink, getPixel, getTeamUser, getWebsite } from '@/queries/prisma'; + +export async function canViewWebsite({ user, shareToken }: Auth, websiteId: string) { + if (user?.isAdmin) { + return true; + } + + if (shareToken?.websiteId === websiteId) { + return true; + } + + const website = await getWebsite(websiteId); + const link = await getLink(websiteId); + const pixel = await getPixel(websiteId); + + const entity = website || link || pixel; + + if (!entity) { + return false; + } + + if (entity.userId) { + return user.id === entity.userId; + } + + if (entity.teamId) { + const teamUser = await getTeamUser(entity.teamId, user.id); + + return !!teamUser; + } + + return false; +} + +export async function canViewAllWebsites({ user }: Auth) { + return user.isAdmin; +} + +export async function canCreateWebsite({ user }: Auth) { + if (user.isAdmin) { + return true; + } + + return hasPermission(user.role, PERMISSIONS.websiteCreate); +} + +export async function canUpdateWebsite({ user }: Auth, websiteId: string) { + if (user.isAdmin) { + return true; + } + + const website = await getWebsite(websiteId); + + if (!website) { + return false; + } + + if (website.userId) { + return user.id === website.userId; + } + + if (website.teamId) { + const teamUser = await getTeamUser(website.teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteUpdate); + } + + return false; +} + +export async function canDeleteWebsite({ user }: Auth, websiteId: string) { + if (user.isAdmin) { + return true; + } + + const website = await getWebsite(websiteId); + + if (!website) { + return false; + } + + if (website.userId) { + return user.id === website.userId; + } + + if (website.teamId) { + const teamUser = await getTeamUser(website.teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteDelete); + } + + return false; +} + +export async function canTransferWebsiteToUser({ user }: Auth, websiteId: string, userId: string) { + const website = await getWebsite(websiteId); + + if (!website) { + return false; + } + + if (website.teamId && user.id === userId) { + const teamUser = await getTeamUser(website.teamId, userId); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteTransferToUser); + } + + return false; +} + +export async function canTransferWebsiteToTeam({ user }: Auth, websiteId: string, teamId: string) { + const website = await getWebsite(websiteId); + + if (!website) { + return false; + } + + if (website.userId && website.userId === user.id) { + const teamUser = await getTeamUser(teamId, user.id); + + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteTransferToTeam); + } + + return false; +} |