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