theonedev/onedev · warning · UnauthorizedException

Permission denied

Error message

Permission denied

What it means

BoardColumnPanel's card drag/drop AJAX handler performs the same server-side authorization as the backlog panel: if canManageIssues(getProject()) is false for the current user it throws UnauthorizedException('Permission denied'). The check runs before any issue mutation from the POST parameters.

Source

Thrown at server-core/src/main/java/io/onedev/server/web/page/project/issues/boards/BoardColumnPanel.java:366

			@Override
			protected void onConfigure() {
				super.onConfigure();
				setVisible(getQuery() != null 
						&& SecurityUtils.getAuthUser() != null
						&& (!getBoard().getIdentifyField().equals(Issue.NAME_STATE) 
								|| getColumn().equals(getIssueSetting().getInitialStateSpec().getName())));
			}
			
		});
		
		head.add(countLabel = new Label("count", countModel).setOutputMarkupId(true));
		
		add(ajaxBehavior = new AbstractPostAjaxBehavior() {

			@Override
			protected void respond(AjaxRequestTarget target) {
				if (!canManageIssues(getProject()))
					throw new UnauthorizedException(_T("Permission denied"));
				
				IRequestParameters params = RequestCycle.get().getRequest().getPostParameters();
				var issueId = params.getParameterValue("issueId").toLong();
				var cardIndex = params.getParameterValue("cardIndex").toInt();
				var card = cardListPanel.findCard(issueId);
				if (card != null) { // Move in same column
					cardListPanel.onCardDropped(target, issueId, cardIndex, true);
				} else {
					var subject = SecurityUtils.getSubject();
					var user = SecurityUtils.getUser(subject);
					Issue issue = getIssueService().load(issueId);
					String fieldName = getBoard().getIdentifyField();
					var iteration = getIterationSelection().getIteration();
					if (iteration != null && !issue.getIterations().contains(iteration)) {
						getIssueChangeService().addSchedule(user, issue, iteration);
						cardListPanel.onCardDropped(target, issueId, cardIndex, true);
					} else if (fieldName.equals(Issue.NAME_STATE)) {
						AtomicReference<ManualSpec> transitionRef = new AtomicReference<>(null);

View on GitHub (pinned to d44925c47c)

Solutions

  1. Assign the user/group a role with issue-management permission in the project's security settings.
  2. Re-login (or reload the page) after permission changes so the subject picks up new roles.
  3. Verify the board is in a project the user actually has edit rights on; move the board/work to an accessible project otherwise.
Defensive patterns

Strategy: try-catch

Validate before calling

if (!SecurityUtils.canManageIssues(project)) { /* disable card drag in UI */ }

Try / catch

try { /* perform card move */ } catch (UnauthorizedException e) { notifyUser("Permission denied: ask for issue-management rights on this project"); }

Prevention

When it happens

Trigger: AJAX card move/drop (issueId/cardIndex POST) on a board column by a user lacking issue-management permission on the project.

Common situations: Read-only reporters/viewers attempting to reorder board cards; expired permission after role change while board page remains open; automated clients replaying board requests.

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