/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.xpn.xwiki.user.impl.xwiki; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.model.EntityType; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.EntityReferenceSerializer; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.classes.GroupsClass; import com.xpn.xwiki.user.api.XWikiGroupService; import com.xpn.xwiki.user.api.XWikiRightNotFoundException; import com.xpn.xwiki.user.api.XWikiRightService; import com.xpn.xwiki.user.api.XWikiUser; import com.xpn.xwiki.util.Util; import com.xpn.xwiki.web.Utils; /** * Default implementation of {@link XWikiRightService}. * * @version $Id: e2a32d75b303d933a8c2ee6587b9102a17bc0464 $ * @deprecated since 4.0, use XWikiCachingRightService instead */ @Deprecated public class XWikiRightServiceImpl implements XWikiRightService { public static final EntityReference RIGHTCLASS_REFERENCE = new EntityReference("XWikiRights", EntityType.DOCUMENT, new EntityReference("XWiki", EntityType.SPACE)); public static final EntityReference GLOBALRIGHTCLASS_REFERENCE = new EntityReference("XWikiGlobalRights", EntityType.DOCUMENT, new EntityReference("XWiki", EntityType.SPACE)); private static final Logger LOGGER = LoggerFactory.getLogger(XWikiRightServiceImpl.class); private static final EntityReference XWIKIPREFERENCES_REFERENCE = new EntityReference("XWikiPreferences", EntityType.DOCUMENT, new EntityReference("XWiki", EntityType.SPACE)); private static final List<String> ALLLEVELS = Arrays.asList("admin", "view", "edit", "comment", "delete", "undelete", "register", "programming"); private static final EntityReference DEFAULTUSERSPACE = new EntityReference("XWiki", EntityType.SPACE); private static Map<String, String> actionMap; /** * Used to convert a string into a proper Document Reference. */ private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver = Utils.getComponent( DocumentReferenceResolver.TYPE_STRING, "currentmixed"); /** * Used to convert a proper Document Name to string. */ private EntityReferenceSerializer<String> entityReferenceSerializer = Utils .getComponent(EntityReferenceSerializer.TYPE_STRING); protected void logAllow(String username, String page, String action, String info) { LOGGER.debug("Access has been granted for ([{}], [{}], [{}]): [{}]", username, page, action, info); } protected void logDeny(String username, String page, String action, String info) { LOGGER.info("Access has been denied for ([{}], [{}], [{}]): [{}]", username, page, action, info); } protected void logDeny(String name, String resourceKey, String accessLevel, String info, Exception e) { LOGGER.debug("Access has been denied for ([{}], [{}], [{}]) at [{}]", name, resourceKey, accessLevel, info, e); } @Override public List<String> listAllLevels(XWikiContext context) throws XWikiException { return new ArrayList<String>(ALLLEVELS); } public String getRight(String action) { if (actionMap == null) { actionMap = new HashMap<String, String>(); actionMap.put("login", "login"); actionMap.put("logout", "login"); actionMap.put("loginerror", "login"); actionMap.put("loginsubmit", "login"); actionMap.put("view", "view"); actionMap.put("viewrev", "view"); actionMap.put("get", "view"); actionMap.put("downloadrev", "view"); actionMap.put("plain", "view"); actionMap.put("raw", "view"); actionMap.put("attach", "view"); actionMap.put("charting", "view"); actionMap.put("skin", "view"); actionMap.put("download", "view"); actionMap.put("dot", "view"); actionMap.put("svg", "view"); actionMap.put("pdf", "view"); actionMap.put("delete", "delete"); actionMap.put("deletespace", "admin"); actionMap.put("deleteversions", "admin"); actionMap.put("undelete", "undelete"); actionMap.put("reset", "delete"); actionMap.put("commentadd", "comment"); actionMap.put("commentsave", "comment"); actionMap.put("register", "register"); actionMap.put("redirect", "view"); actionMap.put("admin", "admin"); actionMap.put("export", "view"); actionMap.put("import", "admin"); actionMap.put("jsx", "view"); actionMap.put("ssx", "view"); actionMap.put("tex", "view"); actionMap.put("create", "edit"); actionMap.put("temp", "view"); actionMap.put("unknown", "view"); } String right = actionMap.get(action); if (right == null) { return "edit"; } else { return right; } } @Override public boolean checkAccess(String action, XWikiDocument doc, XWikiContext context) throws XWikiException { LOGGER.debug("checkAccess for [{}], [{}]", action, doc); String username = null; XWikiUser user = null; boolean needsAuth = false; String right = getRight(action); if (right.equals("login")) { user = context.getWiki().checkAuth(context); if (user == null) { username = XWikiRightService.GUEST_USER_FULLNAME; } else { username = user.getUser(); } // Save the user context.setUser(username); logAllow(username, doc.getFullName(), action, "login/logout pages"); return true; } if (right.equals("delete")) { user = context.getWiki().checkAuth(context); String creator = doc.getCreator(); if ((user != null) && (user.getUser() != null) && (creator != null)) { if (user.getUser().equals(creator)) { context.setUser(user.getUser()); return true; } } } // We do not need to authenticate twice // This seems to cause a problem in virtual wikis user = context.getXWikiUser(); if (user == null) { needsAuth = needsAuth(right, context); try { if (context.getMode() != XWikiContext.MODE_XMLRPC) { user = context.getWiki().checkAuth(context); } else { user = new XWikiUser(context.getUser()); } if ((user == null) && (needsAuth)) { logDeny("unauthentified", doc.getFullName(), action, "Authentication needed"); if (context.getRequest() != null) { if (!context.getWiki().Param("xwiki.hidelogin", "false").equalsIgnoreCase("true")) { context.getWiki().getAuthService().showLogin(context); } } return false; } } catch (XWikiException e) { if (needsAuth) { throw e; } } if (user == null) { username = XWikiRightService.GUEST_USER_FULLNAME; } else { username = user.getUser(); } // Save the user context.setUser(username); } else { username = user.getUser(); } // Check Rights try { // Verify access rights and return if ok String docname; if (context.getWikiId() != null) { docname = context.getWikiId() + ":" + doc.getFullName(); if (username.indexOf(":") == -1) { username = context.getWikiId() + ":" + username; } } else { docname = doc.getFullName(); } if (context.getWiki().getRightService().hasAccessLevel(right, username, docname, context)) { logAllow(username, docname, action, "access manager granted right"); return true; } } catch (Exception e) { // This should not happen.. logDeny(username, doc.getFullName(), action, "access manager exception " + e.getMessage()); e.printStackTrace(); return false; } if (user == null) { // Denied Guest need to be authenticated logDeny("unauthentified", doc.getFullName(), action, "Guest has been denied"); if (context.getRequest() != null && !context.getWiki().Param("xwiki.hidelogin", "false").equalsIgnoreCase("true")) { context.getWiki().getAuthService().showLogin(context); } return false; } else { logDeny(username, doc.getFullName(), action, "access manager denied right"); return false; } } private boolean needsAuth(String right, XWikiContext context) { boolean needsAuth = false; try { needsAuth = context.getWiki().getXWikiPreference("authenticate_" + right, "", context).toLowerCase().equals("yes"); } catch (Exception e) { } try { needsAuth |= (context.getWiki().getXWikiPreferenceAsInt("authenticate_" + right, 0, context) == 1); } catch (Exception e) { } try { needsAuth |= context.getWiki().getSpacePreference("authenticate_" + right, "", context).toLowerCase().equals("yes"); } catch (Exception e) { } try { needsAuth |= (context.getWiki().getSpacePreferenceAsInt("authenticate_" + right, 0, context) == 1); } catch (Exception e) { } return needsAuth; } @Override public boolean hasAccessLevel(String right, String username, String docname, XWikiContext context) throws XWikiException { try { return hasAccessLevel(right, username, docname, true, context); } catch (XWikiException e) { return false; } } public boolean checkRight(String userOrGroupName, XWikiDocument doc, String accessLevel, boolean user, boolean allow, boolean global, XWikiContext context) throws XWikiRightNotFoundException, XWikiException { if (!global && ("admin".equals(accessLevel))) { // Admin rights do not exist at document level. throw new XWikiRightNotFoundException(); } EntityReference rightClassReference = global ? GLOBALRIGHTCLASS_REFERENCE : RIGHTCLASS_REFERENCE; String fieldName = user ? "users" : "groups"; boolean found = false; // Here entity is either a user or a group DocumentReference userOrGroupDocumentReference = this.currentMixedDocumentReferenceResolver.resolve(userOrGroupName); String prefixedFullName = this.entityReferenceSerializer.serialize(userOrGroupDocumentReference); String shortname = userOrGroupName; int i0 = userOrGroupName.indexOf(":"); if (i0 != -1) { shortname = userOrGroupName.substring(i0 + 1); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Checking right: [{}], [{}], [{}], [{}], [{}], [{}]", userOrGroupName, doc.getFullName(), accessLevel, user, allow, global); } List<BaseObject> rightObjects = doc.getXObjects(rightClassReference); if (rightObjects != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Checking objects [{}]", rightObjects.size()); } for (int i = 0; i < rightObjects.size(); i++) { LOGGER.debug("Checking object [{}]", i); BaseObject bobj = rightObjects.get(i); if (bobj == null) { LOGGER.debug("Bypass object [{}]", i); continue; } String users = bobj.getStringValue(fieldName); String levels = bobj.getStringValue("levels"); boolean allowdeny = (bobj.getIntValue("allow") == 1); if (allowdeny == allow) { LOGGER.debug("Checking match: [{}] in [{}]", accessLevel, levels); String[] levelsarray = StringUtils.split(levels, " ,|"); if (ArrayUtils.contains(levelsarray, accessLevel)) { LOGGER.debug("Found a right for [{}]", allow); found = true; LOGGER.debug("Checking match: [{}] in [{}]", userOrGroupName, users); String[] userarray = GroupsClass.getListFromString(users).toArray(new String[0]); for (int ii = 0; ii < userarray.length; ii++) { String value = userarray[ii]; if (value.indexOf(".") == -1) { userarray[ii] = "XWiki." + value; } } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Checking match: [{}] in [{}]", userOrGroupName, StringUtils.join(userarray, ",")); } // In the case where the document database and the user database is the same // then we allow the usage of the short name, otherwise the fully qualified // name is requested if (doc.getWikiName().equals(userOrGroupDocumentReference.getWikiReference().getName())) { if (ArrayUtils.contains(userarray, shortname)) { LOGGER.debug("Found matching right in [{}] for [{}]", users, shortname); return true; } // We should also allow to skip "XWiki." from the usernames and group // lists String veryshortname = shortname.substring(shortname.indexOf(".") + 1); if (ArrayUtils.contains(userarray, veryshortname)) { LOGGER.debug("Found matching right in [{}] for [{}]", users, shortname); return true; } } if ((context.getWikiId() != null) && (ArrayUtils.contains(userarray, userOrGroupName))) { LOGGER.debug("Found matching right in [{}] for [{}]", users, userOrGroupName); return true; } LOGGER.debug("Failed match: [{}] in [{}]", userOrGroupName, users); } } else { LOGGER.debug("Bypass object [{}] because wrong allow/deny", i); } } } LOGGER.debug("Searching for matching rights at group level"); // Didn't found right at this level.. Let's go to group level Map<String, Collection<String>> grouplistcache = (Map<String, Collection<String>>) context.get("grouplist"); if (grouplistcache == null) { grouplistcache = new HashMap<String, Collection<String>>(); context.put("grouplist", grouplistcache); } Collection<String> grouplist = new HashSet<String>(); // Get member groups from document's wiki addMemberGroups(doc.getWikiName(), prefixedFullName, userOrGroupDocumentReference, grouplist, context); // Get member groups from member's wiki if (!context.getWikiId().equalsIgnoreCase(userOrGroupDocumentReference.getWikiReference().getName())) { addMemberGroups(userOrGroupDocumentReference.getWikiReference().getName(), prefixedFullName, userOrGroupDocumentReference, grouplist, context); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Searching for matching rights for [{}] groups: [{}]", grouplist.size(), grouplist); } for (String group : grouplist) { try { // We need to construct the full group name to make sure the groups are // handled separately boolean result = checkRight(group, doc, accessLevel, false, allow, global, context); if (result) { return true; } } catch (XWikiRightNotFoundException e) { } catch (Exception e) { LOGGER.error("Failed to check right [{}] for group [{}] on document [ΒΆ}]", accessLevel, group, doc.getPrefixedFullName(), e); } } LOGGER.debug("Finished searching for rights for [{}]: [{}]", userOrGroupName, found); if (found) { return false; } else { throw new XWikiRightNotFoundException(); } } private void addMemberGroups(String wiki, String prefixedFullName, DocumentReference userOrGroupDocumentReference, Collection<String> grouplist, XWikiContext context) throws XWikiException { XWikiGroupService groupService = context.getWiki().getGroupService(context); Map<String, Collection<String>> grouplistcache = (Map<String, Collection<String>>) context.get("grouplist"); if (grouplistcache == null) { grouplistcache = new HashMap<String, Collection<String>>(); context.put("grouplist", grouplistcache); } // the key is for the entity <code>prefixedFullName</code> in current wiki String key = wiki + ":" + prefixedFullName; Collection<String> tmpGroupList = grouplistcache.get(key); if (tmpGroupList == null) { String currentWiki = context.getWikiId(); try { context.setWikiId(wiki); Collection<DocumentReference> groupReferences = groupService.getAllGroupsReferencesForMember(userOrGroupDocumentReference, 0, 0, context); tmpGroupList = new ArrayList<String>(groupReferences.size()); for (DocumentReference groupReference : groupReferences) { tmpGroupList.add(this.entityReferenceSerializer.serialize(groupReference)); } } catch (Exception e) { LOGGER.error("Failed to get groups for user or group [{}] in wiki [{}]", prefixedFullName, wiki, e); tmpGroupList = Collections.emptyList(); } finally { context.setWikiId(currentWiki); } grouplistcache.put(key, tmpGroupList); } grouplist.addAll(tmpGroupList); } public boolean hasAccessLevel(String accessLevel, String userOrGroupName, String entityReference, boolean user, XWikiContext context) throws XWikiException { LOGGER.debug("hasAccessLevel for [{}], [{}], [{}]", accessLevel, userOrGroupName, entityReference); DocumentReference userOrGroupNameReference = this.currentMixedDocumentReferenceResolver.resolve(userOrGroupName); if (!userOrGroupNameReference.getName().equals(XWikiRightService.GUEST_USER) && context.getWikiId() != null) { // Make sure to have the prefixed full name of the user or group userOrGroupName = this.entityReferenceSerializer.serialize(this.currentMixedDocumentReferenceResolver.resolve( userOrGroupName, DEFAULTUSERSPACE)); // Make sure to have the prefixed full name of the resource entityReference = this.entityReferenceSerializer.serialize(this.currentMixedDocumentReferenceResolver .resolve(entityReference)); } boolean deny = false; boolean allow = false; boolean allow_found = false; boolean deny_found = false; boolean isReadOnly = context.getWiki().isReadOnly(); String database = context.getWikiId(); XWikiDocument currentdoc = null; if (isReadOnly) { if ("edit".equals(accessLevel) || "delete".equals(accessLevel) || "undelete".equals(accessLevel) || "comment".equals(accessLevel) || "register".equals(accessLevel)) { logDeny(userOrGroupName, entityReference, accessLevel, "server in read-only mode"); return false; } } if (userOrGroupNameReference.getName().equals(XWikiRightService.GUEST_USER)) { if (needsAuth(accessLevel, context)) { return false; } } // Fast return for delete right: allow the creator to delete the document if (accessLevel.equals("delete") && user) { currentdoc = context.getWiki().getDocument(entityReference, context); DocumentReference creator = currentdoc.getCreatorReference(); if (ObjectUtils.equals(userOrGroupNameReference, creator)) { logAllow(userOrGroupName, entityReference, accessLevel, "delete right from document ownership"); return true; } } allow = isSuperAdminOrProgramming(userOrGroupName, entityReference, accessLevel, user, context); if ((allow == true) || (accessLevel.equals("programming"))) { return allow; } try { currentdoc = currentdoc == null ? context.getWiki().getDocument(entityReference, context) : currentdoc; DocumentReference docReference = currentdoc.getDocumentReference(); if (accessLevel.equals("edit") && (docReference.getName().equals("WebPreferences") || (docReference.getLastSpaceReference().getName() .equals("XWiki") && docReference.getName().equals("XWikiPreferences")))) { // Since edit rights on these documents would be sufficient for a user to elevate himself to // admin or even programmer, we will instead check for admin access on these documents. // See https://jira.xwiki.org/browse/XWIKI-6987 and https://jira.xwiki.org/browse/XWIKI-2184. accessLevel = "admin"; } // We need to make sure we are in the context of the document which rights is being checked context.setWikiId(currentdoc.getDatabase()); // Verify Wiki Owner String wikiOwner = context.getWiki().getWikiOwner(currentdoc.getDatabase(), context); if (wikiOwner != null) { if (wikiOwner.equals(userOrGroupName)) { logAllow(userOrGroupName, entityReference, accessLevel, "admin level from wiki ownership"); return true; } } XWikiDocument entityWikiPreferences = context.getWiki().getDocument(XWIKIPREFERENCES_REFERENCE, context); // Verify XWiki register right if (accessLevel.equals("register")) { try { allow = checkRight(userOrGroupName, entityWikiPreferences, "register", user, true, true, context); if (allow) { logAllow(userOrGroupName, entityReference, accessLevel, "register level"); return true; } else { logDeny(userOrGroupName, entityReference, accessLevel, "register level"); return false; } } catch (XWikiRightNotFoundException e) { try { deny = checkRight(userOrGroupName, entityWikiPreferences, "register", user, false, true, context); if (deny) { return false; } } catch (XWikiRightNotFoundException e1) { } } logAllow(userOrGroupName, entityReference, accessLevel, "register level (no right found)"); return true; } int maxRecursiveSpaceChecks = context.getWiki().getMaxRecursiveSpaceChecks(context); boolean isSuperUser = isSuperUser(accessLevel, userOrGroupName, entityReference, user, entityWikiPreferences, maxRecursiveSpaceChecks, context); if (isSuperUser) { logAllow(userOrGroupName, entityReference, accessLevel, "admin level"); return true; } // check has deny rights if (hasDenyRights()) { // First check if this document is denied to the specific user entityReference = Util.getName(entityReference, context); try { currentdoc = currentdoc == null ? context.getWiki().getDocument(entityReference, context) : currentdoc; deny = checkRight(userOrGroupName, currentdoc, accessLevel, user, false, false, context); deny_found = true; if (deny) { logDeny(userOrGroupName, entityReference, accessLevel, "document level"); return false; } } catch (XWikiRightNotFoundException e) { } } try { currentdoc = currentdoc == null ? context.getWiki().getDocument(entityReference, context) : currentdoc; allow = checkRight(userOrGroupName, currentdoc, accessLevel, user, true, false, context); allow_found = true; if (allow) { logAllow(userOrGroupName, entityReference, accessLevel, "document level"); return true; } } catch (XWikiRightNotFoundException e) { } // Check if this document is denied/allowed // through the space WebPreferences Global Rights String space = currentdoc.getSpace(); ArrayList<String> spacesChecked = new ArrayList<String>(); int recursiveSpaceChecks = 0; while ((space != null) && (recursiveSpaceChecks <= maxRecursiveSpaceChecks)) { // Add one to the recursive space checks recursiveSpaceChecks++; // add to list of spaces already checked spacesChecked.add(space); XWikiDocument webdoc = context.getWiki().getDocument(space, "WebPreferences", context); if (!webdoc.isNew()) { if (hasDenyRights()) { try { deny = checkRight(userOrGroupName, webdoc, accessLevel, user, false, true, context); deny_found = true; if (deny) { logDeny(userOrGroupName, entityReference, accessLevel, "web level"); return false; } } catch (XWikiRightNotFoundException e) { } } // If a right was found at the previous level // then we cannot check the web rights anymore if (!allow_found) { try { allow = checkRight(userOrGroupName, webdoc, accessLevel, user, true, true, context); allow_found = true; if (allow) { logAllow(userOrGroupName, entityReference, accessLevel, "web level"); return true; } } catch (XWikiRightNotFoundException e) { } } // find the parent web to check rights on it space = webdoc.getStringValue("XWiki.XWikiPreferences", "parent"); if ((space == null) || (space.trim().equals("")) || spacesChecked.contains(space)) { // no parent space or space already checked (recursive loop). let's finish // the loop space = null; } } else { // let's finish the loop space = null; } } // Check if this document is denied/allowed // through the XWiki.XWikiPreferences Global Rights if (hasDenyRights()) { try { deny = checkRight(userOrGroupName, entityWikiPreferences, accessLevel, user, false, true, context); deny_found = true; if (deny) { logDeny(userOrGroupName, entityReference, accessLevel, "xwiki level"); return false; } } catch (XWikiRightNotFoundException e) { } } // If a right was found at the document or web level // then we cannot check the web rights anymore if (!allow_found) { try { allow = checkRight(userOrGroupName, entityWikiPreferences, accessLevel, user, true, true, context); allow_found = true; if (allow) { logAllow(userOrGroupName, entityReference, accessLevel, "xwiki level"); return true; } } catch (XWikiRightNotFoundException e) { } } // If neither doc, web or topic had any allowed ACL // and that all users that were not denied // should be allowed. if (!allow_found) { // Delete must be denied by default. if ("delete".equals(accessLevel)) { if (hasAccessLevel("admin", userOrGroupName, entityReference, user, context)) { logAllow(userOrGroupName, entityReference, accessLevel, "admin rights imply delete on empty wiki"); return true; } logDeny(userOrGroupName, entityReference, accessLevel, "global level (delete right must be explicit)"); return false; } else { logAllow(userOrGroupName, entityReference, accessLevel, "global level (no restricting right)"); return true; } } else { logDeny(userOrGroupName, entityReference, accessLevel, "global level (restricting right was found)"); return false; } } catch (XWikiException e) { logDeny(userOrGroupName, entityReference, accessLevel, "global level (exception)", e); e.printStackTrace(); return false; } finally { context.setWikiId(database); } } private boolean hasDenyRights() { return true; } /** * @param username Any flavor of username. Examples: "xwiki:XWiki.superadmin", "XWiki.superAdmin", "superadmin", etc * @return true if the username is that of the superadmin (whatever the case) or false otherwise */ // TODO: this method is a candidate for the the XWikiRightService API. private boolean isSuperAdmin(String username) { // Note 1: we use the default document reference resolver here but it doesn't matter since we only care about // the resolved page name. // Note 2: we use a resolver since the passed username could contain the wiki and/or space too and we want // to retrieve only the page name DocumentReference userReference = Utils.<DocumentReferenceResolver<String>>getComponent(DocumentReferenceResolver.TYPE_STRING).resolve( username); return StringUtils.equalsIgnoreCase(userReference.getName(), SUPERADMIN_USER); } private boolean isSuperAdminOrProgramming(String name, String resourceKey, String accessLevel, boolean user, XWikiContext context) throws XWikiException { if (name == null) { return false; } String database = context.getWikiId(); boolean allow; if (isSuperAdmin(name)) { logAllow(name, resourceKey, accessLevel, "super admin level"); return true; } try { // The master user and programming rights are checked in the main wiki context.setWikiId(context.getMainXWiki()); XWikiDocument xwikimasterdoc = context.getWiki().getDocument(XWIKIPREFERENCES_REFERENCE, context); // Verify XWiki Master super user try { allow = checkRight(name, xwikimasterdoc, "admin", true, true, true, context); if (allow) { logAllow(name, resourceKey, accessLevel, "master admin level"); return true; } } catch (XWikiRightNotFoundException e) { } // Verify XWiki programming right if (accessLevel.equals("programming")) { // Programming right can only been given if user is from main wiki if (!name.startsWith(context.getMainXWiki() + ":")) { return false; } try { allow = checkRight(name, xwikimasterdoc, "programming", user, true, true, context); if (allow) { logAllow(name, resourceKey, accessLevel, "programming level"); return true; } else { logDeny(name, resourceKey, accessLevel, "programming level"); return false; } } catch (XWikiRightNotFoundException e) { } logDeny(name, resourceKey, accessLevel, "programming level (no right found)"); return false; } } finally { // The next rights are checked in the virtual wiki context.setWikiId(database); } return false; } private boolean isSuperUser(String accessLevel, String name, String resourceKey, boolean user, XWikiDocument xwikidoc, int maxRecursiveSpaceChecks, XWikiContext context) throws XWikiException { boolean allow; // Verify XWiki super user try { allow = checkRight(name, xwikidoc, "admin", user, true, true, context); if (allow) { logAllow(name, resourceKey, accessLevel, "admin level"); return true; } } catch (XWikiRightNotFoundException e) { } XWikiDocument documentName = new XWikiDocument(); documentName.setFullName(resourceKey); // Verify Web super user String space = documentName.getSpace(); ArrayList<String> spacesChecked = new ArrayList<String>(); int recursiveSpaceChecks = 0; while ((space != null) && (recursiveSpaceChecks <= maxRecursiveSpaceChecks)) { // Add one to the recursive space checks recursiveSpaceChecks++; // add to list of spaces already checked spacesChecked.add(space); XWikiDocument webdoc = context.getWiki().getDocument(space, "WebPreferences", context); if (!webdoc.isNew()) { try { allow = checkRight(name, webdoc, "admin", user, true, true, context); if (allow) { logAllow(name, resourceKey, accessLevel, "web admin level"); return true; } } catch (XWikiRightNotFoundException e) { } // find the parent web to check rights on it space = webdoc.getStringValue("XWiki.XWikiPreferences", "parent"); if ((space == null) || (space.trim().equals("")) || spacesChecked.contains(space)) { // no parent space or space already checked (recursive loop). let's finish the // loop space = null; } } else { space = null; } } return false; } @Override public boolean hasProgrammingRights(XWikiContext context) { // Once dropPermissions has been called, the document in the // context cannot have programming permission. if (context.hasDroppedPermissions()) { return false; } XWikiDocument sdoc = (XWikiDocument) context.get("sdoc"); if (sdoc == null) { sdoc = context.getDoc(); } return hasProgrammingRights(sdoc, context); } @Override public boolean hasProgrammingRights(XWikiDocument doc, XWikiContext context) { try { if (doc == null) { // If no context document is set, then check the rights of the current user return isSuperAdminOrProgramming(this.entityReferenceSerializer.serialize(context.getUserReference()), null, "programming", true, context); } String username = doc.getContentAuthor(); if (username == null) { return false; } String docname; if (doc.getDatabase() != null) { docname = doc.getDatabase() + ":" + doc.getFullName(); if (username.indexOf(":") == -1) { username = doc.getDatabase() + ":" + username; } } else { docname = doc.getFullName(); } // programming rights can only been given for user of the main wiki // FIXME: Isn't this wrong? The main db is context.getMainWikiName(), not context.getWiki().getDatabase() // (which is the current db). String maindb = context.getWiki().getDatabase(); if ((maindb == null) || (!username.startsWith(maindb))) { return false; } return hasAccessLevel("programming", username, docname, context); } catch (Exception e) { LOGGER.error("Failed to check programming right for document [{}]", doc.getPrefixedFullName(), e); return false; } } @Override public boolean hasAdminRights(XWikiContext context) { boolean hasAdmin = hasWikiAdminRights(context); if (!hasAdmin) { try { hasAdmin = hasAccessLevel("admin", context.getUser(), context.getDoc().getSpace() + ".WebPreferences", context); } catch (Exception e) { LOGGER.error("Failed to check space admin right for user [{}]", context.getUser(), e); } } return hasAdmin; } @Override public boolean hasWikiAdminRights(XWikiContext context) { try { return hasAccessLevel("admin", context.getUser(), "XWiki.XWikiPreferences", context); } catch (Exception e) { LOGGER.error("Failed to check wiki admin right for user [{}]", context.getUser(), e); return false; } } }