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                    long[] roleIds = PermissionChecker.DEFAULT_ROLE_IDS;
306    
307                    for (long groupId : groupIds) {
308                            for (long roleId : getRoleIds(groupId)) {
309                                    if (!ArrayUtil.contains(roleIds, roleId)) {
310                                            roleIds = ArrayUtil.append(roleIds, roleId);
311                                    }
312                            }
313                    }
314    
315                    return roleIds;
316            }
317    
318            protected long getUserId() {
319                    long userId = 0;
320    
321                    PermissionChecker permissionChecker =
322                            PermissionThreadLocal.getPermissionChecker();
323    
324                    if (permissionChecker != null) {
325                            userId = permissionChecker.getUserId();
326                    }
327    
328                    return userId;
329            }
330    
331            protected String getUserResourceBlockIdsSQL(
332                    PermissionChecker permissionChecker, long checkGroupId, long[] roleIds,
333                    String className, Set<Long> userResourceBlockIds) {
334    
335                    if (userResourceBlockIds.size() <
336                                    PropsValues.
337                                            PERMISSIONS_INLINE_SQL_RESOURCE_BLOCK_QUERY_THRESHHOLD) {
338    
339                            return StringUtil.merge(userResourceBlockIds);
340                    }
341    
342                    return StringUtil.replace(
343                            CustomSQLUtil.get(FIND_BY_RESOURCE_BLOCK_ID),
344                            new String[] {
345                                    "[$COMPANY_ID$]", "[$GROUP_ID$]", "[$RESOURCE_BLOCK_NAME$]",
346                                    "[$ROLE_ID$]"
347                            },
348                            new String[] {
349                                    String.valueOf(permissionChecker.getCompanyId()),
350                                    String.valueOf(checkGroupId), className,
351                                    StringUtil.merge(roleIds)
352                            });
353            }
354    
355            protected String replacePermissionCheckBlocks(
356                    String sql, String className, String classPKField, String userIdField,
357                    long[] groupIds, String bridgeJoin) {
358    
359                    PermissionChecker permissionChecker =
360                            PermissionThreadLocal.getPermissionChecker();
361    
362                    long checkGroupId = 0;
363    
364                    if (groupIds.length == 1) {
365                            checkGroupId = groupIds[0];
366                    }
367    
368                    long[] roleIds = permissionChecker.getRoleIds(
369                            getUserId(), checkGroupId);
370    
371                    try {
372                            for (long roleId : roleIds) {
373                                    if (ResourceTypePermissionLocalServiceUtil.
374                                                    hasCompanyScopePermission(
375                                                            permissionChecker.getCompanyId(), className, roleId,
376                                                            ActionKeys.VIEW)) {
377    
378                                            return sql;
379                                    }
380                            }
381                    }
382                    catch (Exception e) {
383                    }
384    
385                    Set<Long> userResourceBlockIds = getResourceBlockIds(
386                            permissionChecker.getCompanyId(), groupIds, className);
387    
388                    String permissionWhere = StringPool.BLANK;
389    
390                    if (Validator.isNotNull(bridgeJoin)) {
391                            permissionWhere = bridgeJoin;
392                    }
393    
394                    Set<Long> ownerResourceBlockIds = getOwnerResourceBlockIds(
395                            permissionChecker.getCompanyId(), groupIds, className);
396    
397                    // If a user has regular access to a resource block, it isn't necessary
398                    // to check owner permissions on it as well.
399    
400                    ownerResourceBlockIds.removeAll(userResourceBlockIds);
401    
402                    // A SQL syntax error occurs if there is not at least one resource block
403                    // ID.
404    
405                    if (ownerResourceBlockIds.isEmpty()) {
406                            ownerResourceBlockIds.add(_NO_RESOURCE_BLOCKS_ID);
407                    }
408    
409                    if (userResourceBlockIds.isEmpty()) {
410                            userResourceBlockIds.add(_NO_RESOURCE_BLOCKS_ID);
411                    }
412    
413                    if (Validator.isNotNull(userIdField)) {
414                            permissionWhere = permissionWhere.concat(
415                                    CustomSQLUtil.get(FILTER_BY_RESOURCE_BLOCK_ID_OWNER));
416    
417                            permissionWhere = StringUtil.replace(
418                                    permissionWhere,
419                                    new String[] {
420                                            "[$OWNER_RESOURCE_BLOCK_ID$]", "[$USER_ID$]",
421                                            "[$USER_ID_FIELD$]", "[$USER_RESOURCE_BLOCK_ID$]"
422                                    },
423                                    new String[] {
424                                            getOwnerResourceBlockIdsSQL(
425                                                    permissionChecker, checkGroupId, className,
426                                                    ownerResourceBlockIds),
427                                            String.valueOf(permissionChecker.getUserId()), userIdField,
428                                            getUserResourceBlockIdsSQL(
429                                                    permissionChecker, checkGroupId, roleIds, className,
430                                                    userResourceBlockIds)
431                                    });
432                    }
433                    else {
434                            permissionWhere = permissionWhere.concat(
435                                    CustomSQLUtil.get(FILTER_BY_RESOURCE_BLOCK_ID));
436    
437                            permissionWhere = StringUtil.replace(
438                                    permissionWhere, "[$USER_RESOURCE_BLOCK_ID$]",
439                                    getUserResourceBlockIdsSQL(
440                                            permissionChecker, checkGroupId, roleIds, className,
441                                            userResourceBlockIds));
442                    }
443    
444                    int pos = sql.indexOf(_WHERE_CLAUSE);
445    
446                    if (pos != -1) {
447                            StringBundler sb = new StringBundler(4);
448    
449                            sb.append(sql.substring(0, pos));
450                            sb.append(permissionWhere);
451                            sb.append(" AND ");
452                            sb.append(sql.substring(pos + 7));
453    
454                            return sb.toString();
455                    }
456    
457                    pos = sql.indexOf(_GROUP_BY_CLAUSE);
458    
459                    if (pos != -1) {
460                            return sql.substring(0, pos + 1).concat(permissionWhere).concat(
461                                    sql.substring(pos + 1));
462                    }
463    
464                    pos = sql.indexOf(_ORDER_BY_CLAUSE);
465    
466                    if (pos != -1) {
467                            return sql.substring(0, pos + 1).concat(permissionWhere).concat(
468                                    sql.substring(pos + 1));
469                    }
470    
471                    return sql.concat(StringPool.SPACE).concat(permissionWhere);
472            }
473    
474            protected String replacePermissionCheckJoin(
475                    String sql, String className, String classPKField, String userIdField,
476                    String groupIdField, long[] groupIds, String bridgeJoin) {
477    
478                    if (Validator.isNull(classPKField)) {
479                            throw new IllegalArgumentException("classPKField is null");
480                    }
481    
482                    PermissionChecker permissionChecker =
483                            PermissionThreadLocal.getPermissionChecker();
484    
485                    long checkGroupId = 0;
486    
487                    if (groupIds.length == 1) {
488                            checkGroupId = groupIds[0];
489                    }
490    
491                    if (permissionChecker.hasPermission(
492                                    checkGroupId, className, 0, ActionKeys.VIEW)) {
493    
494                            return sql;
495                    }
496    
497                    String permissionJoin = StringPool.BLANK;
498    
499                    if (Validator.isNotNull(bridgeJoin)) {
500                            permissionJoin = bridgeJoin;
501                    }
502    
503                    permissionJoin += CustomSQLUtil.get(JOIN_RESOURCE_PERMISSION);
504    
505                    StringBundler sb = new StringBundler();
506    
507                    sb.append("(((InlineSQLResourcePermission.primKey = CAST_TEXT(");
508                    sb.append(classPKField);
509                    sb.append(")) AND (((");
510                    sb.append("InlineSQLResourcePermission.scope = ");
511                    sb.append(ResourceConstants.SCOPE_INDIVIDUAL);
512                    sb.append(") AND ");
513    
514                    long userId = getUserId();
515    
516                    boolean hasPreviousViewableGroup = false;
517    
518                    List<Long> viewableGroupIds = new ArrayList<Long>();
519    
520                    for (int j = 0; j < groupIds.length; j++) {
521                            long groupId = groupIds[j];
522    
523                            if (!permissionChecker.hasPermission(
524                                            groupId, className, 0, ActionKeys.VIEW)) {
525    
526                                    if ((j > 0) && hasPreviousViewableGroup) {
527                                            sb.append(" OR ");
528                                    }
529    
530                                    hasPreviousViewableGroup = true;
531    
532                                    sb.append(StringPool.OPEN_PARENTHESIS);
533    
534                                    if (Validator.isNull(groupIdField)) {
535                                            sb.append(
536                                                    classPKField.substring(
537                                                            0, classPKField.lastIndexOf(CharPool.PERIOD)));
538                                            sb.append(".groupId = ");
539                                    }
540                                    else {
541                                            sb.append(groupIdField);
542                                            sb.append(" = ");
543                                    }
544    
545                                    sb.append(groupId);
546                                    sb.append(StringPool.CLOSE_PARENTHESIS);
547    
548                                    long[] roleIds = getRoleIds(groupId);
549    
550                                    if (roleIds.length == 0) {
551                                            roleIds = _NO_ROLE_IDS;
552                                    }
553    
554                                    sb.append(" AND (");
555    
556                                    for (int i = 0; i < roleIds.length; i++) {
557                                            if (i > 0) {
558                                                    sb.append(" OR ");
559                                            }
560    
561                                            sb.append("InlineSQLResourcePermission.roleId = ");
562                                            sb.append(roleIds[i]);
563                                    }
564    
565                                    if (permissionChecker.isSignedIn()) {
566                                            sb.append(" OR ");
567    
568                                            if (Validator.isNotNull(userIdField)) {
569                                                    sb.append(StringPool.OPEN_PARENTHESIS);
570                                                    sb.append(userIdField);
571                                                    sb.append(" = ");
572                                                    sb.append(userId);
573                                                    sb.append(StringPool.CLOSE_PARENTHESIS);
574                                            }
575                                            else {
576                                                    sb.append("(InlineSQLResourcePermission.ownerId = ");
577                                                    sb.append(userId);
578                                                    sb.append(StringPool.CLOSE_PARENTHESIS);
579                                            }
580                                    }
581    
582                                    sb.append(StringPool.CLOSE_PARENTHESIS);
583                            }
584                            else {
585                                    viewableGroupIds.add(groupId);
586                            }
587                    }
588    
589                    sb.append(StringPool.CLOSE_PARENTHESIS);
590    
591                    if (!viewableGroupIds.isEmpty()) {
592                            for (Long viewableGroupId : viewableGroupIds) {
593                                    sb.append(" OR (");
594    
595                                    if (Validator.isNull(groupIdField)) {
596                                            sb.append(
597                                                    classPKField.substring(
598                                                            0, classPKField.lastIndexOf(CharPool.PERIOD)));
599                                            sb.append(".groupId = ");
600                                    }
601                                    else {
602                                            sb.append(groupIdField);
603                                            sb.append(" = ");
604                                    }
605    
606                                    sb.append(viewableGroupId);
607                                    sb.append(StringPool.CLOSE_PARENTHESIS);
608                            }
609                    }
610    
611                    sb.append(")))");
612    
613                    permissionJoin = StringUtil.replace(
614                            permissionJoin,
615                            new String[] {
616                                    "[$CLASS_NAME$]", "[$COMPANY_ID$]", "[$PRIM_KEYS$]"
617                            },
618                            new String[] {
619                                    className, String.valueOf(permissionChecker.getCompanyId()),
620                                    sb.toString()
621                            });
622    
623                    int pos = sql.indexOf(_WHERE_CLAUSE);
624    
625                    if (pos != -1) {
626                            return sql.substring(0, pos + 1).concat(permissionJoin).concat(
627                                    sql.substring(pos + 1));
628                    }
629    
630                    pos = sql.indexOf(_GROUP_BY_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(_ORDER_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                    return sql.concat(StringPool.SPACE).concat(permissionJoin);
645            }
646    
647            private static final String _GROUP_BY_CLAUSE = " GROUP BY ";
648    
649            private static final long _NO_RESOURCE_BLOCKS_ID = -1;
650    
651            private static final long[] _NO_ROLE_IDS = {0};
652    
653            private static final String _ORDER_BY_CLAUSE = " ORDER BY ";
654    
655            private static final String _WHERE_CLAUSE = " WHERE ";
656    
657    }