{"record":{"id":"ceb5aa15808e0323","repo":"xuxueli/xxl-job","slug":"permission-limit-username","errorCode":null,"errorMessage":"Permission limit[username={}]","messagePattern":"Permission limit\\[username=(.+?)\\]","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"xxl-job-admin/src/main/java/com/xxl/job/admin/framework/util/JobGroupPermissionUtil.java","lineNumber":40,"sourceCode":"     * check if has jobgroup permission\n     */\n    public static boolean hasJobGroupPermission(LoginInfo loginInfo, int jobGroup){\n        if (XxlSsoHelper.hasRole(loginInfo, Consts.ADMIN_ROLE).isSuccess()) {\n            return true;\n        } else {\n            List<String> jobGroups = (loginInfo.getExtraInfo()!=null && loginInfo.getExtraInfo().containsKey(\"jobGroups\"))\n                    ? StringTool.split(loginInfo.getExtraInfo().get(\"jobGroups\"), \",\") :new ArrayList<>();\n            return jobGroups.contains(String.valueOf(jobGroup));\n        }\n    }\n\n    /**\n     * valid jobGroup permission\n     */\n    public static LoginInfo validJobGroupPermission(HttpServletRequest request, int jobGroup) {\n        Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);\n        if (!(loginInfoResponse.isSuccess() && hasJobGroupPermission(loginInfoResponse.getData(), jobGroup))) {\n            throw new RuntimeException(I18nUtil.getString(\"system_permission_limit\") + \"[username=\"+ loginInfoResponse.getData().getUserName() +\"]\");\n        }\n        return loginInfoResponse.getData();\n    }\n\n    /**\n     * filter jobGroupList by permission\n     */\n    public static List<XxlJobGroup> filterJobGroupByPermission(HttpServletRequest request, List<XxlJobGroup> jobGroupListTotal){\n        Response<LoginInfo>  loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);\n\n        if (XxlSsoHelper.hasRole(loginInfoResponse.getData(), Consts.ADMIN_ROLE).isSuccess()) {\n            return jobGroupListTotal;\n        } else {\n            List<String> jobGroups = (loginInfoResponse.getData().getExtraInfo()!=null\n                    && loginInfoResponse.getData().getExtraInfo().get(\"jobGroups\")!=null\n            )\n                    ? StringTool.split(loginInfoResponse.getData().getExtraInfo().get(\"jobGroups\"), \",\")\n                    :new ArrayList<>();","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/xuxueli/xxl-job/blob/e74c784f68f81fa89cb350913ef15794865d7b12/xxl-job-admin/src/main/java/com/xxl/job/admin/framework/util/JobGroupPermissionUtil.java#L22-L58","documentation":"Thrown by JobGroupPermissionUtil.validJobGroupPermission as a RuntimeException when the SSO login check fails OR the logged-in user lacks permission for the target jobGroup. The message bundles a localized 'permission limit' string with the username. It guards every admin operation that touches a specific job group.","triggerScenarios":"Calling validJobGroupPermission(request, jobGroup) when loginCheckWithAttr reports failure, or when the user's 'jobGroups' extra-info list does not contain the requested jobGroup id. Triggered on protected admin endpoints (start/stop/trigger/log/etc. per job group).","commonSituations":"A non-admin user (no ADMIN_ROLE) attempts to operate on a job group they were not granted; SSO session expired so loginCheck returns non-success; misconfiguration of the user's jobGroups attribute in the identity store; the jobGroup id in the request does not match any granted group.","solutions":["Grant the user's identity the missing jobGroup id in the 'jobGroups' attribute (comma-separated).","Re-authenticate / refresh the SSO session if it expired.","Confirm the request targets a jobGroup the user actually owns; admins (ADMIN_ROLE) bypass the filter entirely.","Ensure loginInfoResponse.getData() is non-null before permission checks to avoid an NPE masking the real cause."],"exampleFix":"// before: user has jobGroups \"1,2\" but request targets jobGroup 3\n// grant jobGroup 3 to the user, or call as an ADMIN_ROLE user\n// after: ensure permission is held before invoking the protected action\nLoginInfo info = JobGroupPermissionUtil.validJobGroupPermission(request, jobGroup);","handlingStrategy":"try-catch","validationCode":"// Verify login + permission before invoking the protected action\nResponse<LoginInfo> r = XxlSsoHelper.loginCheckWithAttr(request);\nif (r.isSuccess() && r.getData() != null\n        && JobGroupPermissionUtil.hasJobGroupPermission(r.getData(), jobGroup)) {\n    // proceed\n} else {\n    log.warn(\"denied: user lacks jobGroup {}\", jobGroup);\n}","typeGuard":null,"tryCatchPattern":"try {\n    LoginInfo info = JobGroupPermissionUtil.validJobGroupPermission(request, jobGroup);\n} catch (RuntimeException e) {\n    // permission/login failure; surface HTTP 403 to the caller\n    log.warn(\"permission denied: {}\", e.getMessage());\n    throw new org.springframework.web.server.ResponseStatusException(org.springframework.http.HttpStatus.FORBIDDEN);\n}","preventionTips":["Grant the user's 'jobGroups' attribute the ids they must operate on.","Handle SSO session expiry with a re-auth redirect rather than letting it throw.","Null-check loginInfoResponse.getData() before relying on it."],"tags":["security","permission","sso","authorization","configuration"],"backgroundTag":null,"analyzedSha":"e74c784f68f81fa89cb350913ef15794865d7b12","analyzedAt":"2026-08-14T04:22:43.715Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}