001/* 002 * Copyright 2015 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.pageaccess; 017 018import java.util.Collection; 019import java.util.Set; 020 021import org.apache.avalon.framework.component.Component; 022import org.apache.avalon.framework.logger.AbstractLogEnabled; 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.avalon.framework.service.Serviceable; 026 027import org.ametys.cms.repository.Content; 028import org.ametys.core.group.GroupIdentity; 029import org.ametys.core.right.AllowedUsers; 030import org.ametys.core.right.ProfileAssignmentStorageExtensionPoint; 031import org.ametys.core.right.RightManager; 032import org.ametys.core.user.CurrentUserProvider; 033import org.ametys.core.user.UserIdentity; 034import org.ametys.web.repository.content.WebContent; 035import org.ametys.web.repository.page.Page; 036 037/** 038 * Class managing access to contents on the front-office side. 039 */ 040public class ContentAccessManager extends AbstractLogEnabled implements Component, Serviceable 041{ 042 043 /** 044 * Enumeration representing a content access status. 045 */ 046 public enum ContentAccess 047 { 048 /** 049 * The content can be viewed by all users because of one of these reasons: 050 * <ul> 051 * <li>it appears on a public page</li> 052 * <li>it doesn't appear on any page (orphan content) but its sitemap is freely accessible</li> 053 * </ul> 054 */ 055 UNRESTRICTED, 056 057 /** 058 * The content is available for anyconnected users 059 */ 060 ANYCONNECTED_ALLOWED, 061 062 /** 063 * The content appears on pages with limited access, and the given user can access at least one. 064 */ 065 ALLOWED, 066 067 /** 068 * The content appears on pages with limited access, but the given user can't access any. 069 */ 070 FORBIDDEN 071 } 072 073 /** The component role. */ 074 public static final String ROLE = ContentAccessManager.class.getName(); 075 076 /** The right manager */ 077 protected RightManager _rightManager; 078 079 /** The current user provider */ 080 protected CurrentUserProvider _currentUserProvider; 081 082 /** The component handling profile storage */ 083 protected ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageEP; 084 085 @Override 086 public void service(ServiceManager manager) throws ServiceException 087 { 088 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 089 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 090 _profileAssignmentStorageEP = (ProfileAssignmentStorageExtensionPoint) manager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE); 091 } 092 093 /** 094 * Get the access status of a content, from the user currently connected to the front-office. 095 * @param content the content to test. 096 * @return the content access status. 097 */ 098 public ContentAccess getAccess(Content content) 099 { 100 return getAccess(content, true); 101 } 102 103 /** 104 * Get the access status of a content, from the user currently connected to the front-office. 105 * @param content the content to test. 106 * @param checkUser When true, the user's ability to view the page will be checked 107 * ({@link ContentAccess#ALLOWED} may be returned).<br> 108 * When false, only the content general availability will be checked: {@link ContentAccess#ALLOWED} will never be returned. 109 * @return the content access status. 110 */ 111 public ContentAccess getAccess(Content content, boolean checkUser) 112 { 113 return getAccess(content, _currentUserProvider.getUser(), checkUser); 114 } 115 116 /** 117 * Get the access status of a content from a given user. 118 * @param content the content to test. 119 * @param userIdentity the user identity, can be null (to test anonymous access). 120 * @return the content access status. 121 */ 122 public ContentAccess getAccess(Content content, UserIdentity userIdentity) 123 { 124 return getAccess(content, userIdentity, true); 125 } 126 127 /** 128 * Get the access status of a content from a given user. 129 * The user access is check ONLY on the content's pages. Has read access on content is not enough ! 130 * @param content the content to test. 131 * @param userIdentity the user identity, can be null (to test anonymous access). 132 * @param checkUser When true, the user's ability to view the page will be checked 133 * ({@link ContentAccess#ALLOWED} may be returned).<br> 134 * When false, only the content general availability will be checked: {@link ContentAccess#ALLOWED} will never be returned. 135 * @return the content access status. 136 */ 137 public ContentAccess getAccess(Content content, UserIdentity userIdentity, boolean checkUser) 138 { 139 if (!(content instanceof WebContent)) 140 { 141 return _getContentAccess(content, userIdentity, checkUser); 142 } 143 WebContent webContent = (WebContent) content; 144 Collection<Page> refPages = webContent.getReferencingPages(); 145 if (refPages.isEmpty()) 146 { 147 return _getContentAccess(content, userIdentity, checkUser); 148 } 149 return _getAccessForContentPages(refPages, userIdentity, checkUser); 150 } 151 152 private ContentAccess _getAccessForContentPages(Collection<Page> contentPages, UserIdentity userIdentity, boolean checkUser) 153 { 154 for (Page page : contentPages) 155 { 156 // If the content is present in a page that is not restricted, it's visible by all. 157 if (_rightManager.hasAnonymousReadAccess(page)) 158 { 159 return ContentAccess.UNRESTRICTED; 160 } 161 if (_rightManager.hasAnyConnectedUserReadAccess(page)) 162 { 163 return ContentAccess.ANYCONNECTED_ALLOWED; 164 } 165 166 // If the user is allowed to see at least one page containing the content, he can access the content. 167 if (checkUser && _rightManager.hasReadAccess(userIdentity, page)) 168 { 169 return ContentAccess.ALLOWED; 170 } 171 } 172 return ContentAccess.FORBIDDEN; 173 } 174 175 private ContentAccess _getContentAccess(Content content, UserIdentity userIdentity, boolean checkUser) 176 { 177 // Returns content access regardless of its pages. 178 if (_rightManager.hasAnonymousReadAccess(content)) 179 { 180 return ContentAccess.UNRESTRICTED; 181 } 182 183 if (_rightManager.hasAnyConnectedUserReadAccess(content)) 184 { 185 return ContentAccess.ANYCONNECTED_ALLOWED; 186 } 187 188 return checkUser && _rightManager.hasReadAccess(userIdentity, content) ? ContentAccess.ALLOWED : ContentAccess.FORBIDDEN; 189 } 190 191 /** 192 * Test if the content is displayed in a page having the same access rights as the current page. 193 * @param content the web content. 194 * @param currentPage the current page. 195 * @return true if the content is accessible, false otherwise. 196 */ 197 public boolean isAccessibleByPage(WebContent content, Page currentPage) 198 { 199 if (!_rightManager.hasAnonymousReadAccess(currentPage)) 200 { 201 return _isAccessibleByRestrictedPage(content, currentPage); 202 } 203 else 204 { 205 // The current page is not restricted: display only unrestricted contents 206 // N.B: the current user's right are *NOT* tested, as in this mode, the results are cached. 207 return getAccess(content, null, false) == ContentAccess.UNRESTRICTED; 208 } 209 } 210 211 private boolean _isAccessibleByRestrictedPage(WebContent content, Page currentPage) 212 { 213 AllowedUsers currentUsersWithReadAccess = _rightManager.getReadAccessAllowedUsers(currentPage); 214 boolean currentAllowAnyConnectedUser = currentUsersWithReadAccess.isAnyConnectedUserAllowed(); 215 Set<UserIdentity> currentAllowedUsers = currentUsersWithReadAccess.getAllowedUsers(); 216 Set<GroupIdentity> currentAllowedGroups = currentUsersWithReadAccess.getAllowedGroups(); 217 Set<UserIdentity> currentDeniedUsers = currentUsersWithReadAccess.getDeniedUsers(); 218 Set<GroupIdentity> currentDeniedGroups = currentUsersWithReadAccess.getDeniedGroups(); 219 Collection<Page> refPages = content.getReferencingPages(); 220 if (!refPages.isEmpty()) 221 { 222 for (Page referencingPage : refPages) 223 { 224 if (_isAccessibleByReferencingPage(referencingPage, currentAllowAnyConnectedUser, currentAllowedUsers, currentAllowedGroups, currentDeniedUsers, currentDeniedGroups)) 225 { 226 return true; 227 } 228 } 229 return false; 230 } 231 else 232 { 233 // Orphan content 234 return _isAccessibleOrphanContent(content, currentAllowAnyConnectedUser, currentAllowedUsers, currentAllowedGroups, currentDeniedUsers, currentDeniedGroups); 235 } 236 } 237 238 private boolean _isAccessibleByReferencingPage(Page referencingPage, boolean currentAllowAnyConnectedUser, Set<UserIdentity> currentAllowedUsers, Set<GroupIdentity> currentAllowedGroups, Set<UserIdentity> currentDeniedUsers, Set<GroupIdentity> currentDeniedGroups) 239 { 240 if (!_rightManager.hasAnonymousReadAccess(referencingPage)) 241 { 242 AllowedUsers refUsersWithReadAccess = _rightManager.getReadAccessAllowedUsers(referencingPage); 243 boolean refAllowAnyConnectedUser = refUsersWithReadAccess.isAnyConnectedUserAllowed(); 244 Set<UserIdentity> refAllowedUsers = refUsersWithReadAccess.getAllowedUsers(); 245 Set<GroupIdentity> refAllowedGroups = refUsersWithReadAccess.getAllowedGroups(); 246 Set<UserIdentity> refDeniedUsers = refUsersWithReadAccess.getDeniedUsers(); 247 Set<GroupIdentity> refDeniedGroups = refUsersWithReadAccess.getDeniedGroups(); 248 249 // Return true if the content is referenced by a page either accessible either to all connected users 250 // or restricted to the same population as the current page: display the content. 251 return (refAllowAnyConnectedUser 252 || !currentAllowAnyConnectedUser && refAllowedUsers.containsAll(currentAllowedUsers) && refAllowedGroups.containsAll(currentAllowedGroups)) 253 && currentDeniedUsers.containsAll(refDeniedUsers) && currentDeniedGroups.containsAll(refDeniedGroups); 254 } 255 else 256 { 257 // The content is referenced by a public page 258 return true; 259 } 260 } 261 262 private boolean _isAccessibleOrphanContent(Content content, boolean currentAllowAnyConnectedUser, Set<UserIdentity> currentAllowedUsers, Set<GroupIdentity> currentAllowedGroups, Set<UserIdentity> currentDeniedUsers, Set<GroupIdentity> currentDeniedGroups) 263 { 264 if (!_rightManager.hasAnonymousReadAccess(content)) 265 { 266 AllowedUsers refUsersWithReadAccess = _rightManager.getReadAccessAllowedUsers(content); 267 boolean refAllowAnyConnectedUser = refUsersWithReadAccess.isAnyConnectedUserAllowed(); 268 Set<UserIdentity> refAllowedUsers = refUsersWithReadAccess.getAllowedUsers(); 269 Set<GroupIdentity> refAllowedGroups = refUsersWithReadAccess.getAllowedGroups(); 270 Set<UserIdentity> refDeniedUsers = refUsersWithReadAccess.getDeniedUsers(); 271 Set<GroupIdentity> refDeniedGroups = refUsersWithReadAccess.getDeniedGroups(); 272 273 // Check if the content is accessible to all connected users 274 // or restricted to the same population as the current page: display the content. 275 return (refAllowAnyConnectedUser 276 || !currentAllowAnyConnectedUser && refAllowedUsers.containsAll(currentAllowedUsers) && refAllowedGroups.containsAll(currentAllowedGroups)) 277 && currentDeniedUsers.containsAll(refDeniedUsers) && currentDeniedGroups.containsAll(refDeniedGroups); 278 } 279 else 280 { 281 // The content is anonymous read access 282 return true; 283 } 284 } 285}