Path: blob/master/src/packages/conat/persist/auth.ts
1452 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): 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,23}: {24// Subject definitely has one of the following forms, or we would never25// see this message:26// ${SERVICE}.account-${account_id}.> or27// ${SERVICE}.project-${project_id}.> or28// ${SERVICE}.hub.>29// ${SERVICE}.SOMETHING-WRONG30// A user is only allowed to write to a subject if they have rights31// to the given project, account or are a hub.32// The path can a priori be any string. However, here's what's allowed33// accounts/[account_id]/any...thing34// projects/[project_id]/any...thing35// hub/any...thing <- only hub can write to this.36// Also, we don't allow malicious paths, which means by definition that37// normalize(path) != path.38// This is to avoid accidentally writing a file to different project, which39// would be very bad.40subject: string;41path: string;42}) {43if (path != normalize(path)) {44throw Error(`permission denied: path '${path}' is not normalized`);45}46if (path.length > MAX_PATH_LENGTH) {47throw new ConatError(48`permission denied: path (of length ${path.length}) is too long (limit is '${MAX_PATH_LENGTH}' characters)`,49{ code: 403 },50);51}52if (path.startsWith("/") || path.endsWith("/")) {53throw new ConatError(54`permission denied: path '${path}' must not start or end with '/'`,55{ code: 403 },56);57}58const v = subject.split(".");59if (v[0] != SERVICE) {60throw Error(61`bug -- first segment of subject must be ${SERVICE} -- subject=${subject}`,62);63}64const s = v[1];65if (s == "hub") {66// hub user can write to any path67return;68}69for (const cls of ["account", "project"]) {70if (s.startsWith(cls + "-")) {71const user_id = getUserId(subject);72const base = cls + "s/" + user_id + "/";73if (path.startsWith(base)) {74// permissions granted75return;76} else {77throw new ConatError(78`permission denied: subject '${subject}' does not grant write permission to path='${path}' since it is not under '${base}'`,79{ code: 403 },80);81}82}83}84throw new ConatError(`invalid subject: '${subject}'`, { code: 403 });85}868788