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