theonedev/onedev · warning · UnauthorizedException

Permission denied

Error message

Permission denied

What it means

BacklogColumnPanel's AJAX behavior (drag/drop of issue cards on the issue board) re-checks authorization server-side on every request: if the current subject cannot manage issues in the project (SecurityUtils.canManageIssues), it throws UnauthorizedException('Permission denied'). Client-side drag affordances are cosmetic; the server enforces the real rule.

Source

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

			add(new WebMarkupContainer("showProgress").setVisible(false));
		}
		
		if (getQuery() != null) {
			PageParameters params = ProjectIssueListPage.paramsOf(getProject(), getQuery().toString(), 0);
			add(new BookmarkablePageLink<Void>("viewAsList", ProjectIssueListPage.class, params));
		} else {
			add(new WebMarkupContainer("viewAsList").setVisible(false));
		}
		
		add(countLabel = new Label("count", countModel).setOutputMarkupId(true));
		
		add(ajaxBehavior = new AbstractPostAjaxBehavior() {
			
			@Override
			protected void respond(AjaxRequestTarget target) {
				var subject = SecurityUtils.getSubject();
				if (!canManageIssues(subject, 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) { // moved from other columns
					var issue = getIssueService().load(issueId);
					var user = SecurityUtils.getUser(subject);
					for (var iteration: getProject().getHierarchyIterations()) {
						if (getIterationPrefix() == null || iteration.getName().startsWith(getIterationPrefix()))
							getIssueChangeService().removeSchedule(user, issue, iteration);
					}
				}
				cardListPanel.onCardDropped(target, issueId, cardIndex, true);
			}
			
		});

View on GitHub (pinned to d44925c47c)

Solutions

  1. Grant the user role/permission 'Manage Issues' (or appropriate issue-board editing permission) in Project -> Access/Authorization.
  2. Log in as a user with issue-management rights before reordering board cards.
  3. If users should only reorder their own issues, adjust the board/issue permission setup rather than bypassing the check.

Example fix

// before (server logs)
UnauthorizedException: Permission denied
// after (admin action)
Project -> Authorization -> add role with 'Manage issues' permission for the user/group
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side pre-check before issuing the AJAX move
boolean canManage = OneDev.getInstance(Session.class) /* or UI check */ != null && SecurityUtils.canManageIssues(SecurityUtils.getSubject(), project);
if (!canManage) alert("You need 'Manage Issues' permission to reorder board cards");

Try / catch

onAjaxError: function(err) {
  if (err.message.includes('Permission denied')) {
    showNotice('You lack permission to manage issues in this project.');
  }
}

Prevention

When it happens

Trigger: POSTing the board's AJAX callback (moving/dropping an issue card in the backlog column) while logged in as a user without 'Manage Issues' / board-editing permission on that project.

Common situations: Non-admin users dragging cards on a board where they have read-only access; session switched to a lower-privilege user while a board page stays open; API/script replaying board AJAX calls.

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