Path: blob/master/src/packages/conat/persist/auth.ts
1710 views
import { SERVICE } from "./util";1import { ConatError } from "@cocalc/conat/core/client";2import { normalize } from "path";34export const MAX_PATH_LENGTH = 4000;56export function getUserId(subject: string, service = SERVICE): string {7if (8subject.startsWith(`${service}.account-`) ||9subject.startsWith(`${service}.project-`)10) {11// note that project and account have the same number of letters12return subject.slice(13`${service}.account-`.length,14`${service}.account-`.length + 36,15);16}17return "";18}1920export function assertHasWritePermission({21subject,22path,23service = SERVICE,24}: {25// Subject definitely has one of the following forms, or we would never26// see this message:27// ${service}.account-${account_id}.> or28// ${service}.project-${project_id}.> or29// ${service}.hub.>30// ${service}.SOMETHING-WRONG31// A user is only allowed to write to a subject if they have rights32// to the given project, account or are a hub.33// The path can a priori be any string. However, here's what's allowed34// accounts/[account_id]/any...thing35// projects/[project_id]/any...thing36// hub/any...thing <- only hub can write to this.37// Also, we don't allow malicious paths, which means by definition that38// normalize(path) != path.39// This is to avoid accidentally writing a file to different project, which40// would be very bad.41subject: string;42path: string;43service?: string;44}) {45if (path != normalize(path)) {46throw Error(`permission denied: path '${path}' is not normalized`);47}48if (path.length > MAX_PATH_LENGTH) {49throw new ConatError(50`permission denied: path (of length ${path.length}) is too long (limit is '${MAX_PATH_LENGTH}' characters)`,51{ code: 403 },52);53}54if (path.startsWith("/") || path.endsWith("/")) {55throw new ConatError(56`permission denied: path '${path}' must not start or end with '/'`,57{ code: 403 },58);59}60const v = subject.split(".");61if (v[0] != service) {62throw Error(63`bug -- first segment of subject must be '${service}' -- subject='${subject}'`,64);65}66const s = v[1];67if (s == "hub") {68// hub user can write to any path69return;70}71for (const cls of ["account", "project"]) {72if (s.startsWith(cls + "-")) {73const user_id = getUserId(subject, service);74const base = cls + "s/" + user_id + "/";75if (path.startsWith(base)) {76// permissions granted77return;78} else {79throw new ConatError(80`permission denied: subject '${subject}' does not grant write permission to path='${path}' since it is not under '${base}'`,81{ code: 403 },82);83}84}85}86throw new ConatError(`invalid subject: '${subject}'`, { code: 403 });87}888990