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