blob: 7a940d491a55c2ae40300750e725a49fe7919191 [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.jetty;
import java.util.Objects;
import org.apache.jena.atlas.web.AuthScheme;
import org.apache.jena.riot.WebContent;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.security.*;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.security.Password;
/** Helpers for working with Jetty.
* <p>
* <h3>SecurityHandler</h3>
* <pre>
* UserStore userStore = JettyLib.makeUserStore(...);
* SecurityHandler securityHandler = JettyLib.makeSecurityHandler(String pathSpec, String realm, UserStore userStore);
* </pre>
*/
public class JettyLib {
/** Create a Jetty {@link SecurityHandler} for a specific pathSpace, e.g {@code /database}. */
public static SecurityHandler makeSecurityHandlerForPathspec(String pathSpec, String realm, UserStore userStore) {
ConstraintSecurityHandler sh = makeSecurityHandler(realm, userStore);
addPathConstraint(sh, pathSpec);
return sh;
}
// /** Create a Jetty {@link SecurityHandler} for basic authentication. */
// @Deprecated
// public static SecurityHandler makeSecurityHandlerForPathspec(String pathSpec, String realm, UserStore userStore, String role) {
// ConstraintSecurityHandler securityHandler = makeSecurityHandler(realm, userStore, role);
// // Pathspec based.
// addDatasetConstraint(securityHandler, pathSpec);
// return securityHandler;
// }
/**
* Digest requires an extra round trip so it is unfriendly to API
* or scripts that stream.
*/
public final static AuthScheme dftAuthMode = AuthScheme.DIGEST;
/** Current auth mode */
public static AuthScheme authMode = dftAuthMode;
/** Create a Jetty {@link SecurityHandler} for basic authentication.
* See {@linkplain #addPathConstraint(ConstraintSecurityHandler, String)}
* for adding the {@code pathspec} to apply it to.
*/
public static ConstraintSecurityHandler makeSecurityHandler(String realm, UserStore userStore) {
return makeSecurityHandler(realm, userStore, "**", authMode);
}
/** Create a Jetty {@link SecurityHandler} for basic authentication.
* See {@linkplain #addPathConstraint(ConstraintSecurityHandler, String)}
* for adding the {@code pathspec} to apply it to.
*/
public static ConstraintSecurityHandler makeSecurityHandler(String realm, UserStore userStore, AuthScheme authMode) {
return makeSecurityHandler(realm, userStore, "**", authMode);
}
/** Create a Jetty {@link SecurityHandler} for basic authentication.
* See {@linkplain #addPathConstraint(ConstraintSecurityHandler, String)}
* for adding the {@code pathspec} to apply it to.
*/
public static ConstraintSecurityHandler makeSecurityHandler(String realm, UserStore userStore, String role, AuthScheme authMode) {
// role can be "**" for any authenticated user.
Objects.requireNonNull(userStore);
Objects.requireNonNull(role);
if ( authMode == null )
authMode = dftAuthMode;
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
IdentityService identService = new DefaultIdentityService();
securityHandler.setIdentityService(identService);
// ---- HashLoginService
HashLoginService loginService = new HashLoginService(realm);
loginService.setUserStore(userStore);
loginService.setIdentityService(identService);
securityHandler.setLoginService(loginService);
securityHandler.setAuthenticator( authMode == AuthScheme.BASIC ? new BasicAuthenticator() : new DigestAuthenticator() );
if ( realm != null )
securityHandler.setRealmName(realm);
return securityHandler;
}
public static void addPathConstraint(ConstraintSecurityHandler securityHandler, String pathSpec) {
addPathConstraint(securityHandler, pathSpec, "**");
}
public static void addPathConstraint(ConstraintSecurityHandler securityHandler, String pathSpec, String role) {
Objects.requireNonNull(securityHandler);
Objects.requireNonNull(pathSpec);
ConstraintMapping mapping = new ConstraintMapping();
Constraint constraint = new Constraint();
String[] roles = new String[]{role};
constraint.setRoles(roles);
constraint.setName(securityHandler.getAuthenticator().getAuthMethod());
constraint.setAuthenticate(true);
mapping.setConstraint(constraint);
mapping.setPathSpec(pathSpec);
securityHandler.addConstraintMapping(mapping);
}
/**
* Make a {@link UserStore} from a password file.
* {@link PropertyUserStore} for details.
*/
public static UserStore makeUserStore(String passwordFile) {
PropertyUserStore propertyUserStore = new PropertyUserStore();
propertyUserStore.setConfig(passwordFile);
propertyUserStore.setHotReload(true);
try { propertyUserStore.start(); }
catch (Exception ex) { throw new RuntimeException("UserStore", ex); }
return propertyUserStore;
}
/** Make a {@link UserStore} for a single user,password in any role. */
public static UserStore makeUserStore(String user, String password) {
return makeUserStore(user, password, "**");
}
/** Make a {@link UserStore} for a single user,password,role*/
public static UserStore makeUserStore(String user, String password, String role) {
Objects.requireNonNull(user);
Objects.requireNonNull(password);
Objects.requireNonNull(role);
UserStore userStore = new UserStore();
addUser(userStore, user, password, role);
try { userStore.start(); }
catch (Exception ex) { throw new RuntimeException("UserStore", ex); }
return userStore;
}
public static UserStore addUser(UserStore userStore, String user, String password) {
return addUser(userStore, user, password, "**");
}
/** Make a {@link UserStore} for a single user,password,role*/
public static UserStore addUser(UserStore userStore, String user, String password, String role) {
String[] roles = role == null ? null : new String[]{role};
Credential cred = new Password(password);
userStore.addUser(user, cred, roles);
return userStore;
}
/** Add or append a {@link Handler} to a Jetty {@link Server}. */
public static void addHandler(Server server, Handler handler) {
final Handler currentHandler = server.getHandler();
if (currentHandler == null) {
server.setHandler(handler);
} else {
if (currentHandler instanceof HandlerList) {
((HandlerList) currentHandler).addHandler(handler);
} else {
// Singleton handler. Convert to list.
final HandlerList handlerList = new HandlerList();
handlerList.addHandler(currentHandler);
handlerList.addHandler(handler);
server.setHandler(handlerList);
}
}
}
/** Add the RDF MIME Type mappins */
public static void setMimeTypes(ServletContextHandler context) {
MimeTypes mimeTypes = new MimeTypes();
// RDF syntax
mimeTypes.addMimeMapping("nt", WebContent.contentTypeNTriples);
mimeTypes.addMimeMapping("nq", WebContent.contentTypeNQuads);
mimeTypes.addMimeMapping("ttl", WebContent.contentTypeTurtle+";charset=utf-8");
mimeTypes.addMimeMapping("trig", WebContent.contentTypeTriG+";charset=utf-8");
mimeTypes.addMimeMapping("rdf", WebContent.contentTypeRDFXML);
mimeTypes.addMimeMapping("jsonld", WebContent.contentTypeJSONLD);
mimeTypes.addMimeMapping("rj", WebContent.contentTypeRDFJSON);
mimeTypes.addMimeMapping("rt", WebContent.contentTypeRDFThrift);
mimeTypes.addMimeMapping("trdf", WebContent.contentTypeRDFThrift);
// SPARQL syntax
mimeTypes.addMimeMapping("rq", WebContent.contentTypeSPARQLQuery);
mimeTypes.addMimeMapping("ru", WebContent.contentTypeSPARQLUpdate);
// SPARQL Result set
mimeTypes.addMimeMapping("rsj", WebContent.contentTypeResultsJSON);
mimeTypes.addMimeMapping("rsx", WebContent.contentTypeResultsXML);
mimeTypes.addMimeMapping("srt", WebContent.contentTypeResultsThrift);
// Other
mimeTypes.addMimeMapping("txt", WebContent.contentTypeTextPlain);
mimeTypes.addMimeMapping("csv", WebContent.contentTypeTextCSV);
mimeTypes.addMimeMapping("tsv", WebContent.contentTypeTextTSV);
context.setMimeTypes(mimeTypes);
}
}