diff --git a/source/server/auth/UserManager.ts b/source/server/auth/UserManager.ts index ecdc72b8..6c245a2e 100644 --- a/source/server/auth/UserManager.ts +++ b/source/server/auth/UserManager.ts @@ -297,12 +297,12 @@ export default class UserManager { return (await this.db.get(` SELECT COALESCE( json_extract(access, '$.' || $uid), - ${0 < uid? `json_extract(access, '$.1'),`:""} + ${((0 < uid) ? `json_extract(access, '$.1'),`:"")} json_extract(access, '$.0') ) AS access FROM scenes WHERE scene_name = $scene - `, {$scene:scene, $uid:uid.toString(10)}))?.access; + `, {$scene:scene, $uid:uid.toString(10)}))?.access ?? null; } /** diff --git a/source/server/integration.test.ts b/source/server/integration.test.ts index 63dce2b5..b071a8a2 100644 --- a/source/server/integration.test.ts +++ b/source/server/integration.test.ts @@ -52,7 +52,7 @@ describe("Web Server Integration", function(){ await request(this.server).get("/scenes/foo/models/foo.glb").expect(200); }); it("can't create a model", async function(){ - await request(this.server).put("/scenes/bar/models/bar.glb").expect(401); + await request(this.server).put("/scenes/foo/models/bar.glb").expect(401); }); it("can't fetch user list", async function(){ await request(this.server).get("/api/v1/users") @@ -191,8 +191,8 @@ describe("Web Server Integration", function(){ .set("Accept", "") .expect(200); }); - it("can't edit other people's models", async function(){ - await this.agent.put("/scenes/foo/models/foo.glb").send("foo\n").expect(401); + it("can't edit other people's models (obfuscated as 404)", async function(){ + await this.agent.put("/scenes/foo/models/foo.glb").send("foo\n").expect(404); }); it.skip("can be granted permissions", async function(){ diff --git a/source/server/routes/api/v1/scenes/scene/get.test.ts b/source/server/routes/api/v1/scenes/scene/get.test.ts index a40da9c1..a26e6a93 100644 --- a/source/server/routes/api/v1/scenes/scene/get.test.ts +++ b/source/server/routes/api/v1/scenes/scene/get.test.ts @@ -36,10 +36,10 @@ describe("GET /api/v1/scenes/:scene", function(){ .expect("Content-Type", "application/json; charset=utf-8"); }); - it("is access-protected", async function(){ + it("is access-protected (obfuscated as 404)", async function(){ await userManager.grant("foo", "default", "none"); await request(this.server).get("/api/v1/scenes/foo") - .expect(401); + .expect(404); }); }); diff --git a/source/server/routes/scenes/get/file.test.ts b/source/server/routes/scenes/get/file.test.ts index 7a083dad..10bb4722 100644 --- a/source/server/routes/scenes/get/file.test.ts +++ b/source/server/routes/scenes/get/file.test.ts @@ -37,13 +37,13 @@ describe("GET /scenes/:scene/:filename(.*)", function(){ .expect("foo\n"); }); - it("can't get a private scene's file", async function(){ + it("can't get a private scene's file (obfuscated as 404)", async function(){ let scene_id = await vfs.createScene("foo", {"0":"none", [user.uid]: "admin"}); await vfs.writeDoc("{}", scene_id, user.uid); await vfs.writeFile(dataStream(), {scene: "foo", mime:"model/gltf-binary", name: "models/foo.glb", user_id: user.uid}); await request(this.server).get("/scenes/foo/models/foo.glb") - .expect(401); + .expect(404); }); it("can get an owned scene's file", async function(){ diff --git a/source/server/server.ts b/source/server/server.ts index 54935970..5c7d3589 100644 --- a/source/server/server.ts +++ b/source/server/server.ts @@ -205,7 +205,10 @@ export default async function createServer(config = defaultConfig) :Promise + Collection + Documentation + +
+ Connexion + +
{{#with error}} -

Error :{{code}}

+

Error :{{code}}

-

{{message}}

+

{{message}}

-
{{stack}}
+
{{stack}}
-{{/with}} \ No newline at end of file +{{/with}} +
+ + + + + \ No newline at end of file diff --git a/source/server/utils/locals.ts b/source/server/utils/locals.ts index 8f7a8bde..0c199ec6 100644 --- a/source/server/utils/locals.ts +++ b/source/server/utils/locals.ts @@ -4,7 +4,7 @@ import {basename, dirname} from "path"; import User, { SafeUser } from "../auth/User"; import UserManager, { AccessType, AccessTypes } from "../auth/UserManager"; import Vfs, { GetFileParams } from "../vfs"; -import { BadRequestError, ForbiddenError, HTTPError, InternalError, UnauthorizedError } from "./errors"; +import { BadRequestError, ForbiddenError, HTTPError, InternalError, NotFoundError, UnauthorizedError } from "./errors"; export interface AppLocals extends Record{ port :number; @@ -72,11 +72,20 @@ function _perms(check:number,req :Request, res :Response, next :NextFunction){ if(isAdministrator) return next(); let userManager = getUserManager(req); - (res.locals.access? Promise.resolve(res.locals.access): - userManager.getAccessRights(scene, uid).then(a=>{ res.locals.access = a; return a }) + (res.locals.access? + Promise.resolve(res.locals.access) : + userManager.getAccessRights(scene, uid) ).then( access => { - if(check <= AccessTypes.indexOf(access)) next(); - else next(new UnauthorizedError(`user does not have ${AccessTypes[check]} rights on ${scene}`)); + res.locals.access = access; + const lvl = AccessTypes.indexOf(access); + if(check <= lvl){ + next(); + } else if(req.method === "GET" || lvl <= AccessTypes.indexOf("none")){ + next(new NotFoundError(`Can't find scene ${scene}. It may be private or not exist entirely.`)) + } else { + //User has insuficient level but can read the scene + next(new UnauthorizedError(`user does not have ${AccessTypes[check]} rights on ${scene}`)); + } }, next); } diff --git a/source/ui/state/router.ts b/source/ui/state/router.ts index bb56521b..77b1e3f1 100644 --- a/source/ui/state/router.ts +++ b/source/ui/state/router.ts @@ -86,6 +86,7 @@ export function router>(baseClass:T) : T & Con if(!this.inPath(url.pathname) && this.path != "/") return; //Return to bubble up the stack ev.stopPropagation(); //Handle the route change + ev.preventDefault(); if(!url.pathname.endsWith("/")) url.pathname += "/"; window.history.pushState({},"", url); @@ -129,12 +130,17 @@ export function navigate(that :HTMLElement,href ?:string|URL, queries?:Record