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