theonedev/onedev · error · UnauthorizedException

Unauthorized

Error message

Unauthorized

What it means

PatchResource serves generated patch files for project commits. Outside the system security context it loads the project and throws UnauthorizedException when SecurityUtils.canReadCode(project) is false — patch content is considered code, so code read permission is mandatory. The resource framework returns it as HTTP 401/403 "Unauthorized".

Source

Thrown at server-core/src/main/java/io/onedev/server/web/resource/PatchResource.java:65

	protected ResourceResponse newResourceResponse(Attributes attributes) {
		PageParameters params = attributes.getParameters();
		
		Long projectId = params.get(PARAM_PROJECT).toLong();
		var oldCommitId = ObjectId.fromString(params.get(PARAM_OLD_COMMIT).toString());
		var newCommitId = ObjectId.fromString(params.get(PARAM_NEW_COMMIT).toString());
		var forCodeReview = params.get(PARAM_FOR_CODE_REVIEW).toBoolean(false);
		
		String excludedFiles;
		if (forCodeReview) {
			Project project = getProjectService().load(projectId);
			excludedFiles = project.findExcludedAiReviewFiles();
		} else {
			excludedFiles = null;
		}
		if (!SecurityUtils.isSystem()) {
			Project project = getProjectService().load(projectId);
			if (!SecurityUtils.canReadCode(project))
				throw new UnauthorizedException();
		}

		ResourceResponse response = new ResourceResponse();
		
		response.getHeaders().addHeader("X-Content-Type-Options", "nosniff");
		response.setContentType(MimeTypes.OCTET_STREAM);

		response.setFileName(URLEncoder.encode("changes.patch", UTF_8));

		response.setWriteCallback(new WriteCallback() {

			@Override
			public void writeData(Attributes attributes) throws IOException {
				String activeServer = getProjectService().getActiveServer(projectId, true);
				if (activeServer.equals(getClusterService().getLocalServerAddress())) {
					try (var os = attributes.getResponse().getOutputStream()) {
						var repository = getProjectService().getRepository(projectId);
						GitUtils.diff(repository, oldCommitId, newCommitId, excludedFiles, os);

View on GitHub (pinned to d44925c47c)

Solutions

  1. Authenticate as a user with 'Read Code' permission on the project.
  2. Grant the requesting user/role Read Code in project access settings.
  3. If fetching programmatically, use an access token with code read scope.

Example fix

// before: anonymous fetch of patch -> 401
curl https://onedev.example.com/~resource/patches/1/abcdef
// after
curl -u user:password https://onedev.example.com/~resource/patches/1/abcdef
Defensive patterns

Strategy: validation

Validate before calling

// ensure the caller can read code before requesting patches
if (!userCan('READ_CODE', projectId)) {
  throw new Error('fetching patches requires Read Code permission on project ' + projectId);
}

Try / catch

try { fetchPatch(url); } catch (HttpException e) { if (e.getStatusCode() === 401 || e.getStatusCode() === 403) promptForCredentials(); else throw e; }

Prevention

When it happens

Trigger: GET of the patch resource URL (e.g. /~resource/patches/...) by a user without Read Code permission on the project — anonymous access to a private project, or a role limited to issues/builds.

Common situations: Sharing patch links externally while the repository is private; bots/crawlers hitting patch URLs; a formerly public project made private so previously working links start failing.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/44e3ad66ff26e094. Report an issue: GitHub.