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