001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.permission;
016    
017    import com.liferay.portal.kernel.util.ArrayUtil;
018    import com.liferay.portal.kernel.util.CharPool;
019    import com.liferay.portal.kernel.util.StringBundler;
020    import com.liferay.portal.kernel.util.StringPool;
021    import com.liferay.portal.kernel.util.StringUtil;
022    import com.liferay.portal.kernel.util.Validator;
023    import com.liferay.portal.model.ResourceConstants;
024    import com.liferay.portal.service.ResourceBlockLocalServiceUtil;
025    import com.liferay.portal.service.ResourceTypePermissionLocalServiceUtil;
026    import com.liferay.portal.util.PropsValues;
027    import com.liferay.util.dao.orm.CustomSQLUtil;
028    
029    import java.util.ArrayList;
030    import java.util.HashSet;
031    import java.util.List;
032    import java.util.Set;
033    
034    /**
035     * @author Raymond Augé
036     * @author Connor McKay
037     */
038    public class InlineSQLHelperImpl implements InlineSQLHelper {
039    
040            public static final String FILTER_BY_RESOURCE_BLOCK_ID =
041                    InlineSQLHelper.class.getName() + ".filterByResourceBlockId";
042    
043            public static final String FILTER_BY_RESOURCE_BLOCK_ID_OWNER =
044                    InlineSQLHelper.class.getName() + ".filterByResourceBlockIdOwner";
045    
046            public static final String FIND_BY_RESOURCE_BLOCK_ID =
047                    InlineSQLHelper.class.getName() + ".findByResourceBlockId";
048    
049            public static final String JOIN_RESOURCE_PERMISSION =
050                    InlineSQLHelper.class.getName() + ".joinResourcePermission";
051    
052            public boolean isEnabled() {
053                    return isEnabled(0);
054            }
055    
056            public boolean isEnabled(long groupId) {
057                    if (!PropsValues.PERMISSIONS_INLINE_SQL_CHECK_ENABLED) {
058                            return false;
059                    }
060    
061                    PermissionChecker permissionChecker =
062                            PermissionThreadLocal.getPermissionChecker();
063    
064                    if (permissionChecker == null) {
065                            return false;
066                    }
067    
068                    if (groupId > 0) {
069                            if (permissionChecker.isGroupAdmin(groupId) ||
070                                    permissionChecker.isGroupOwner(groupId)) {
071    
072                                    return false;
073                            }
074                    }
075                    else {
076                            if (permissionChecker.isCompanyAdmin()) {
077                                    return false;
078                            }
079                    }
080    
081                    return true;
082            }
083    
084            public boolean isEnabled(long[] groupIds) {
085                    if (!PropsValues.PERMISSIONS_INLINE_SQL_CHECK_ENABLED) {
086                            return false;
087                    }
088    
089                    for (long groupId : groupIds) {
090                            if (isEnabled(groupId)) {
091                                    return true;
092                            }
093                    }
094    
095                    return false;
096            }
097    
098            public String replacePermissionCheck(
099                    String sql, String className, String classPKField) {
100    
101                    return replacePermissionCheck(
102                            sql, className, classPKField, null, new long[] {0}, null);
103            }
104    
105            public String replacePermissionCheck(
106                    String sql, String className, String classPKField, long groupId) {
107    
108                    return replacePermissionCheck(
109                            sql, className, classPKField, null, new long[] {groupId}, null);
110            }
111    
112            public String replacePermissionCheck(
113                    String sql, String className, String classPKField, long groupId,
114                    String bridgeJoin) {
115    
116                    return replacePermissionCheck(
117                            sql, className, classPKField, null, new long[] {groupId},
118                            bridgeJoin);
119            }
120    
121            public String replacePermissionCheck(
122                    String sql, String className, String classPKField, long[] groupIds) {
123    
124                    return replacePermissionCheck(
125                            sql, className, classPKField, null, groupIds, null);
126            }
127    
128            public String replacePermissionCheck(
129                    String sql, String className, String classPKField, long[] groupIds,
130                    String bridgeJoin) {
131    
132                    return replacePermissionCheck(
133                            sql, className, classPKField, null, groupIds, bridgeJoin);
134            }
135    
136            public String replacePermissionCheck(
137                    String sql, String className, String classPKField, String userIdField) {
138    
139                    return replacePermissionCheck(
140                            sql, className, classPKField, userIdField, new long[] {0}, null);
141            }
142    
143            public String replacePermissionCheck(
144                    String sql, String className, String classPKField, String userIdField,
145                    long groupId) {
146    
147                    return replacePermissionCheck(
148                            sql, className, classPKField, userIdField, new long[] {groupId},
149                            null);
150            }
151    
152            public String replacePermissionCheck(
153                    String sql, String className, String classPKField, String userIdField,
154                    long groupId, String bridgeJoin) {
155    
156                    return replacePermissionCheck(
157                            sql, className, classPKField, userIdField, new long[] {groupId},
158                            bridgeJoin);
159            }
160    
161            public String replacePermissionCheck(
162                    String sql, String className, String classPKField, String userIdField,
163                    long[] groupIds) {
164    
165                    return replacePermissionCheck(
166                            sql, className, classPKField, userIdField, groupIds, null);
167            }
168    
169            public String replacePermissionCheck(
170                    String sql, String className, String classPKField, String userIdField,
171                    long[] groupIds, String bridgeJoin) {
172    
173                    return replacePermissionCheck(
174                            sql, className, classPKField, userIdField, null, groupIds,
175                            bridgeJoin);
176            }
177    
178            public String replacePermissionCheck(
179                    String sql, String className, String classPKField, String userIdField,
180                    String bridgeJoin) {
181    
182                    return replacePermissionCheck(
183                            sql, className, classPKField, userIdField, 0, bridgeJoin);
184            }
185    
186            public String replacePermissionCheck(
187                    String sql, String className, String classPKField, String userIdField,
188                    String groupIdField, long[] groupIds, String bridgeJoin) {
189    
190                    if (!isEnabled(groupIds)) {
191                            return sql;
192                    }
193    
194                    if (Validator.isNull(className)) {
195                            throw new IllegalArgumentException("className is null");
196                    }
197    
198                    if (Validator.isNull(sql)) {
199                            return sql;
200                    }
201    
202                    if (ResourceBlockLocalServiceUtil.isSupported(className)) {
203                            return replacePermissionCheckBlocks(
204                                    sql, className, classPKField, userIdField, groupIds,
205                                    bridgeJoin);
206                    }
207                    else {
208                            return replacePermissionCheckJoin(
209                                    sql, className, classPKField, userIdField, groupIdField,
210                                    groupIds, bridgeJoin);
211                    }
212            }
213    
214            protected Set<Long> getOwnerResourceBlockIds(
215                    long companyId, long[] groupIds, String className) {
216    
217                    Set<Long> resourceBlockIds = new HashSet<Long>();
218    
219                    PermissionChecker permissionChecker =
220                            PermissionThreadLocal.getPermissionChecker();
221    
222                    for (long groupId : groupIds) {
223                            resourceBlockIds.addAll(
224                                    permissionChecker.getOwnerResourceBlockIds(
225                                            companyId, groupId, className, ActionKeys.VIEW));
226                    }
227    
228                    return resourceBlockIds;
229            }
230    
231            protected String getOwnerResourceBlockIdsSQL(
232                    PermissionChecker permissionChecker, long checkGroupId,
233                    String className, Set<Long> ownerResourceBlockIds) {
234    
235                    if (ownerResourceBlockIds.size() <
236                                    PropsValues.
237                                            PERMISSIONS_INLINE_SQL_RESOURCE_BLOCK_QUERY_THRESHHOLD) {
238    
239                            return StringUtil.merge(ownerResourceBlockIds);
240                    }
241    
242                    return StringUtil.replace(
243                            CustomSQLUtil.get(FIND_BY_RESOURCE_BLOCK_ID),
244                            new String[] {
245                                    "[$COMPANY_ID$]", "[$GROUP_ID$]", "[$RESOURCE_BLOCK_NAME$]",
246                                    "[$ROLE_ID$]"
247                            },
248                            new String[] {
249                                    String.valueOf(permissionChecker.getCompanyId()),
250                                    String.valueOf(checkGroupId), className,
251                                    StringUtil.valueOf(permissionChecker.getOwnerRoleId())
252                            });
253            }
254    
255            protected Set<Long> getResourceBlockIds(
256                    long companyId, long[] groupIds, String className) {
257    
258                    Set<Long> resourceBlockIds = new HashSet<Long>();
259    
260                    PermissionChecker permissionChecker =
261                            PermissionThreadLocal.getPermissionChecker();
262    
263                    for (long groupId : groupIds) {
264                            resourceBlockIds.addAll(
265                                    permissionChecker.getResourceBlockIds(
266                                            companyId, groupId, permissionChecker.getUserId(),
267                                            className, ActionKeys.VIEW));
268                    }
269    
270                    return resourceBlockIds;
271            }
272    
273            protected long[] getRoleIds(long groupId) {
274                    long[] roleIds = PermissionChecker.DEFAULT_ROLE_IDS;
275    
276                    PermissionChecker permissionChecker =
277                            PermissionThreadLocal.getPermissionChecker();
278    
279                    if (permissionChecker != null) {
280                            roleIds = permissionChecker.getRoleIds(
281                                    permissionChecker.getUserId(), groupId);
282                    }
283    
284                    return roleIds;
285            }
286    
287            protected long[] getRoleIds(long[] groupIds) {
288                    long[] roleIds = PermissionChecker.DEFAULT_ROLE_IDS;
289    
290                    for (long groupId : groupIds) {
291                            for (long roleId : getRoleIds(groupId)) {
292                                    if (!ArrayUtil.contains(roleIds, roleId)) {
293                                            roleIds = ArrayUtil.append(roleIds, roleId);
294                                    }
295                            }
296                    }
297    
298                    return roleIds;
299            }
300    
301            protected long getUserId() {
302                    long userId = 0;
303    
304                    PermissionChecker permissionChecker =
305                            PermissionThreadLocal.getPermissionChecker();
306    
307                    if (permissionChecker != null) {
308                            userId = permissionChecker.getUserId();
309                    }
310    
311                    return userId;
312            }
313    
314            protected String getUserResourceBlockIdsSQL(
315                    PermissionChecker permissionChecker, long checkGroupId, long[] roleIds,
316                    String className, Set<Long> userResourceBlockIds) {
317    
318                    if (userResourceBlockIds.size() <
319                                    PropsValues.
320                                            PERMISSIONS_INLINE_SQL_RESOURCE_BLOCK_QUERY_THRESHHOLD) {
321    
322                            return StringUtil.merge(userResourceBlockIds);
323                    }
324    
325                    return StringUtil.replace(
326                            CustomSQLUtil.get(FIND_BY_RESOURCE_BLOCK_ID),
327                            new String[] {
328                                    "[$COMPANY_ID$]", "[$GROUP_ID$]", "[$RESOURCE_BLOCK_NAME$]",
329                                    "[$ROLE_ID$]"
330                            },
331                            new String[] {
332                                    String.valueOf(permissionChecker.getCompanyId()),
333                                    String.valueOf(checkGroupId), className,
334                                    StringUtil.merge(roleIds)
335                            });
336            }
337    
338            protected String replacePermissionCheckBlocks(
339                    String sql, String className, String classPKField, String userIdField,
340                    long[] groupIds, String bridgeJoin) {
341    
342                    PermissionChecker permissionChecker =
343                            PermissionThreadLocal.getPermissionChecker();
344    
345                    long checkGroupId = 0;
346    
347                    if (groupIds.length == 1) {
348                            checkGroupId = groupIds[0];
349                    }
350    
351                    long[] roleIds = permissionChecker.getRoleIds(
352                            getUserId(), checkGroupId);
353    
354                    try {
355                            for (long roleId : roleIds) {
356                                    if (ResourceTypePermissionLocalServiceUtil.
357                                                    hasCompanyScopePermission(
358                                                            permissionChecker.getCompanyId(), className, roleId,
359                                                            ActionKeys.VIEW)) {
360    
361                                            return sql;
362                                    }
363                            }
364                    }
365                    catch (Exception e) {
366                    }
367    
368                    Set<Long> userResourceBlockIds = getResourceBlockIds(
369                            permissionChecker.getCompanyId(), groupIds, className);
370    
371                    String permissionWhere = StringPool.BLANK;
372    
373                    if (Validator.isNotNull(bridgeJoin)) {
374                            permissionWhere = bridgeJoin;
375                    }
376    
377                    Set<Long> ownerResourceBlockIds = getOwnerResourceBlockIds(
378                            permissionChecker.getCompanyId(), groupIds, className);
379    
380                    // If a user has regular access to a resource block, it isn't necessary
381                    // to check owner permissions on it as well.
382    
383                    ownerResourceBlockIds.removeAll(userResourceBlockIds);
384    
385                    // A SQL syntax error occurs if there is not at least one resource block
386                    // ID.
387    
388                    if (ownerResourceBlockIds.isEmpty()) {
389                            ownerResourceBlockIds.add(_NO_RESOURCE_BLOCKS_ID);
390                    }
391    
392                    if (userResourceBlockIds.isEmpty()) {
393                            userResourceBlockIds.add(_NO_RESOURCE_BLOCKS_ID);
394                    }
395    
396                    if (Validator.isNotNull(userIdField)) {
397                            permissionWhere = permissionWhere.concat(
398                                    CustomSQLUtil.get(FILTER_BY_RESOURCE_BLOCK_ID_OWNER));
399    
400                            permissionWhere = StringUtil.replace(
401                                    permissionWhere,
402                                    new String[] {
403                                            "[$OWNER_RESOURCE_BLOCK_ID$]", "[$USER_ID$]",
404                                            "[$USER_ID_FIELD$]", "[$USER_RESOURCE_BLOCK_ID$]"
405                                    },
406                                    new String[] {
407                                            getOwnerResourceBlockIdsSQL(
408                                                    permissionChecker, checkGroupId, className,
409                                                    ownerResourceBlockIds),
410                                            String.valueOf(permissionChecker.getUserId()), userIdField,
411                                            getUserResourceBlockIdsSQL(
412                                                    permissionChecker, checkGroupId, roleIds, className,
413                                                    userResourceBlockIds)
414                                    });
415                    }
416                    else {
417                            permissionWhere = permissionWhere.concat(
418                                    CustomSQLUtil.get(FILTER_BY_RESOURCE_BLOCK_ID));
419    
420                            permissionWhere = StringUtil.replace(
421                                    permissionWhere, "[$USER_RESOURCE_BLOCK_ID$]",
422                                    getUserResourceBlockIdsSQL(
423                                            permissionChecker, checkGroupId, roleIds, className,
424                                            userResourceBlockIds));
425                    }
426    
427                    int pos = sql.indexOf(_WHERE_CLAUSE);
428    
429                    if (pos != -1) {
430                            StringBundler sb = new StringBundler(4);
431    
432                            sb.append(sql.substring(0, pos));
433                            sb.append(permissionWhere);
434                            sb.append(" AND ");
435                            sb.append(sql.substring(pos + 7));
436    
437                            return sb.toString();
438                    }
439    
440                    pos = sql.indexOf(_GROUP_BY_CLAUSE);
441    
442                    if (pos != -1) {
443                            return sql.substring(0, pos + 1).concat(permissionWhere).concat(
444                                    sql.substring(pos + 1));
445                    }
446    
447                    pos = sql.indexOf(_ORDER_BY_CLAUSE);
448    
449                    if (pos != -1) {
450                            return sql.substring(0, pos + 1).concat(permissionWhere).concat(
451                                    sql.substring(pos + 1));
452                    }
453    
454                    return sql.concat(StringPool.SPACE).concat(permissionWhere);
455            }
456    
457            protected String replacePermissionCheckJoin(
458                    String sql, String className, String classPKField, String userIdField,
459                    String groupIdField, long[] groupIds, String bridgeJoin) {
460    
461                    if (Validator.isNull(classPKField)) {
462                            throw new IllegalArgumentException("classPKField is null");
463                    }
464    
465                    PermissionChecker permissionChecker =
466                            PermissionThreadLocal.getPermissionChecker();
467    
468                    long checkGroupId = 0;
469    
470                    if (groupIds.length == 1) {
471                            checkGroupId = groupIds[0];
472                    }
473    
474                    if (permissionChecker.hasPermission(
475                                    checkGroupId, className, 0, ActionKeys.VIEW)) {
476    
477                            return sql;
478                    }
479    
480                    String permissionJoin = StringPool.BLANK;
481    
482                    if (Validator.isNotNull(bridgeJoin)) {
483                            permissionJoin = bridgeJoin;
484                    }
485    
486                    permissionJoin += CustomSQLUtil.get(JOIN_RESOURCE_PERMISSION);
487    
488                    StringBundler sb = new StringBundler();
489    
490                    sb.append("(((InlineSQLResourcePermission.primKey = CAST_TEXT(");
491                    sb.append(classPKField);
492                    sb.append(")) AND (((");
493                    sb.append("InlineSQLResourcePermission.scope = ");
494                    sb.append(ResourceConstants.SCOPE_INDIVIDUAL);
495                    sb.append(") AND ");
496    
497                    long userId = getUserId();
498    
499                    boolean hasPreviousViewableGroup = false;
500    
501                    List<Long> viewableGroupIds = new ArrayList<Long>();
502    
503                    for (int j = 0; j < groupIds.length; j++) {
504                            long groupId = groupIds[j];
505    
506                            if (!permissionChecker.hasPermission(
507                                            groupId, className, 0, ActionKeys.VIEW)) {
508    
509                                    if ((j > 0) && hasPreviousViewableGroup) {
510                                            sb.append(" OR ");
511                                    }
512    
513                                    hasPreviousViewableGroup = true;
514    
515                                    sb.append(StringPool.OPEN_PARENTHESIS);
516    
517                                    if (Validator.isNull(groupIdField)) {
518                                            sb.append(
519                                                    classPKField.substring(
520                                                            0, classPKField.lastIndexOf(CharPool.PERIOD)));
521                                            sb.append(".groupId = ");
522                                    }
523                                    else {
524                                            sb.append(groupIdField);
525                                            sb.append(" = ");
526                                    }
527    
528                                    sb.append(groupId);
529                                    sb.append(StringPool.CLOSE_PARENTHESIS);
530    
531                                    long[] roleIds = getRoleIds(groupId);
532    
533                                    if (roleIds.length == 0) {
534                                            roleIds = _NO_ROLE_IDS;
535                                    }
536    
537                                    sb.append(" AND (");
538    
539                                    for (int i = 0; i < roleIds.length; i++) {
540                                            if (i > 0) {
541                                                    sb.append(" OR ");
542                                            }
543    
544                                            sb.append("InlineSQLResourcePermission.roleId = ");
545                                            sb.append(roleIds[i]);
546                                    }
547    
548                                    if (permissionChecker.isSignedIn()) {
549                                            sb.append(" OR ");
550    
551                                            if (Validator.isNotNull(userIdField)) {
552                                                    sb.append(StringPool.OPEN_PARENTHESIS);
553                                                    sb.append(userIdField);
554                                                    sb.append(" = ");
555                                                    sb.append(userId);
556                                                    sb.append(StringPool.CLOSE_PARENTHESIS);
557                                            }
558                                            else {
559                                                    sb.append("(InlineSQLResourcePermission.ownerId = ");
560                                                    sb.append(userId);
561                                                    sb.append(StringPool.CLOSE_PARENTHESIS);
562                                            }
563                                    }
564    
565                                    sb.append(StringPool.CLOSE_PARENTHESIS);
566                            }
567                            else {
568                                    viewableGroupIds.add(groupId);
569                            }
570                    }
571    
572                    sb.append(StringPool.CLOSE_PARENTHESIS);
573    
574                    if (!viewableGroupIds.isEmpty()) {
575                            for (Long viewableGroupId : viewableGroupIds) {
576                                    sb.append(" OR (");
577    
578                                    if (Validator.isNull(groupIdField)) {
579                                            sb.append(
580                                                    classPKField.substring(
581                                                            0, classPKField.lastIndexOf(CharPool.PERIOD)));
582                                            sb.append(".groupId = ");
583                                    }
584                                    else {
585                                            sb.append(groupIdField);
586                                            sb.append(" = ");
587                                    }
588    
589                                    sb.append(viewableGroupId);
590                                    sb.append(StringPool.CLOSE_PARENTHESIS);
591                            }
592                    }
593    
594                    sb.append(")))");
595    
596                    permissionJoin = StringUtil.replace(
597                            permissionJoin,
598                            new String[] {
599                                    "[$CLASS_NAME$]", "[$COMPANY_ID$]", "[$PRIM_KEYS$]"
600                            },
601                            new String[] {
602                                    className, String.valueOf(permissionChecker.getCompanyId()),
603                                    sb.toString()
604                            });
605    
606                    int pos = sql.indexOf(_WHERE_CLAUSE);
607    
608                    if (pos != -1) {
609                            return sql.substring(0, pos + 1).concat(permissionJoin).concat(
610                                    sql.substring(pos + 1));
611                    }
612    
613                    pos = sql.indexOf(_GROUP_BY_CLAUSE);
614    
615                    if (pos != -1) {
616                            return sql.substring(0, pos + 1).concat(permissionJoin).concat(
617                                    sql.substring(pos + 1));
618                    }
619    
620                    pos = sql.indexOf(_ORDER_BY_CLAUSE);
621    
622                    if (pos != -1) {
623                            return sql.substring(0, pos + 1).concat(permissionJoin).concat(
624                                    sql.substring(pos + 1));
625                    }
626    
627                    return sql.concat(StringPool.SPACE).concat(permissionJoin);
628            }
629    
630            private static final String _GROUP_BY_CLAUSE = " GROUP BY ";
631    
632            private static final long _NO_RESOURCE_BLOCKS_ID = -1;
633    
634            private static final long[] _NO_ROLE_IDS = {0};
635    
636            private static final String _ORDER_BY_CLAUSE = " ORDER BY ";
637    
638            private static final String _WHERE_CLAUSE = " WHERE ";
639    
640    }