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