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