1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.security.permission;
24  
25  import com.liferay.portal.NoSuchResourceException;
26  import com.liferay.portal.kernel.util.GetterUtil;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.kernel.util.Validator;
29  import com.liferay.portal.model.Group;
30  import com.liferay.portal.model.Organization;
31  import com.liferay.portal.model.Resource;
32  import com.liferay.portal.model.ResourceConstants;
33  import com.liferay.portal.model.Role;
34  import com.liferay.portal.model.User;
35  import com.liferay.portal.model.UserGroup;
36  import com.liferay.portal.model.impl.GroupImpl;
37  import com.liferay.portal.model.impl.PortletImpl;
38  import com.liferay.portal.model.impl.RoleImpl;
39  import com.liferay.portal.service.GroupLocalServiceUtil;
40  import com.liferay.portal.service.OrganizationLocalServiceUtil;
41  import com.liferay.portal.service.PermissionLocalServiceUtil;
42  import com.liferay.portal.service.ResourceLocalServiceUtil;
43  import com.liferay.portal.service.RoleLocalServiceUtil;
44  import com.liferay.portal.service.UserGroupLocalServiceUtil;
45  import com.liferay.portal.service.UserLocalServiceUtil;
46  import com.liferay.portal.service.permission.PortletPermissionUtil;
47  import com.liferay.portal.util.PropsUtil;
48  import com.liferay.portlet.admin.util.OmniadminUtil;
49  import com.liferay.util.UniqueList;
50  
51  import java.io.Serializable;
52  
53  import java.util.ArrayList;
54  import java.util.HashMap;
55  import java.util.List;
56  import java.util.Map;
57  
58  import javax.portlet.PortletRequest;
59  
60  import org.apache.commons.lang.time.StopWatch;
61  import org.apache.commons.logging.Log;
62  import org.apache.commons.logging.LogFactory;
63  
64  /**
65   * <a href="PermissionCheckerImpl.java.html"><b><i>View Source</i></b></a>
66   *
67   * @author Charles May
68   * @author Brian Wing Shun Chan
69   *
70   */
71  public class PermissionCheckerImpl implements PermissionChecker, Serializable {
72  
73      public static final int USER_CHECK_ALGORITHM = GetterUtil.getInteger(
74          PropsUtil.get(PropsUtil.PERMISSIONS_USER_CHECK_ALGORITHM));
75  
76      public PermissionCheckerImpl() {
77      }
78  
79      public void init(User user, boolean checkGuest) {
80          this.user = user;
81  
82          if (user.isDefaultUser()) {
83              this.defaultUserId = user.getUserId();
84              this.signedIn = false;
85          }
86          else {
87              try {
88                  this.defaultUserId = UserLocalServiceUtil.getDefaultUserId(
89                      user.getCompanyId());
90              }
91              catch (Exception e) {
92                  _log.error(e, e);
93              }
94  
95              this.signedIn = true;
96          }
97  
98          this.checkGuest = checkGuest;
99      }
100 
101     public void recycle() {
102         user = null;
103         defaultUserId = 0;
104         signedIn = false;
105         checkGuest = false;
106         omniadmin = null;
107         companyAdmins.clear();
108         bags.clear();
109         resetValues();
110     }
111 
112     public void setValues(PortletRequest req) {
113 
114         // This method is called in com.liferay.portlet.StrutsPortlet to allow
115         // developers to hook in additiona parameters from the portlet request.
116         // Don't overwrite this method unless you're using Liferay in a 2 tier
117         // environment and don't expect to make remote calls. Remote calls to
118         // service beans will not have any values set from the portlet request.
119 
120     }
121 
122     public void resetValues() {
123     }
124 
125     public User getUser() {
126         return user;
127     }
128 
129     public void setUser(User user) {
130         this.user = user;
131     }
132 
133     public long getUserId() {
134         return user.getUserId();
135     }
136 
137     public boolean isSignedIn() {
138         return signedIn;
139     }
140 
141     public void setSignedIn(boolean signedIn) {
142         this.signedIn = signedIn;
143     }
144 
145     public boolean isCheckGuest() {
146         return checkGuest;
147     }
148 
149     public void setCheckGuest(boolean checkGuest) {
150         this.checkGuest = checkGuest;
151     }
152 
153     public boolean hasPermission(
154         long groupId, String name, long primKey, String actionId) {
155 
156         return hasPermission(groupId, name, String.valueOf(primKey), actionId);
157     }
158 
159     public boolean hasPermission(
160         long groupId, String name, String primKey, String actionId) {
161 
162         StopWatch stopWatch = null;
163 
164         if (_log.isDebugEnabled()) {
165             stopWatch = new StopWatch();
166 
167             stopWatch.start();
168         }
169 
170         Group group = null;
171 
172         // If the current group is staging, the live group should be checked for
173         // permissions instead
174 
175         try {
176             if (groupId > 0) {
177                 group = GroupLocalServiceUtil.getGroup(groupId);
178 
179                 if (group.isStagingGroup()) {
180                     if (primKey.equals(String.valueOf(groupId))) {
181                         primKey = String.valueOf(group.getLiveGroupId());
182                     }
183 
184                     groupId = group.getLiveGroupId();
185                     group = group.getLiveGroup();
186                 }
187             }
188         }
189         catch (Exception e) {
190             _log.error(e, e);
191         }
192 
193         PermissionCheckerBag bag = getBag(groupId);
194 
195         if (signedIn && (bag == null)) {
196             try {
197 
198                 // If we are checking permissions on an object that belongs to a
199                 // community, then it's only necessary to check the group that
200                 // represents the community and not all the groups that the user
201                 // belongs to. This is so because an object cannot belong to
202                 // more than one community.
203 
204                 List<Group> userGroups = new ArrayList<Group>();
205                 //List<Group> userGroups = UserUtil.getGroups(userId);
206 
207                 if (groupId > 0) {
208                     if (GroupLocalServiceUtil.hasUserGroup(
209                             user.getUserId(), groupId)) {
210 
211                         userGroups.add(group);
212                     }
213                 }
214 
215                 List<Organization> userOrgs = getUserOrgs(user.getUserId());
216 
217                 List<Group> userOrgGroups =
218                     GroupLocalServiceUtil.getOrganizationsGroups(userOrgs);
219 
220                 List<UserGroup> userUserGroups =
221                     UserGroupLocalServiceUtil.getUserUserGroups(
222                         user.getUserId());
223 
224                 List<Group> userUserGroupGroups =
225                     GroupLocalServiceUtil.getUserGroupsGroups(userUserGroups);
226 
227                 List<Group> groups = new ArrayList<Group>(
228                     userGroups.size() + userOrgGroups.size() +
229                         userUserGroupGroups.size());
230 
231                 groups.addAll(userGroups);
232                 groups.addAll(userOrgGroups);
233                 groups.addAll(userUserGroupGroups);
234 
235                 List<Role> roles = null;
236 
237                 if ((USER_CHECK_ALGORITHM == 3) ||
238                     (USER_CHECK_ALGORITHM == 4)) {
239 
240                     roles = RoleLocalServiceUtil.getUserRelatedRoles(
241                         user.getUserId(), groups);
242 
243                     List<Role> userGroupRoles =
244                         RoleLocalServiceUtil.getUserGroupRoles(
245                             user.getUserId(), groupId);
246 
247                     roles.addAll(userGroupRoles);
248                 }
249                 else {
250                     roles = new ArrayList<Role>();
251                 }
252 
253                 if (_log.isDebugEnabled()) {
254                     _log.debug(
255                         "Creating bag for " + groupId + " " + name + " " +
256                             primKey + " " + actionId + " takes " +
257                                 stopWatch.getTime() + " ms");
258                 }
259 
260                 bag = new PermissionCheckerBagImpl(
261                     user.getUserId(), userGroups, userOrgs, userOrgGroups,
262                     userUserGroupGroups, groups, roles);
263 
264                 putBag(groupId, bag);
265             }
266             catch (Exception e) {
267                 _log.error(e, e);
268             }
269         }
270 
271         Boolean value = PermissionCacheUtil.hasPermission(
272             user.getUserId(), groupId, name, primKey, actionId);
273 
274         if (value == null) {
275             value = Boolean.valueOf(
276                 hasPermissionImpl(groupId, name, primKey, actionId));
277 
278             PermissionCacheUtil.putPermission(
279                 user.getUserId(), groupId, name, primKey, actionId, value);
280 
281             if (_log.isDebugEnabled()) {
282                 _log.debug(
283                     "Checking permission for " + groupId + " " + name + " " +
284                         primKey + " " + actionId + " takes " +
285                             stopWatch.getTime() + " ms");
286             }
287         }
288 
289         return value.booleanValue();
290     }
291 
292     public boolean hasUserPermission(
293         long groupId, String name, String primKey, String actionId,
294         boolean checkAdmin) {
295 
296         try {
297             return hasUserPermissionImpl(
298                 groupId, name, primKey, actionId, checkAdmin);
299         }
300         catch (Exception e) {
301             _log.error(e, e);
302 
303             return false;
304         }
305     }
306 
307     public boolean isOmniadmin() {
308         if (omniadmin == null) {
309             omniadmin = Boolean.valueOf(OmniadminUtil.isOmniadmin(getUserId()));
310         }
311 
312         return omniadmin.booleanValue();
313     }
314 
315     public boolean isCompanyAdmin(long companyId) {
316         try {
317             return isCompanyAdminImpl(companyId);
318         }
319         catch (Exception e) {
320             _log.error(e, e);
321 
322             return false;
323         }
324     }
325 
326     public boolean isCommunityAdmin(long groupId) {
327         try {
328             return isCommunityAdminImpl(groupId);
329         }
330         catch (Exception e) {
331             _log.error(e, e);
332 
333             return false;
334         }
335     }
336 
337     public boolean isCommunityOwner(long groupId) {
338         try {
339             return isCommunityOwnerImpl(groupId);
340         }
341         catch (Exception e) {
342             _log.error(e, e);
343 
344             return false;
345         }
346     }
347 
348     protected PermissionCheckerBag getBag(long groupId) {
349         return bags.get(groupId);
350     }
351 
352     protected long[] getResourceIds(
353             long companyId, long groupId, String name, String primKey,
354             String actionId)
355         throws Exception {
356 
357         // Individual
358 
359         long[] resourceIds = new long[4];
360 
361         try {
362             Resource resource = ResourceLocalServiceUtil.getResource(
363                 companyId, name, ResourceConstants.SCOPE_INDIVIDUAL, primKey);
364 
365             resourceIds[0] = resource.getResourceId();
366         }
367         catch (NoSuchResourceException nsre) {
368             if (_log.isWarnEnabled()) {
369                 _log.warn(
370                     "Resource " + companyId + " " + name + " " +
371                         ResourceConstants.SCOPE_INDIVIDUAL + " " + primKey +
372                             " does not exist");
373             }
374         }
375 
376         // Group
377 
378         try {
379             if (groupId > 0) {
380                 Resource resource = ResourceLocalServiceUtil.getResource(
381                     companyId, name, ResourceConstants.SCOPE_GROUP,
382                     String.valueOf(groupId));
383 
384                 resourceIds[1] = resource.getResourceId();
385             }
386         }
387         catch (NoSuchResourceException nsre) {
388             if (_log.isWarnEnabled()) {
389                 _log.warn(
390                     "Resource " + companyId + " " + name + " " +
391                         ResourceConstants.SCOPE_GROUP + " " + groupId +
392                             " does not exist");
393             }
394         }
395 
396         // Group template
397 
398         try {
399             if (groupId > 0) {
400                 Resource resource = ResourceLocalServiceUtil.getResource(
401                     companyId, name, ResourceConstants.SCOPE_GROUP_TEMPLATE,
402                     String.valueOf(GroupImpl.DEFAULT_PARENT_GROUP_ID));
403 
404                 resourceIds[2] = resource.getResourceId();
405             }
406         }
407         catch (NoSuchResourceException nsre) {
408             if (_log.isWarnEnabled()) {
409                 _log.warn(
410                     "Resource " + companyId + " " + name + " " +
411                         ResourceConstants.SCOPE_GROUP_TEMPLATE + " " +
412                             GroupImpl.DEFAULT_PARENT_GROUP_ID +
413                                 " does not exist");
414             }
415         }
416 
417         // Company
418 
419         try {
420             Resource resource = ResourceLocalServiceUtil.getResource(
421                 companyId, name, ResourceConstants.SCOPE_COMPANY,
422                 String.valueOf(companyId));
423 
424             resourceIds[3] = resource.getResourceId();
425         }
426         catch (NoSuchResourceException nsre) {
427             if (_log.isWarnEnabled()) {
428                 _log.warn(
429                     "Resource " + companyId + " " + name + " " +
430                         ResourceConstants.SCOPE_COMPANY + " " + companyId +
431                             " does not exist");
432             }
433         }
434 
435         return resourceIds;
436     }
437 
438     protected List<Organization> getUserOrgs(long userId) throws Exception {
439         List<Organization> userOrgs =
440             OrganizationLocalServiceUtil.getUserOrganizations(userId);
441 
442         if (userOrgs.size() == 0) {
443             return userOrgs;
444         }
445 
446         List<Organization> organizations = new UniqueList<Organization>();
447 
448         for (Organization organization : userOrgs) {
449             if (!organizations.contains(organization)) {
450                 organizations.add(organization);
451 
452                 List<Organization> ancestorOrganizations =
453                     OrganizationLocalServiceUtil.getParentOrganizations(
454                         organization.getOrganizationId());
455 
456                 organizations.addAll(ancestorOrganizations);
457             }
458         }
459 
460         return organizations;
461     }
462 
463     protected boolean hasGuestPermission(
464             String name, String primKey, String actionId)
465         throws Exception {
466 
467         if (name.indexOf(StringPool.PERIOD) != -1) {
468 
469             // Check unsupported model actions
470 
471             List<String> actions = ResourceActionsUtil.
472                 getModelResourceGuestUnsupportedActions(name);
473 
474             if (actions.contains(actionId)) {
475                 return false;
476             }
477         }
478         else {
479 
480             // Check unsupported portlet actions
481 
482             List<String> actions = ResourceActionsUtil.
483                 getPortletResourceGuestUnsupportedActions(name);
484 
485             if (actions.contains(actionId)) {
486                 return false;
487             }
488         }
489 
490         long companyId = user.getCompanyId();
491 
492         long[] resourceIds = getResourceIds(
493             companyId, 0, name, primKey, actionId);
494 
495         PermissionCheckerBag bag = getBag(GUEST_GROUP_BAG_ID);
496 
497         if (bag == null) {
498             Group guestGroup = GroupLocalServiceUtil.getGroup(
499                 companyId, GroupImpl.GUEST);
500 
501             List<Role> roles = RoleLocalServiceUtil.getGroupRoles(
502                 guestGroup.getGroupId());
503 
504             bag = new PermissionCheckerBagImpl(
505                 defaultUserId, new ArrayList<Group>(),
506                 new ArrayList<Organization>(), new ArrayList<Group>(),
507                 new ArrayList<Group>(), new ArrayList<Group>(), roles);
508 
509             putBag(GUEST_GROUP_BAG_ID, bag);
510         }
511 
512         try {
513             return PermissionLocalServiceUtil.hasUserPermissions(
514                 defaultUserId, 0, actionId, resourceIds, bag);
515         }
516         catch (Exception e) {
517             return false;
518         }
519     }
520 
521     protected boolean hasPermissionImpl(
522         long groupId, String name, String primKey, String actionId) {
523 
524         try {
525             if (!signedIn) {
526                 return hasGuestPermission(name, primKey, actionId);
527             }
528             else {
529                 boolean value = false;
530 
531                 if (checkGuest) {
532                     value = hasGuestPermission(name, primKey, actionId);
533                 }
534 
535                 if (!value) {
536                     value = hasUserPermission(
537                         groupId, name, primKey, actionId, true);
538                 }
539 
540                 return value;
541             }
542         }
543         catch (Exception e) {
544             _log.error(e, e);
545 
546             return false;
547         }
548     }
549 
550     public boolean hasUserPermissionImpl(
551             long groupId, String name, String primKey, String actionId,
552             boolean checkAdmin)
553         throws Exception {
554 
555         StopWatch stopWatch = null;
556 
557         if (_log.isDebugEnabled()) {
558             stopWatch = new StopWatch();
559 
560             stopWatch.start();
561         }
562 
563         long companyId = user.getCompanyId();
564 
565         boolean hasLayoutManagerPermission = true;
566 
567         // Check if the layout manager has permission to do this action for the
568         // current portlet
569 
570         if ((Validator.isNotNull(name)) && (Validator.isNotNull(primKey)) &&
571             (primKey.indexOf(PortletImpl.LAYOUT_SEPARATOR) != -1)) {
572 
573             hasLayoutManagerPermission =
574                 PortletPermissionUtil.hasLayoutManagerPermission(
575                     name, actionId);
576         }
577 
578         if (checkAdmin &&
579             (isCompanyAdminImpl(companyId) ||
580                 (isCommunityAdminImpl(groupId) &&
581                     hasLayoutManagerPermission))) {
582 
583             return true;
584         }
585 
586         logHasUserPermission(groupId, name, primKey, actionId, stopWatch, 1);
587 
588         long[] resourceIds = getResourceIds(
589             companyId, groupId, name, primKey, actionId);
590 
591         logHasUserPermission(groupId, name, primKey, actionId, stopWatch, 2);
592 
593         // Check if user has access to perform the action on the given
594         // resource scopes. The resources are scoped to check first for an
595         // individual class, then for the group that the class may belong
596         // to, and then for the company that the class belongs to.
597 
598         PermissionCheckerBag bag = getBag(groupId);
599 
600         boolean value = PermissionLocalServiceUtil.hasUserPermissions(
601             user.getUserId(), groupId, actionId, resourceIds, bag);
602 
603         logHasUserPermission(groupId, name, primKey, actionId, stopWatch, 3);
604 
605         return value;
606     }
607 
608     protected boolean isCompanyAdminImpl(long companyId) throws Exception {
609         if (isOmniadmin()) {
610             return true;
611         }
612 
613         Boolean value = companyAdmins.get(companyId);
614 
615         if (value == null) {
616             boolean hasAdminRole = RoleLocalServiceUtil.hasUserRole(
617                 user.getUserId(), companyId, RoleImpl.ADMINISTRATOR, true);
618 
619             value = Boolean.valueOf(hasAdminRole);
620 
621             companyAdmins.put(companyId, value);
622         }
623 
624         return value.booleanValue();
625     }
626 
627     protected boolean isCommunityAdminImpl(long groupId) throws Exception {
628         if (isOmniadmin()) {
629             return true;
630         }
631 
632         if (groupId <= 0) {
633             return false;
634         }
635 
636         Group group = GroupLocalServiceUtil.getGroup(groupId);
637 
638         if (isCompanyAdmin(group.getCompanyId())) {
639             return true;
640         }
641 
642         PermissionCheckerBag bag = getBag(groupId);
643 
644         if (bag == null) {
645             _log.error("Bag should never be null");
646         }
647 
648         if (bag.isCommunityAdmin(this, group)) {
649             return true;
650         }
651         else {
652             return false;
653         }
654     }
655 
656     protected boolean isCommunityOwnerImpl(long groupId) throws Exception {
657         if (isOmniadmin()) {
658             return true;
659         }
660 
661         if (groupId <= 0) {
662             return false;
663         }
664 
665         Group group = GroupLocalServiceUtil.getGroup(groupId);
666 
667         if (isCompanyAdmin(group.getCompanyId())) {
668             return true;
669         }
670 
671         PermissionCheckerBag bag = getBag(groupId);
672 
673         if (bag == null) {
674             _log.error("Bag should never be null");
675         }
676 
677         if (bag.isCommunityOwner(this, group)) {
678             return true;
679         }
680         else {
681             return false;
682         }
683     }
684 
685     protected void logHasUserPermission(
686         long groupId, String name, String primKey, String actionId,
687         StopWatch stopWatch, int block) {
688 
689         if (!_log.isDebugEnabled()) {
690             return;
691         }
692 
693         _log.debug(
694             "Checking user permission block " + block + " for " + groupId +
695                 " " + name + " " + primKey + " " + actionId + " takes " +
696                     stopWatch.getTime() + " ms");
697     }
698 
699     protected void putBag(long groupId, PermissionCheckerBag bag) {
700         bags.put(groupId, bag);
701     }
702 
703     protected static final int GUEST_GROUP_BAG_ID = -101;
704 
705     protected static final String RESULTS_SEPARATOR = "_RESULTS_SEPARATOR_";
706 
707     protected User user;
708     protected long defaultUserId;
709     protected boolean signedIn;
710     protected boolean checkGuest;
711     protected Boolean omniadmin;
712     protected Map<Long, Boolean> companyAdmins = new HashMap<Long, Boolean>();
713     protected Map<Long, PermissionCheckerBag> bags =
714         new HashMap<Long, PermissionCheckerBag>();
715 
716     private static Log _log = LogFactory.getLog(PermissionCheckerImpl.class);
717 
718 }