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