{"record":{"id":"7c23ba054a557c2b","repo":"theonedev/onedev","slug":"not-authorized-7c23ba","errorCode":null,"errorMessage":"Not authorized","messagePattern":"Not authorized","errorType":"http","errorClass":"UnauthorizedException","httpStatus":403,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/rest/resource/UserResource.java","lineNumber":129,"sourceCode":"\t\tdata.setId(user.getId());\n\t\tdata.setDisabled(user.isDisabled());\n\t\tdata.setType(user.getType());\n\t\tdata.setName(user.getName());\n\t\tdata.setFullName(user.getFullName());\n\t\tif (user.getType() != SERVICE) \n\t\t\tdata.setNotifyOwnEvents(user.isNotifyOwnEvents());\n\t\tif (user.getType() == AI)\n\t\t\tdata.setAiSetting(user.getAiSetting());\n\t\treturn data;\n\t}\n\n\t@Api(order=100)\n\t@Path(\"/{userId}\")\n    @GET\n    public UserData getUser(@PathParam(\"userId\") Long userId) {\n    \tUser user = userService.load(userId);\n    \tif (!SecurityUtils.isAdministrator() && !user.equals(getAuthUser())) \n\t\t\tthrow new UnauthorizedException();\n\t\treturn getData(user);\n    }\n\n\t@Api(order=200)\n\t@Path(\"/me\")\n    @GET\n    public UserData getMe() {\n\t\tUser user = getAuthUser();\n\t\tif (user == null)\n\t\t\tthrow new UnauthorizedException();\n\t\treturn getData(user);\n    }\n\t\n\t@Api(order=250)\n\t@Path(\"/{userId}/access-tokens\")\n    @GET\n    public Collection<AccessToken> getAccessTokens(@PathParam(\"userId\") Long userId) {\n    \tUser user = userService.load(userId);","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/rest/resource/UserResource.java#L111-L147","documentation":"getUser (GET /~api/users/{userId} in UserResource) returns profile data only to administrators or the user themself. OneDev throws UnauthorizedException ('Not authorized', HTTP 403) for any other authenticated or anonymous caller. This protects personal user data from being enumerated via the REST API.","triggerScenarios":"GET /~api/users/{userId} where the authenticated user is not an administrator and userId does not equal the id of the authenticated user (getAuthUser()).","commonSituations":"A non-admin token querying another user's profile; scripts hardcoding a userId that differs from the token owner's id; calling without authentication so getAuthUser() is not admin and matches nobody; assuming any project manager can read all users (only admins can).","solutions":["Query your own user id (find it via GET /~api/users/me) with your token.","Use an administrator's access token when you need other users' data.","Confirm the token is being sent (an anonymous request fails this check even for self).","If you need only basic info about another user, use the project-scoped member endpoints where visible."],"exampleFix":"// before\ncurl -H \"Authorization: Bearer <user-token>\" https://onedev/~api/users/7 // userId 7 != token owner\n// after\ncurl -H \"Authorization: Bearer <user-token>\" https://onedev/~api/users/me","handlingStrategy":"validation","validationCode":"const me = await fetch(`${baseUrl}/~api/users/me`, { headers }).then(r => r.json());\nif (me.id !== targetUserId) {\n  console.warn('403 expected: only admins may read other users');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const user = await get(`/users/${userId}`);\n} catch (e) {\n  if (e.response?.status === 403) return get('/users/me'); // self fallback\n  throw e;\n}","preventionTips":["Resolve your own id with /users/me before calling user endpoints.","Only request other users' data with admin credentials.","Never assume project-manager rights extend to server-wide user reads.","Check token validity early — anonymous calls fail this endpoint."],"tags":["rest-api","user","permissions","onedev"],"backgroundTag":"permission-denied","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}