blob: 4cb01694aee21b6c5282d6e76c4208861dd8cdbd [file] [log] [blame]
Michael Hanl19390652016-01-16 11:01:24 +01001package de.ids_mannheim.korap.web.service.full;
Michael Hanlfb839b92015-09-19 21:32:34 +02002
3import com.sun.jersey.spi.container.ContainerRequest;
4import com.sun.jersey.spi.container.ResourceFilters;
Bodmo3d6bd352017-04-25 11:31:39 +02005
Michael Hanl00b64e02016-05-24 20:24:27 +02006import de.ids_mannheim.korap.config.Attributes;
Michael Hanldaf86602016-05-12 14:31:52 +02007import de.ids_mannheim.korap.config.BeansFactory;
Michael Hanlfb839b92015-09-19 21:32:34 +02008import de.ids_mannheim.korap.exceptions.KustvaktException;
9import de.ids_mannheim.korap.exceptions.StatusCodes;
10import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
11import de.ids_mannheim.korap.security.auth.BasicHttpAuth;
Michael Hanlc2a9f622016-01-28 16:40:06 +010012import de.ids_mannheim.korap.user.*;
Michael Hanlfb839b92015-09-19 21:32:34 +020013import de.ids_mannheim.korap.utils.JsonUtils;
14import de.ids_mannheim.korap.utils.KustvaktLogger;
Michael Hanldaf86602016-05-12 14:31:52 +020015import de.ids_mannheim.korap.utils.ServiceInfo;
Michael Hanlfb839b92015-09-19 21:32:34 +020016import de.ids_mannheim.korap.web.KustvaktServer;
Michael Hanl99cb9632016-06-29 16:24:40 +020017import de.ids_mannheim.korap.web.filter.*;
Michael Hanl482f30d2015-09-25 12:39:46 +020018import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
Bodmo3d6bd352017-04-25 11:31:39 +020019
Michael Hanlfb839b92015-09-19 21:32:34 +020020import org.slf4j.Logger;
21
Bodmo3d6bd352017-04-25 11:31:39 +020022import javax.servlet.http.HttpServletRequest; // FB
Michael Hanlfb839b92015-09-19 21:32:34 +020023import javax.ws.rs.*;
24import javax.ws.rs.core.*;
Bodmo3d6bd352017-04-25 11:31:39 +020025import javax.xml.ws.WebServiceContext; // FB
26import javax.xml.ws.handler.MessageContext; // FB
27import javax.annotation.Resource; // FB
28
Bodmoca3dcfb2017-05-24 16:36:00 +020029import java.util.Arrays;
Michael Hanlfb839b92015-09-19 21:32:34 +020030import java.util.HashMap;
31import java.util.List;
32import java.util.Locale;
33import java.util.Map;
Bodmoca3dcfb2017-05-24 16:36:00 +020034import java.util.ArrayList;
Bodmo3d6bd352017-04-25 11:31:39 +020035import java.util.Iterator; // 07.02.17/FB
Michael Hanlfb839b92015-09-19 21:32:34 +020036
37//import com.sun.xml.internal.messaging.saaj.util.Base64;
38
39/**
40 * @author hanl
41 * @date 24/01/2014
42 */
margarethaecbe72d2017-07-26 17:13:32 +020043@Path("/auth")
Michael Hanlfb839b92015-09-19 21:32:34 +020044@ResourceFilters({ PiwikFilter.class })
45@Produces(MediaType.TEXT_HTML + ";charset=utf-8")
46public class AuthService {
47
Bodmoc125bf12017-06-01 16:23:59 +020048 private static Boolean DEBUG_LOG = true;
Bodmo3d6bd352017-04-25 11:31:39 +020049
Michael Hanlfb839b92015-09-19 21:32:34 +020050 //todo: bootstrap function to transmit certain default configuration settings and examples (example user queries,
51 // default usersettings, etc.)
Michael Hanlfdd9a012015-11-13 15:56:38 +010052 private static Logger jlog = KustvaktLogger.getLogger(AuthService.class);
Michael Hanlfb839b92015-09-19 21:32:34 +020053
54 private AuthenticationManagerIface controller;
Michael Hanl8abaf9e2016-05-23 16:46:35 +020055
Michael Hanlfb839b92015-09-19 21:32:34 +020056 // private SendMail mail;
57
Michael Hanl8abaf9e2016-05-23 16:46:35 +020058 public AuthService () {
Michael Hanldaf86602016-05-12 14:31:52 +020059 this.controller = BeansFactory.getKustvaktContext()
Michael Hanlfb839b92015-09-19 21:32:34 +020060 .getAuthenticationManager();
61 //todo: replace with real property values
62 // this.mail = new SendMail(ExtConfiguration.getMailProperties());
63 }
64
Bodmo3d6bd352017-04-25 11:31:39 +020065
Michael Hanlfb839b92015-09-19 21:32:34 +020066 /**
Michael Hanl8abaf9e2016-05-23 16:46:35 +020067 * represents json string with data. All GUI clients can access
68 * this method to get certain default values
Michael Hanlfb839b92015-09-19 21:32:34 +020069 * --> security checks?
Michael Hanl8abaf9e2016-05-23 16:46:35 +020070 *
Michael Hanlfb839b92015-09-19 21:32:34 +020071 * @return String
72 */
Michael Hanl25aac542016-02-01 18:16:44 +010073 @Deprecated
Michael Hanlfb839b92015-09-19 21:32:34 +020074 @GET
75 @Path("bootstrap")
76 @Produces(MediaType.APPLICATION_JSON)
Michael Hanl8abaf9e2016-05-23 16:46:35 +020077 public Response bootstrap () {
Michael Hanlfb839b92015-09-19 21:32:34 +020078 Map m = new HashMap();
Michael Hanl8abaf9e2016-05-23 16:46:35 +020079 // m.put("settings", new UserSettings().toObjectMap());
Michael Hanldaf86602016-05-12 14:31:52 +020080 m.put("ql", BeansFactory.getKustvaktContext().getConfiguration()
Michael Hanlfb839b92015-09-19 21:32:34 +020081 .getQueryLanguages());
82 m.put("SortTypes", null); // types of sorting that are supported!
Michael Hanldaf86602016-05-12 14:31:52 +020083 m.put("version", ServiceInfo.getInfo().getVersion());
Michael Hanlfb839b92015-09-19 21:32:34 +020084 return Response.ok(JsonUtils.toJSON(m)).build();
85 }
86
Michael Hanl8abaf9e2016-05-23 16:46:35 +020087
Michael Hanl19390652016-01-16 11:01:24 +010088 // fixme: moved to user
Michael Hanlfb839b92015-09-19 21:32:34 +020089 @GET
90 @Path("status")
Michael Hanl99cb9632016-06-29 16:24:40 +020091 @ResourceFilters({ AuthFilter.class, DemoUserFilter.class, BlockingFilter.class })
Michael Hanl8abaf9e2016-05-23 16:46:35 +020092 public Response getStatus (@Context SecurityContext context,
Michael Hanlfb839b92015-09-19 21:32:34 +020093 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
94 @HeaderParam(ContainerRequest.HOST) String host,
95 @Context Locale locale) {
96 TokenContext ctx = (TokenContext) context.getUserPrincipal();
Michael Hanl2c3b0b12016-07-01 18:30:12 +020097 return Response.ok(ctx.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +020098 }
99
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200100
Michael Hanlfb839b92015-09-19 21:32:34 +0200101 @GET
102 @Path("apiToken")
Michael Hanl9be4e422016-07-21 14:13:27 +0200103 //@ResourceFilters({HeaderFilter.class})
Bodmo3d6bd352017-04-25 11:31:39 +0200104 public Response requestAPIToken (
105 @Context HttpHeaders headers,
Michael Hanlfb839b92015-09-19 21:32:34 +0200106 @Context Locale locale,
107 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
108 @HeaderParam(ContainerRequest.HOST) String host,
109 @HeaderParam("referer-url") String referer,
Bodmo3d6bd352017-04-25 11:31:39 +0200110 @QueryParam("scope") String scopes,
111 // @Context WebServiceContext wsContext, // FB
112 @Context SecurityContext secCtx) {
113
Michael Hanlfb839b92015-09-19 21:32:34 +0200114 List<String> auth = headers
115 .getRequestHeader(ContainerRequest.AUTHORIZATION);
116
Michael Hanlfb839b92015-09-19 21:32:34 +0200117 String[] values = BasicHttpAuth.decode(auth.get(0));
118
Bodmo3d6bd352017-04-25 11:31:39 +0200119 if( DEBUG_LOG == true )
120 {
121 System.out.printf("Debug: AuthService.requestAPIToken...:\n");
122 System.out.printf("Debug: auth.size=%d\n", auth.size());
123 System.out.printf("auth.get(0)='%s'\n", auth.get(0));
124 System.out.printf("Debug: values.length=%d\n", values.length);
Bodmo11fb6f82017-06-01 11:39:15 +0200125 /* hide password etc. - FB
126 if( auth.size() > 0 )
Bodmo3d6bd352017-04-25 11:31:39 +0200127 {
128 Iterator it = auth.iterator();
129 while( it.hasNext() )
130 System.out.printf(" header '%s'\n", it.next());
131 }
132 if( values.length > 0 )
133 {
134 for(int i=0; i< values.length; i++)
135 {
136 System.out.printf(" values[%d]='%s'\n", i, values[i]);
137 }
138 }
Bodmo11fb6f82017-06-01 11:39:15 +0200139 */
Bodmo3d6bd352017-04-25 11:31:39 +0200140 MultivaluedMap<String,String> headerMap = headers.getRequestHeaders();
141 if( headerMap != null && headerMap.size() > 0 )
142 {
143 Iterator<String> it = headerMap.keySet().iterator();
144 while( it.hasNext() )
145 {
146 String key = (String)it.next();
147 List<String> vals= headerMap.get(key);
148 System.out.printf("Debug: requestAPIToken: '%s' = '%s'\n", key, vals);
149 }
150
151 }
152 System.out.printf("Debug: requestAPIToken: isSecure = %s.\n", secCtx.isSecure() ? "yes" : "no");
153 } // DEBUG_LOG
Bodmoca3dcfb2017-05-24 16:36:00 +0200154
Michael Hanlfb839b92015-09-19 21:32:34 +0200155 // "Invalid syntax for username and password"
156 if (values == null)
Michael Hanl482f30d2015-09-25 12:39:46 +0200157 throw KustvaktResponseHandler
Michael Hanl99cb9632016-06-29 16:24:40 +0200158 .throwit(StatusCodes.ACCESS_DENIED);
Michael Hanlfb839b92015-09-19 21:32:34 +0200159
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200160 if (values[0].equalsIgnoreCase("null")
161 | values[1].equalsIgnoreCase("null"))
Michael Hanlfb839b92015-09-19 21:32:34 +0200162 // is actual an invalid request
Michael Hanlf1e85e72016-01-21 16:55:45 +0100163 throw KustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
Michael Hanlfb839b92015-09-19 21:32:34 +0200164
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100165 Map<String, Object> attr = new HashMap<>();
Michael Hanl482f30d2015-09-25 12:39:46 +0200166 if (scopes != null && !scopes.isEmpty())
167 attr.put(Attributes.SCOPES, scopes);
Michael Hanlfb839b92015-09-19 21:32:34 +0200168 attr.put(Attributes.HOST, host);
169 attr.put(Attributes.USER_AGENT, agent);
Bodmoca3dcfb2017-05-24 16:36:00 +0200170
Michael Hanlfb839b92015-09-19 21:32:34 +0200171 TokenContext context;
172 try {
Bodmo3d6bd352017-04-25 11:31:39 +0200173 // User user = controller.authenticate(0, values[0], values[1], attr); Implementation by Hanl
174 User user = controller.authenticate(2, values[0], values[1], attr); // Implementation with IdM/LDAP
175 // Userdata data = this.controller.getUserData(user, UserDetails.class); // Implem. by Hanl
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100176 // todo: is this necessary?
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200177 // attr.putAll(data.fields());
Bodmoca3dcfb2017-05-24 16:36:00 +0200178 controller.setAccessAndLocation(user, headers);
Bodmoc125bf12017-06-01 16:23:59 +0200179 if( DEBUG_LOG == true )
180 System.out.printf("Debug: /apiToken/: location=%s, access='%s'.\n", user.locationtoString(), user.accesstoString());
Bodmoca3dcfb2017-05-24 16:36:00 +0200181 attr.put(Attributes.LOCATION, user.getLocation());
182 attr.put(Attributes.CORPUS_ACCESS, user.getCorpusAccess());
Bodmo3d6bd352017-04-25 11:31:39 +0200183 context = controller.createTokenContext(user, attr, Attributes.API_AUTHENTICATION);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200184 }
185 catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200186 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200187 }
188
Michael Hanl2c3b0b12016-07-01 18:30:12 +0200189 return Response.ok(context.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +0200190 }
191
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200192
Michael Hanlfb839b92015-09-19 21:32:34 +0200193 // todo:
194 @Deprecated
195 @GET
196 @Path("refresh")
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200197 public Response refresh (@Context SecurityContext context,
Michael Hanlfb839b92015-09-19 21:32:34 +0200198 @Context Locale locale) {
199 TokenContext ctx = (TokenContext) context.getUserPrincipal();
200 TokenContext newContext;
201
202 // try {
203 // newContext = controller.refresh(ctx);
204 // }catch (KorAPException e) {
205 // KorAPLogger.ERROR_LOGGER.error("Exception encountered!", e);
Michael Hanl482f30d2015-09-25 12:39:46 +0200206 // throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200207 // }
208 // return Response.ok().entity(newContext.getToken()).build();
209 return null;
210 }
211
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200212
Michael Hanlfb839b92015-09-19 21:32:34 +0200213 @GET
214 @Path("sessionToken")
Michael Hanl9be4e422016-07-21 14:13:27 +0200215 //@ResourceFilters({HeaderFilter.class})
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200216 public Response requestSession (@Context HttpHeaders headers,
Michael Hanlfb839b92015-09-19 21:32:34 +0200217 @Context Locale locale,
218 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
219 @HeaderParam(ContainerRequest.HOST) String host) {
220 List<String> auth = headers
221 .getRequestHeader(ContainerRequest.AUTHORIZATION);
222
Michael Hanlfb839b92015-09-19 21:32:34 +0200223 String[] values = BasicHttpAuth.decode(auth.get(0));
224 // authentication = StringUtils.stripTokenType(authentication);
225 // String[] values = new String(
226 // DatatypeConverter.parseBase64Binary(authentication)).split(":");
227 // String[] values = Base64.base64Decode(authentication).split(":");
228
229 // "Invalid syntax for username and password"
230 if (values == null)
Michael Hanl482f30d2015-09-25 12:39:46 +0200231 throw KustvaktResponseHandler
Michael Hanl99cb9632016-06-29 16:24:40 +0200232 .throwit(StatusCodes.BAD_CREDENTIALS);
Michael Hanlfb839b92015-09-19 21:32:34 +0200233
Bodmo3d6bd352017-04-25 11:31:39 +0200234 // Implementation Hanl mit '|'. 16.02.17/FB
235 //if (values[0].equalsIgnoreCase("null")
236 // | values[1].equalsIgnoreCase("null"))
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200237 if (values[0].equalsIgnoreCase("null")
Bodmo3d6bd352017-04-25 11:31:39 +0200238 || values[1].equalsIgnoreCase("null"))
Michael Hanlf1e85e72016-01-21 16:55:45 +0100239 throw KustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
Michael Hanlfb839b92015-09-19 21:32:34 +0200240
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100241 Map<String, Object> attr = new HashMap<>();
Michael Hanlfb839b92015-09-19 21:32:34 +0200242 attr.put(Attributes.HOST, host);
243 attr.put(Attributes.USER_AGENT, agent);
244 TokenContext context;
245 try {
246 User user = controller.authenticate(0, values[0], values[1], attr);
247 context = controller.createTokenContext(user, attr,
248 Attributes.SESSION_AUTHENTICATION);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200249 }
250 catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200251 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200252 }
Michael Hanl2c3b0b12016-07-01 18:30:12 +0200253 return Response.ok().entity(context.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +0200254 }
255
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200256
Michael Hanlfb839b92015-09-19 21:32:34 +0200257 // fixme: security issues: setup shibboleth compatible authentication system
258 // todo: will be purged with token authentication --> shib is client side
259 @POST
Michael Hanl6bfe4002016-07-02 11:43:09 +0200260 @Consumes(MediaType.APPLICATION_JSON)
Michael Hanlfb839b92015-09-19 21:32:34 +0200261 @Produces("application/json")
262 @Path("shibboleth")
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200263 public Response loginshib (@Context HttpHeaders headers,
Michael Hanlfb839b92015-09-19 21:32:34 +0200264 @Context Locale locale,
265 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
266 @HeaderParam(ContainerRequest.HOST) String host,
267 @QueryParam("client_id") String client_id) {
268
269 // the shibfilter decrypted the values
270 // define default provider for returned access token strategy?!
271
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100272 Map<String, Object> attr = new HashMap<>();
Michael Hanlfb839b92015-09-19 21:32:34 +0200273 attr.put(Attributes.HOST, host);
274 attr.put(Attributes.USER_AGENT, agent);
275
276 TokenContext context;
277
278 try {
279 // todo: distinguish type KorAP/Shibusers
280 User user = controller.authenticate(1, null, null, attr);
281 context = controller.createTokenContext(user, attr, null);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200282 }
283 catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200284 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200285 }
Michael Hanl2c3b0b12016-07-01 18:30:12 +0200286 return Response.ok().entity(context.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +0200287 }
Michael Hanl19390652016-01-16 11:01:24 +0100288
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200289
Michael Hanl19390652016-01-16 11:01:24 +0100290 //fixme: moved from userservice
291 @GET
292 @Path("logout")
Michael Hanl99cb9632016-06-29 16:24:40 +0200293 @ResourceFilters({ AuthFilter.class, DemoUserFilter.class, PiwikFilter.class })
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200294 public Response logout (@Context SecurityContext ctx, @Context Locale locale) {
Michael Hanl19390652016-01-16 11:01:24 +0100295 TokenContext context = (TokenContext) ctx.getUserPrincipal();
296 try {
297 controller.logout(context);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200298 }
299 catch (KustvaktException e) {
Michael Hanl00ef5462016-06-06 17:39:59 +0200300 jlog.error("Logout Exception: {}", e.string());
Michael Hanl19390652016-01-16 11:01:24 +0100301 throw KustvaktResponseHandler.throwit(e);
302 }
303 return Response.ok().build();
304 }
305
Michael Hanlfb839b92015-09-19 21:32:34 +0200306}