blob: ee63565e87a9be93bd811a95ecfdd9e8764ecbb8 [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
Michael Hanlfb839b92015-09-19 21:32:34 +020029import java.util.HashMap;
30import java.util.List;
31import java.util.Locale;
32import java.util.Map;
Bodmo3d6bd352017-04-25 11:31:39 +020033import java.util.Iterator; // 07.02.17/FB
Michael Hanlfb839b92015-09-19 21:32:34 +020034
35//import com.sun.xml.internal.messaging.saaj.util.Base64;
36
37/**
38 * @author hanl
39 * @date 24/01/2014
40 */
41@Path(KustvaktServer.API_VERSION + "/auth")
42@ResourceFilters({ PiwikFilter.class })
43@Produces(MediaType.TEXT_HTML + ";charset=utf-8")
44public class AuthService {
45
Bodmo3d6bd352017-04-25 11:31:39 +020046 private static Boolean DEBUG_LOG = false;
47
Michael Hanlfb839b92015-09-19 21:32:34 +020048 //todo: bootstrap function to transmit certain default configuration settings and examples (example user queries,
49 // default usersettings, etc.)
Michael Hanlfdd9a012015-11-13 15:56:38 +010050 private static Logger jlog = KustvaktLogger.getLogger(AuthService.class);
Michael Hanlfb839b92015-09-19 21:32:34 +020051
52 private AuthenticationManagerIface controller;
Michael Hanl8abaf9e2016-05-23 16:46:35 +020053
Michael Hanlfb839b92015-09-19 21:32:34 +020054 // private SendMail mail;
55
Michael Hanl8abaf9e2016-05-23 16:46:35 +020056 public AuthService () {
Michael Hanldaf86602016-05-12 14:31:52 +020057 this.controller = BeansFactory.getKustvaktContext()
Michael Hanlfb839b92015-09-19 21:32:34 +020058 .getAuthenticationManager();
59 //todo: replace with real property values
60 // this.mail = new SendMail(ExtConfiguration.getMailProperties());
61 }
62
Bodmo3d6bd352017-04-25 11:31:39 +020063
Michael Hanlfb839b92015-09-19 21:32:34 +020064 /**
Michael Hanl8abaf9e2016-05-23 16:46:35 +020065 * represents json string with data. All GUI clients can access
66 * this method to get certain default values
Michael Hanlfb839b92015-09-19 21:32:34 +020067 * --> security checks?
Michael Hanl8abaf9e2016-05-23 16:46:35 +020068 *
Michael Hanlfb839b92015-09-19 21:32:34 +020069 * @return String
70 */
Michael Hanl25aac542016-02-01 18:16:44 +010071 @Deprecated
Michael Hanlfb839b92015-09-19 21:32:34 +020072 @GET
73 @Path("bootstrap")
74 @Produces(MediaType.APPLICATION_JSON)
Michael Hanl8abaf9e2016-05-23 16:46:35 +020075 public Response bootstrap () {
Michael Hanlfb839b92015-09-19 21:32:34 +020076 Map m = new HashMap();
Michael Hanl8abaf9e2016-05-23 16:46:35 +020077 // m.put("settings", new UserSettings().toObjectMap());
Michael Hanldaf86602016-05-12 14:31:52 +020078 m.put("ql", BeansFactory.getKustvaktContext().getConfiguration()
Michael Hanlfb839b92015-09-19 21:32:34 +020079 .getQueryLanguages());
80 m.put("SortTypes", null); // types of sorting that are supported!
Michael Hanldaf86602016-05-12 14:31:52 +020081 m.put("version", ServiceInfo.getInfo().getVersion());
Michael Hanlfb839b92015-09-19 21:32:34 +020082 return Response.ok(JsonUtils.toJSON(m)).build();
83 }
84
Michael Hanl8abaf9e2016-05-23 16:46:35 +020085
Michael Hanl19390652016-01-16 11:01:24 +010086 // fixme: moved to user
Michael Hanlfb839b92015-09-19 21:32:34 +020087 @GET
88 @Path("status")
Michael Hanl99cb9632016-06-29 16:24:40 +020089 @ResourceFilters({ AuthFilter.class, DemoUserFilter.class, BlockingFilter.class })
Michael Hanl8abaf9e2016-05-23 16:46:35 +020090 public Response getStatus (@Context SecurityContext context,
Michael Hanlfb839b92015-09-19 21:32:34 +020091 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
92 @HeaderParam(ContainerRequest.HOST) String host,
93 @Context Locale locale) {
94 TokenContext ctx = (TokenContext) context.getUserPrincipal();
Michael Hanl2c3b0b12016-07-01 18:30:12 +020095 return Response.ok(ctx.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +020096 }
97
Michael Hanl8abaf9e2016-05-23 16:46:35 +020098
Michael Hanlfb839b92015-09-19 21:32:34 +020099 @GET
100 @Path("apiToken")
Michael Hanl9be4e422016-07-21 14:13:27 +0200101 //@ResourceFilters({HeaderFilter.class})
Bodmo3d6bd352017-04-25 11:31:39 +0200102 public Response requestAPIToken (
103 @Context HttpHeaders headers,
Michael Hanlfb839b92015-09-19 21:32:34 +0200104 @Context Locale locale,
105 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
106 @HeaderParam(ContainerRequest.HOST) String host,
107 @HeaderParam("referer-url") String referer,
Bodmo3d6bd352017-04-25 11:31:39 +0200108 @QueryParam("scope") String scopes,
109 // @Context WebServiceContext wsContext, // FB
110 @Context SecurityContext secCtx) {
111
Michael Hanlfb839b92015-09-19 21:32:34 +0200112 List<String> auth = headers
113 .getRequestHeader(ContainerRequest.AUTHORIZATION);
114
Michael Hanlfb839b92015-09-19 21:32:34 +0200115 String[] values = BasicHttpAuth.decode(auth.get(0));
116
Bodmo3d6bd352017-04-25 11:31:39 +0200117 if( DEBUG_LOG == true )
118 {
119 System.out.printf("Debug: AuthService.requestAPIToken...:\n");
120 System.out.printf("Debug: auth.size=%d\n", auth.size());
121 System.out.printf("auth.get(0)='%s'\n", auth.get(0));
122 System.out.printf("Debug: values.length=%d\n", values.length);
123 if( auth.size() > 0 )
124 {
125 Iterator it = auth.iterator();
126 while( it.hasNext() )
127 System.out.printf(" header '%s'\n", it.next());
128 }
129 if( values.length > 0 )
130 {
131 for(int i=0; i< values.length; i++)
132 {
133 System.out.printf(" values[%d]='%s'\n", i, values[i]);
134 }
135 }
136
137 MultivaluedMap<String,String> headerMap = headers.getRequestHeaders();
138 if( headerMap != null && headerMap.size() > 0 )
139 {
140 Iterator<String> it = headerMap.keySet().iterator();
141 while( it.hasNext() )
142 {
143 String key = (String)it.next();
144 List<String> vals= headerMap.get(key);
145 System.out.printf("Debug: requestAPIToken: '%s' = '%s'\n", key, vals);
146 }
147
148 }
149 System.out.printf("Debug: requestAPIToken: isSecure = %s.\n", secCtx.isSecure() ? "yes" : "no");
150 } // DEBUG_LOG
151
Michael Hanlfb839b92015-09-19 21:32:34 +0200152 // "Invalid syntax for username and password"
153 if (values == null)
Michael Hanl482f30d2015-09-25 12:39:46 +0200154 throw KustvaktResponseHandler
Michael Hanl99cb9632016-06-29 16:24:40 +0200155 .throwit(StatusCodes.ACCESS_DENIED);
Michael Hanlfb839b92015-09-19 21:32:34 +0200156
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200157 if (values[0].equalsIgnoreCase("null")
158 | values[1].equalsIgnoreCase("null"))
Michael Hanlfb839b92015-09-19 21:32:34 +0200159 // is actual an invalid request
Michael Hanlf1e85e72016-01-21 16:55:45 +0100160 throw KustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
Michael Hanlfb839b92015-09-19 21:32:34 +0200161
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100162 Map<String, Object> attr = new HashMap<>();
Michael Hanl482f30d2015-09-25 12:39:46 +0200163 if (scopes != null && !scopes.isEmpty())
164 attr.put(Attributes.SCOPES, scopes);
Michael Hanlfb839b92015-09-19 21:32:34 +0200165 attr.put(Attributes.HOST, host);
166 attr.put(Attributes.USER_AGENT, agent);
167 TokenContext context;
168 try {
Bodmo3d6bd352017-04-25 11:31:39 +0200169 // User user = controller.authenticate(0, values[0], values[1], attr); Implementation by Hanl
170 User user = controller.authenticate(2, values[0], values[1], attr); // Implementation with IdM/LDAP
171 // Userdata data = this.controller.getUserData(user, UserDetails.class); // Implem. by Hanl
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100172 // todo: is this necessary?
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200173 // attr.putAll(data.fields());
Bodmo3d6bd352017-04-25 11:31:39 +0200174 context = controller.createTokenContext(user, attr, Attributes.API_AUTHENTICATION);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200175 }
176 catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200177 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200178 }
179
Michael Hanl2c3b0b12016-07-01 18:30:12 +0200180 return Response.ok(context.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +0200181 }
182
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200183
Michael Hanlfb839b92015-09-19 21:32:34 +0200184 // todo:
185 @Deprecated
186 @GET
187 @Path("refresh")
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200188 public Response refresh (@Context SecurityContext context,
Michael Hanlfb839b92015-09-19 21:32:34 +0200189 @Context Locale locale) {
190 TokenContext ctx = (TokenContext) context.getUserPrincipal();
191 TokenContext newContext;
192
193 // try {
194 // newContext = controller.refresh(ctx);
195 // }catch (KorAPException e) {
196 // KorAPLogger.ERROR_LOGGER.error("Exception encountered!", e);
Michael Hanl482f30d2015-09-25 12:39:46 +0200197 // throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200198 // }
199 // return Response.ok().entity(newContext.getToken()).build();
200 return null;
201 }
202
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200203
Michael Hanlfb839b92015-09-19 21:32:34 +0200204 @GET
205 @Path("sessionToken")
Michael Hanl9be4e422016-07-21 14:13:27 +0200206 //@ResourceFilters({HeaderFilter.class})
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200207 public Response requestSession (@Context HttpHeaders headers,
Michael Hanlfb839b92015-09-19 21:32:34 +0200208 @Context Locale locale,
209 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
210 @HeaderParam(ContainerRequest.HOST) String host) {
211 List<String> auth = headers
212 .getRequestHeader(ContainerRequest.AUTHORIZATION);
213
Michael Hanlfb839b92015-09-19 21:32:34 +0200214 String[] values = BasicHttpAuth.decode(auth.get(0));
215 // authentication = StringUtils.stripTokenType(authentication);
216 // String[] values = new String(
217 // DatatypeConverter.parseBase64Binary(authentication)).split(":");
218 // String[] values = Base64.base64Decode(authentication).split(":");
219
220 // "Invalid syntax for username and password"
221 if (values == null)
Michael Hanl482f30d2015-09-25 12:39:46 +0200222 throw KustvaktResponseHandler
Michael Hanl99cb9632016-06-29 16:24:40 +0200223 .throwit(StatusCodes.BAD_CREDENTIALS);
Michael Hanlfb839b92015-09-19 21:32:34 +0200224
Bodmo3d6bd352017-04-25 11:31:39 +0200225 // Implementation Hanl mit '|'. 16.02.17/FB
226 //if (values[0].equalsIgnoreCase("null")
227 // | values[1].equalsIgnoreCase("null"))
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200228 if (values[0].equalsIgnoreCase("null")
Bodmo3d6bd352017-04-25 11:31:39 +0200229 || values[1].equalsIgnoreCase("null"))
Michael Hanlf1e85e72016-01-21 16:55:45 +0100230 throw KustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
Michael Hanlfb839b92015-09-19 21:32:34 +0200231
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100232 Map<String, Object> attr = new HashMap<>();
Michael Hanlfb839b92015-09-19 21:32:34 +0200233 attr.put(Attributes.HOST, host);
234 attr.put(Attributes.USER_AGENT, agent);
235 TokenContext context;
236 try {
237 User user = controller.authenticate(0, values[0], values[1], attr);
238 context = controller.createTokenContext(user, attr,
239 Attributes.SESSION_AUTHENTICATION);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200240 }
241 catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200242 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200243 }
Michael Hanl2c3b0b12016-07-01 18:30:12 +0200244 return Response.ok().entity(context.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +0200245 }
246
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200247
Michael Hanlfb839b92015-09-19 21:32:34 +0200248 // fixme: security issues: setup shibboleth compatible authentication system
249 // todo: will be purged with token authentication --> shib is client side
250 @POST
Michael Hanl6bfe4002016-07-02 11:43:09 +0200251 @Consumes(MediaType.APPLICATION_JSON)
Michael Hanlfb839b92015-09-19 21:32:34 +0200252 @Produces("application/json")
253 @Path("shibboleth")
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200254 public Response loginshib (@Context HttpHeaders headers,
Michael Hanlfb839b92015-09-19 21:32:34 +0200255 @Context Locale locale,
256 @HeaderParam(ContainerRequest.USER_AGENT) String agent,
257 @HeaderParam(ContainerRequest.HOST) String host,
258 @QueryParam("client_id") String client_id) {
259
260 // the shibfilter decrypted the values
261 // define default provider for returned access token strategy?!
262
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100263 Map<String, Object> attr = new HashMap<>();
Michael Hanlfb839b92015-09-19 21:32:34 +0200264 attr.put(Attributes.HOST, host);
265 attr.put(Attributes.USER_AGENT, agent);
266
267 TokenContext context;
268
269 try {
270 // todo: distinguish type KorAP/Shibusers
271 User user = controller.authenticate(1, null, null, attr);
272 context = controller.createTokenContext(user, attr, null);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200273 }
274 catch (KustvaktException e) {
Michael Hanl482f30d2015-09-25 12:39:46 +0200275 throw KustvaktResponseHandler.throwit(e);
Michael Hanlfb839b92015-09-19 21:32:34 +0200276 }
Michael Hanl2c3b0b12016-07-01 18:30:12 +0200277 return Response.ok().entity(context.toJson()).build();
Michael Hanlfb839b92015-09-19 21:32:34 +0200278 }
Michael Hanl19390652016-01-16 11:01:24 +0100279
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200280
Michael Hanl19390652016-01-16 11:01:24 +0100281 //fixme: moved from userservice
282 @GET
283 @Path("logout")
Michael Hanl99cb9632016-06-29 16:24:40 +0200284 @ResourceFilters({ AuthFilter.class, DemoUserFilter.class, PiwikFilter.class })
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200285 public Response logout (@Context SecurityContext ctx, @Context Locale locale) {
Michael Hanl19390652016-01-16 11:01:24 +0100286 TokenContext context = (TokenContext) ctx.getUserPrincipal();
287 try {
288 controller.logout(context);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200289 }
290 catch (KustvaktException e) {
Michael Hanl00ef5462016-06-06 17:39:59 +0200291 jlog.error("Logout Exception: {}", e.string());
Michael Hanl19390652016-01-16 11:01:24 +0100292 throw KustvaktResponseHandler.throwit(e);
293 }
294 return Response.ok().build();
295 }
296
Michael Hanlfb839b92015-09-19 21:32:34 +0200297}