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