blob: 99ae42cf13fec54805e4619c06cc7b2d957ed489 [file] [log] [blame]
package de.ids_mannheim.korap.web.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import de.ids_mannheim.korap.authentication.AuthenticationManager;
import de.ids_mannheim.korap.authentication.http.AuthorizationData;
import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
import de.ids_mannheim.korap.config.Attributes;
import de.ids_mannheim.korap.config.BeansFactory;
import de.ids_mannheim.korap.constant.AuthenticationMethod;
import de.ids_mannheim.korap.constant.AuthenticationScheme;
import de.ids_mannheim.korap.constant.TokenType;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.user.User;
import de.ids_mannheim.korap.utils.JsonUtils;
import de.ids_mannheim.korap.utils.ServiceInfo;
import de.ids_mannheim.korap.web.KustvaktResponseHandler;
import de.ids_mannheim.korap.web.filter.APIVersionFilter;
import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
import de.ids_mannheim.korap.web.filter.BlockingFilter;
import de.ids_mannheim.korap.web.filter.DemoUserFilter;
import de.ids_mannheim.korap.web.utils.ResourceFilters;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;
// import com.sun.xml.internal.messaging.saaj.util.Base64;
/**
* @author hanl
* @date 24/01/2014
*
* @author margaretha
* @last-update 01/07/2019
*
* - added user authentication time in token context
* - added api version filter
* - changed the response media-type
*/
@Controller
@Path("/{version}/auth")
@ResourceFilters({ APIVersionFilter.class})
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public class AuthenticationController {
@Autowired
private KustvaktResponseHandler kustvaktResponseHandler;
@Autowired
private HttpAuthorizationHandler authorizationHandler;
private static Boolean DEBUG_LOG = true;
//todo: bootstrap function to transmit certain default configuration settings and examples (example user queries,
// default usersettings, etc.)
private static Logger jlog = LogManager
.getLogger(AuthenticationController.class);
@Autowired
private AuthenticationManager controller;
// private SendMail mail;
/**
* represents json string with data. All GUI clients can access
* this method to get certain default values
* --> security checks?
*
* @return String
*/
@Deprecated
@GET
@Path("bootstrap")
@Produces(MediaType.APPLICATION_JSON)
public Response bootstrap () {
Map m = new HashMap();
// m.put("settings", new UserSettings().toObjectMap());
m.put("ql", BeansFactory.getKustvaktContext().getConfiguration()
.getQueryLanguages());
m.put("SortTypes", null); // types of sorting that are supported!
m.put("version", ServiceInfo.getInfo().getVersion());
try {
return Response.ok(JsonUtils.toJSON(m)).build();
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
}
}
// fixme: moved to user
@GET
@Path("status")
@ResourceFilters({ AuthenticationFilter.class, DemoUserFilter.class,
BlockingFilter.class })
public Response getStatus (@Context SecurityContext context,
@HeaderParam(ContainerRequest.USER_AGENT) String agent,
@HeaderParam(ContainerRequest.HOST) String host,
@Context Locale locale) {
TokenContext ctx = (TokenContext) context.getUserPrincipal();
try {
return Response.ok(ctx.toJson()).build();
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
}
}
// EM: testing using spring security authentication manager
// @Deprecated
// @GET
// @Path("ldap/token")
// public Response requestToken (@Context HttpHeaders headers,
// @Context Locale locale,
// @HeaderParam(ContainerRequest.USER_AGENT) String agent,
// @HeaderParam(ContainerRequest.HOST) String host,
// @HeaderParam("referer-url") String referer,
// @QueryParam("scope") String scopes,
// // @Context WebServiceContext wsContext, // FB
// @Context SecurityContext securityContext) {
//
// Map<String, Object> attr = new HashMap<>();
// if (scopes != null && !scopes.isEmpty())
// attr.put(Attributes.SCOPES, scopes);
// attr.put(Attributes.HOST, host);
// attr.put(Attributes.USER_AGENT, agent);
//
// User user = new KorAPUser();
// user.setUsername(securityContext.getUserPrincipal().getName());
// controller.setAccessAndLocation(user, headers);
// if (DEBUG_LOG == true) System.out.printf(
// "Debug: /token/: location=%s, access='%s'.\n",
// user.locationtoString(), user.accesstoString());
// attr.put(Attributes.LOCATION, user.getLocation());
// attr.put(Attributes.CORPUS_ACCESS, user.getCorpusAccess());
//
// try {
// TokenContext context = controller.createTokenContext(user, attr,
// TokenType.API);
// return Response.ok(context.toJson()).build();
// }
// catch (KustvaktException e) {
// throw kustvaktResponseHandler.throwit(e);
// }
// }
@Deprecated
@GET
@Path("apiToken")
//@ResourceFilters({HeaderFilter.class})
public Response requestAPIToken (@Context HttpHeaders headers,
@Context Locale locale,
@HeaderParam(ContainerRequest.USER_AGENT) String agent,
@HeaderParam(ContainerRequest.HOST) String host,
@HeaderParam("referer-url") String referer,
@QueryParam("scope") String scopes,
// @Context WebServiceContext wsContext, // FB
@Context SecurityContext secCtx) {
if (DEBUG_LOG == true) {
String warning = "Access to API token (JWT) web service";
List<String> auth = headers
.getRequestHeader(ContainerRequest.AUTHORIZATION);
if (auth != null && !auth.isEmpty()) {
try {
AuthorizationData authorizationData = authorizationHandler
.parseAuthorizationHeaderValue(auth.get(0));
if (authorizationData.getAuthenticationScheme()
.equals(AuthenticationScheme.BASIC)) {
authorizationData = authorizationHandler
.parseBasicToken(authorizationData);
jlog.warn(warning + " with username:"
+ authorizationData.getUsername());
}
}
catch (KustvaktException e) {}
}
else {
jlog.warn(warning);
}
}
throw kustvaktResponseHandler.throwit(new KustvaktException(
StatusCodes.DEPRECATED,
"API token is no longer supported. Please use OAuth2 procedure instead."));
}
// List<String> auth =
// headers.getRequestHeader(ContainerRequest.AUTHORIZATION);
// if (auth == null || auth.isEmpty()) {
// throw kustvaktResponseHandler
// .throwit(new KustvaktException(StatusCodes.MISSING_PARAMETER,
// "Authorization header is missing.",
// "Authorization header"));
// }
//
// AuthorizationData authorizationData;
// try {
// authorizationData = authorizationHandler.
// parseAuthorizationHeaderValue(auth.get(0));
// if (authorizationData.getAuthenticationScheme().equals(AuthenticationScheme.BASIC)){
// authorizationData = authorizationHandler.parseBasicToken(authorizationData);
// }
// else {
// // EM: throw exception that auth scheme is not supported?
// }
//
// }
// catch (KustvaktException e) {
// throw kustvaktResponseHandler.throwit(e);
// }
//
// if (DEBUG_LOG == true) {
// System.out.printf("Debug: AuthService.requestAPIToken...:\n");
// System.out.printf("Debug: auth.size=%d\n", auth.size());
// System.out.printf("auth.get(0)='%s'\n", auth.get(0));
// /* hide password etc. - FB
// if( auth.size() > 0 )
// {
// Iterator it = auth.iterator();
// while( it.hasNext() )
// System.out.printf(" header '%s'\n", it.next());
// }
// if( values.length > 0 )
// {
// for(int i=0; i< values.length; i++)
// {
// System.out.printf(" values[%d]='%s'\n", i, values[i]);
// }
// }
// */
// MultivaluedMap<String, String> headerMap =
// headers.getRequestHeaders();
// if (headerMap != null && headerMap.size() > 0) {
// Iterator<String> it = headerMap.keySet().iterator();
// while (it.hasNext()) {
// String key = (String) it.next();
// List<String> vals = headerMap.get(key);
//// System.out.printf("Debug: requestAPIToken: '%s' = '%s'\n",
//// key, vals);
// }
//
// }
//// System.out.printf("Debug: requestAPIToken: isSecure = %s.\n",
//// secCtx.isSecure() ? "yes" : "no");
// } // DEBUG_LOG
//
// if (authorizationData.getUsername() == null ||
// authorizationData.getUsername().isEmpty() ||
// authorizationData.getPassword()== null ||
// authorizationData.getPassword().isEmpty())
// // is actual an invalid request
// throw kustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
//
// Map<String, Object> attr = new HashMap<>();
// if (scopes != null && !scopes.isEmpty())
// attr.put(Attributes.SCOPE, scopes);
// attr.put(Attributes.HOST, host);
// attr.put(Attributes.USER_AGENT, agent);
//
// TokenContext context;
// try {
// // User user = controller.authenticate(0, values[0], values[1], attr); Implementation by Hanl
// User user = controller.authenticate(AuthenticationMethod.LDAP,
// authorizationData.getUsername(), authorizationData.getPassword(), attr); // Implementation with IdM/LDAP
// // Userdata data = this.controller.getUserData(user, UserDetails.class); // Implem. by Hanl
// // todo: is this necessary?
// // attr.putAll(data.fields());
//
// // EM: add authentication time
// Date authenticationTime = TimeUtils.getNow().toDate();
// attr.put(Attributes.AUTHENTICATION_TIME, authenticationTime);
// // -- EM
//
// controller.setAccessAndLocation(user, headers);
// if (DEBUG_LOG == true) System.out.printf(
// "Debug: /apiToken/: location=%s, access='%s'.\n",
// user.locationtoString(), user.accesstoString());
// attr.put(Attributes.LOCATION, user.getLocation());
// attr.put(Attributes.CORPUS_ACCESS, user.getCorpusAccess());
// context = controller.createTokenContext(user, attr,
// TokenType.API);
//// context = controller.createTokenContext(user, attr,
//// Attributes.API_AUTHENTICATION);
// }
// catch (KustvaktException e) {
// throw kustvaktResponseHandler.throwit(e);
// }
//
// try {
// return Response.ok(context.toJson()).build();
// }
// catch (KustvaktException e) {
// throw kustvaktResponseHandler.throwit(e);
// }
// }
// todo:
@Deprecated
@GET
@Path("refresh")
public Response refresh (@Context SecurityContext context,
@Context Locale locale) {
TokenContext ctx = (TokenContext) context.getUserPrincipal();
TokenContext newContext;
// try {
// newContext = controller.refresh(ctx);
// }catch (KorAPException e) {
// KorAPLogger.ERROR_LOGGER.error("Exception encountered!", e);
// throw KustvaktResponseHandler.throwit(e);
// }
// return Response.ok().entity(newContext.getToken()).build();
return null;
}
@GET
@Path("sessionToken")
//@ResourceFilters({HeaderFilter.class})
public Response requestSession (@Context HttpHeaders headers,
@Context Locale locale,
@HeaderParam(ContainerRequest.USER_AGENT) String agent,
@HeaderParam(ContainerRequest.HOST) String host) {
List<String> auth = headers
.getRequestHeader(ContainerRequest.AUTHORIZATION);
AuthorizationData authorizationData;
try {
authorizationData = authorizationHandler
.parseAuthorizationHeaderValue(auth.get(0));
authorizationData = authorizationHandler
.parseBasicToken(authorizationData);
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
}
// Implementation Hanl mit '|'. 16.02.17/FB
//if (values[0].equalsIgnoreCase("null")
// | values[1].equalsIgnoreCase("null"))
if (authorizationData.getUsername() == null
|| authorizationData.getUsername().isEmpty()
|| authorizationData.getPassword() == null
|| authorizationData.getPassword().isEmpty())
// is actual an invalid request
throw kustvaktResponseHandler.throwit(StatusCodes.INVALID_REQUEST);
Map<String, Object> attr = new HashMap<>();
attr.put(Attributes.HOST, host);
attr.put(Attributes.USER_AGENT, agent);
TokenContext context;
String contextJson;
try {
//EM: authentication scheme default
User user = controller.authenticate(AuthenticationMethod.DATABASE,
authorizationData.getUsername(),
authorizationData.getPassword(), attr);
context = controller.createTokenContext(user, attr,
TokenType.SESSION);
// context = controller.createTokenContext(user, attr,
// Attributes.SESSION_AUTHENTICATION);
contextJson = context.toJson();
jlog.debug(contextJson);
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
}
return Response.ok().entity(contextJson).build();
}
// fixme: security issues: setup shibboleth compatible authentication system
// todo: will be purged with token authentication --> shib is client side
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces("application/json")
@Path("shibboleth")
public Response loginshib (@Context HttpHeaders headers,
@Context Locale locale,
@HeaderParam(ContainerRequest.USER_AGENT) String agent,
@HeaderParam(ContainerRequest.HOST) String host,
@QueryParam("client_id") String client_id) {
// the shibfilter decrypted the values
// define default provider for returned access token strategy?!
Map<String, Object> attr = new HashMap<>();
attr.put(Attributes.HOST, host);
attr.put(Attributes.USER_AGENT, agent);
TokenContext context;
try {
// todo: distinguish type KorAP/Shibusers
User user = controller.authenticate(AuthenticationMethod.SHIBBOLETH,
null, null, attr);
context = controller.createTokenContext(user, attr, null);
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
}
try {
return Response.ok().entity(context.toJson()).build();
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
}
}
}