001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.guard;
018
019import java.text.*;
020import java.util.*;
021import java.util.stream.*;
022
023import org.apache.juneau.rest.*;
024
025/**
026 * {@link RestGuard} that uses role expressions to determine whether an authenticated user has access to a class or method.
027 *
028 * <p>
029 * The role expression supports the following constructs:
030 * <ul>
031 *    <li><js>"foo"</js> - Single arguments.
032 *    <li><js>"foo,bar,baz"</js> - Multiple OR'ed arguments.
033 *    <li><js>"foo | bar | bqz"</js> - Multiple OR'ed arguments, pipe syntax.
034 *    <li><js>"foo || bar || bqz"</js> - Multiple OR'ed arguments, Java-OR syntax.
035 *    <li><js>"fo*"</js> - Patterns including <js>'*'</js> and <js>'?'</js>.
036 *    <li><js>"fo* &amp; *oo"</js> - Multiple AND'ed arguments, ampersand syntax.
037 *    <li><js>"fo* &amp;&amp; *oo"</js> - Multiple AND'ed arguments, Java-AND syntax.
038 *    <li><js>"fo* || (*oo || bar)"</js> - Parenthesis.
039 * </ul>
040 *
041 * <h5 class='section'>Notes:</h5><ul>
042 *    <li class='note'>AND operations take precedence over OR operations (as expected).
043 *    <li class='note'>Whitespace is ignored.
044 *    <li class='note'><jk>null</jk> or empty expressions always match as <jk>false</jk>.
045 * </ul>
046 *
047 * <h5 class='section'>See Also:</h5><ul>
048 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Guards">Guards</a>
049 * </ul>
050 */
051public class RoleBasedRestGuard extends RestGuard {
052
053   private final Set<String> roles;
054   private final RoleMatcher roleMatcher;
055
056   /**
057    * Constructor.
058    *
059    * @param declaredRoles
060    *    List of possible declared roles.
061    *    <br>If <jk>null</jk>, we find the roles in the expression itself.
062    *    <br>This is only needed if you're using pattern matching in the expression.
063    * @param roleExpression
064    *    The role expression.
065    *    <br>If <jk>null</jk> or empty/blanks, the this guard will never pass.
066    * @throws ParseException Invalid role expression syntax.
067    */
068   public RoleBasedRestGuard(Set<String> declaredRoles, String roleExpression) throws ParseException {
069      roleMatcher = new RoleMatcher(roleExpression);
070      roles = new TreeSet<>(declaredRoles == null ? roleMatcher.getRolesInExpression() : declaredRoles);
071   }
072
073   @Override
074   public boolean isRequestAllowed(RestRequest req) {
075      Set<String> userRoles = roles.stream().filter(x -> req.isUserInRole(x)).collect(Collectors.toSet());
076      return roleMatcher.matches(userRoles);
077   }
078}