{"record":{"id":"ad3ff13ed33dd7b9","repo":"theonedev/onedev","slug":"no-permission-to-comment-issue","errorCode":null,"errorMessage":"No permission to comment issue: ","messagePattern":"No permission to comment issue: ","errorType":"exception","errorClass":"UnauthorizedException","httpStatus":403,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/mail/DefaultMailService.java","lineNumber":520,"sourceCode":"\t\t\t\t\t\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tString subject = \"Unsubscribed successfully from issue \" + involvedIssue.getReference();\n\t\t\t\t\t\t\t\t\t\tString template = settingService.getEmailTemplates().getIssueNotificationUnsubscribed();\n\n\t\t\t\t\t\t\t\t\t\tMap<String, Object> bindings = new HashMap<>();\n\t\t\t\t\t\t\t\t\t\tbindings.put(\"issue\", involvedIssue);\n\n\t\t\t\t\t\t\t\t\t\tString htmlBody = EmailTemplates.evalTemplate(true, template, bindings);\n\t\t\t\t\t\t\t\t\t\tString textBody = EmailTemplates.evalTemplate(false, template, bindings);\n\n\t\t\t\t\t\t\t\t\t\tvar threadingReferences = getThreadingReferences(UUID.randomUUID().toString(), getMessageId(message));\n\t\t\t\t\t\t\t\t\t\tsendMailAsync(newArrayList(fromInternetAddress.getAddress()), newArrayList(), newArrayList(),\n\t\t\t\t\t\t\t\t\t\t\t\tsubject, htmlBody, textBody, null, null, threadingReferences);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tif (fromUser != null) {\n\t\t\t\t\t\t\t\t\t\t\tif (SecurityUtils.canAccessIssue(fromUser.asSubject(), involvedIssue))\t\n\t\t\t\t\t\t\t\t\t\t\t\taddComment(involvedIssue, message, fromInternetAddress, fromUser, receiverInternetAddresses);\n\t\t\t\t\t\t\t\t\t\t\telse \n\t\t\t\t\t\t\t\t\t\t\t\tthrow new UnauthorizedException(\"No permission to comment issue: \" + involvedIssue.getReference());\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tif (involvedIssue.getExternalParticipants().contains(fromInternetAddress)) \t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\taddComment(involvedIssue, message, fromInternetAddress, null, receiverInternetAddresses);\n\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\tthrow new UnauthorizedException(\"Not eligible to comment issue: \" + involvedIssue.getReference());\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else if (subAddress.contains(\"pullrequest\")) {\n\t\t\t\t\t\t\t\t\tif (fromUser != null) {\n\t\t\t\t\t\t\t\t\t\tinvolvedPullRequest = pullRequestService.get(entityId);\n\t\t\t\t\t\t\t\t\t\tif (involvedPullRequest == null)\n\t\t\t\t\t\t\t\t\t\t\tthrow new ExplicitException(\"Non-existent pull request specified in recipient address: \" + parsedReceiverAddress);\n\t\t\t\t\t\t\t\t\t\tif (subAddress.contains(\"unsubscribe\")) {\n\t\t\t\t\t\t\t\t\t\t\tPullRequestWatch watch = pullRequestWatchService.find(involvedPullRequest, fromUser);\n\t\t\t\t\t\t\t\t\t\t\tif (watch != null) \n\t\t\t\t\t\t\t\t\t\t\t\twatch.setWatching(false);\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\tString subject = \"Unsubscribed successfully from pull request \" + involvedPullRequest.getReference().toString(null);","sourceCodeStart":502,"sourceCodeEnd":538,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/mail/DefaultMailService.java#L502-L538","documentation":"When replying by email to an issue subaddress, if the sender maps to a known OneDev user lacking access to the issue (SecurityUtils.canAccessIssue fails), an UnauthorizedException 'No permission to comment issue: <ref>' is thrown instead of adding the comment.","triggerScenarios":"Email reply to issue~<id>~comment from a recognized user account whose permissions do not allow reading the issue.","commonSituations":"User's access to the project/issue was revoked after subscribing; reply from a coworker's account without project access; group/role changes restricting confidential issues.","solutions":["Grant the user read access to the issue's project (appropriate role/permissions).","Comment via the OneDev web UI with an account that has access.","If the user should not have access, stop emailing that issue address."],"exampleFix":"// admin: grant access\n// before: user has no role on project 'app'\n// after: assign role with 'Read Issue' permission to the user on project 'app'","handlingStrategy":"try-catch","validationCode":"if (!SecurityUtils.canAccessIssue(user.asSubject(), issue)) throw new UnauthorizedException(\"No permission to comment issue: \" + issue.getReference());","typeGuard":null,"tryCatchPattern":"try { addComment(issue, message, from, user, receivers); } catch (UnauthorizedException e) { log.warn(\"Email comment rejected: {}\", e.getMessage()); }","preventionTips":["Only reply from accounts with project access","Check user roles after permission changes","Use web UI when unsure of access"],"tags":["email","authorization","permissions"],"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"}