blob: 5cbf15f3b473fcf0e2d7fd9f2dda2683ffa00c6c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.fuseki.auth;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.Objects;
import org.apache.jena.fuseki.Fuseki;
import org.apache.jena.fuseki.FusekiConfigException;
/** Authorization Policies.
* See {@link Users} for special user names.
*/
public class Auth {
public static final String dftRealm = "TripleStore";
/** Any authenticated user. */
public static AuthPolicy ANY_USER = (user) -> user != null;
/** Any user, whether authenticated or not. */
public static AuthPolicy ANY_ANON = (user) -> true;
/** Never allow. */
public static AuthPolicy DENY = (user) -> false;
/** A policy that allows specific users (convenience wrapped for {@link #policyAllowSpecific(Collection)}). */
public static AuthPolicy policyAllowSpecific(String... allowedUsers) {
return Auth.policyAllowSpecific(Arrays.asList(allowedUsers));
}
/**
* A policy that allows specific users.
* <ul>
* <li>If any user is {@linkplain Users#UserAnyLoggedIn}, then this policy is the same as {@linkplain #ANY_USER}.
* <li>If any user is {@linkplain Users#UserAnyAnon}, then this policy is the same as {@linkplain #ANY_ANON}.
* </ul>
*/
public static AuthPolicy policyAllowSpecific(Collection<String> allowedUsers) {
Objects.requireNonNull(allowedUsers, "allowedUsers");
if ( allowedUsers.contains(Users.UserAnyLoggedIn) ) {
if ( allowedUsers.size() > 1 )
Fuseki.configLog.warn("Both 'any user' and a list of users given");
return ANY_USER;
}
if ( allowedUsers.contains(Users.UserAnyAnon) ) {
if ( allowedUsers.size() > 1 )
Fuseki.configLog.warn("Both 'anon user' and a list of users given");
return ANY_ANON;
}
if ( allowedUsers.stream().anyMatch(Objects::isNull) )
throw new FusekiConfigException("null user found : "+allowedUsers);
if ( allowedUsers.isEmpty() )
return Auth.DENY;
return new AuthUserList(allowedUsers);
}
/**
* Test whether a user (principal) is allowed by a authorization policy.
* The policy can be null, meaning no restrictions, and the function returns true.
* {@code user} maybe null, meaning unauthenticated and any policy must deal with this.
* @param user
* @param policy
* @return boolean True if the policy is null or allows the user.
*/
public static boolean allow(String user, AuthPolicy policy) {
if ( policy == null )
return true;
return policy.isAllowed(user);
}
/**
* Test whether a user (principal) is allowed by a authorization policy
* and perform an action if the policy does not allow the user.
* The action can throw an exception.
* Additional, return true/false - see {@link #allow(String, AuthPolicy)}.
* The policy can be null, meaning no restrictions, and the function returns true.
* {@code user} maybe null, meaning unauthenticated and any policy must deal with this.
* @param user
* @param policy
* @param notAllowed Runnable to execute if the policy does not allow the user.
*/
public static boolean allow(String user, AuthPolicy policy, Runnable notAllowed) {
if ( allow(user, policy) )
return true;
notAllowed.run();
return false;
}
/**
* Calculate the value of the "Authentication" HTTP header for basic auth. Basic
* auth is not secure when used over HTTP (the password can be extracted). Use
* with HTTPS is better.
* <p>
* Unlike digest auth, basic auth can be setup without an extra round trip to the
* server, making it easier for scripts where teh body is not replayable.
*
* @param username
* @param password
* @return String
*/
private static String basicAuth(String username, String password) {
return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
}
}