theonedev/onedev · error · UnauthorizedException

Unauthorized

Error message

Unauthorized

What it means

For non-system callers lacking a valid encrypted 'authorization' parameter, AttachmentResource authorizes the attachment group by entity type: pull request/code comment groups require canReadCode(project), issue groups require canAccessIssue(issue), build groups require canAccessProject(build.getProject()), and unknown groups fall back to canAccessProject(project). Any failed check throws Shiro UnauthorizedException.

Source

Thrown at server-core/src/main/java/io/onedev/server/web/resource/AttachmentResource.java:81

		String attachmentGroup = params.get(PARAM_ATTACHMENT_GROUP).toString();
		
		if (StringUtils.isBlank(attachmentGroup))
			throw new IllegalArgumentException("Parameter 'attachment-group' has to be specified");
		else if (attachmentGroup.contains(".."))
			throw new IllegalArgumentException("Invalid parameter 'attachment-group'");

		if (!SecurityUtils.isSystem()) {
			Project project = OneDev.getInstance(ProjectService.class).load(projectId);
			
			String authorization = params.get(PARAM_AUTHORIZATION).toOptionalString();
			if (authorization == null 
					|| !new String(CryptoUtils.decrypt(Base64.decodeBase64(authorization)), UTF_8).equals(attachmentGroup)) {
				Issue issue;
				Build build;
				if (OneDev.getInstance(PullRequestService.class).find(attachmentGroup) != null
						|| OneDev.getInstance(CodeCommentService.class).findByUUID(attachmentGroup) != null) {
					if (!SecurityUtils.canReadCode(project))
						throw new UnauthorizedException();
				} else if ((issue = OneDev.getInstance(IssueService.class).find(attachmentGroup)) != null) {
					if (!SecurityUtils.canAccessIssue(issue))
						throw new UnauthorizedException();
				} else if ((build = OneDev.getInstance(BuildService.class).find(attachmentGroup)) != null) {
					if (!SecurityUtils.canAccessProject(build.getProject()))
						throw new UnauthorizedException();
				} else if (!SecurityUtils.canAccessProject(project)) {
					throw new UnauthorizedException();
				}
			}
		}

		String attachment = params.get(PARAM_ATTACHMENT).toString();
		if (StringUtils.isBlank(attachment))
			throw new IllegalArgumentException("attachment parameter has to be specified");
		else if (attachment.contains(".."))
			throw new IllegalArgumentException("Invalid attachment parameter");

View on GitHub (pinned to d44925c47c)

Solutions

  1. Request access: ask an admin to grant code-read (for PR/comment attachments), issue access, or project access as appropriate to the attachment's origin
  2. For CI, use the same project's job token or pass a valid 'authorization' parameter (the encrypted, Base64 token OneDev generates for the group)
  3. Verify the attachment-group value actually refers to the entity you intend; an unknown group falls back to plain project access check
  4. Re-obtain the link from the OneDev UI as the current user to confirm the entity and your access
Defensive patterns

Strategy: validation

Validate before calling

const canRead = await onedevApi.get(`/projects/${projectId}/permissions`);
if (!canRead.canReadCode && !canRead.canAccess) throw new Error('insufficient permissions for this attachment group; request code read / issue access / project access as appropriate');

Try / catch

try { await fetch(attachmentUrl); } catch (e) { if (e.status === 403) console.error('Unauthorized for attachment group: acquire the proper entity/project access or a valid encrypted authorization token'); }

Prevention

When it happens

Trigger: An authenticated user without the needed permission requests an attachment: e.g. an issue attachment the user cannot access, a build attachment in a project they cannot access, or a group the user cannot resolve at all.

Common situations: Sharing attachment links with users lacking issue/project access; job attachments fetched with a foreign project's token; code comment attachments fetched by users without code read; stale links after permissions were tightened.

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/f06a6cb3f476edc6. Report an issue: GitHub.