a9466e6e5565590951b5bc556d5b34a98ce176c0
[aaa.git] / aaa-shiro / impl / src / main / java / org / opendaylight / aaa / authenticator / ODLAuthenticator.java
1 /*
2  * Copyright © 2018 Inocybe Technologies and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.aaa.authenticator;
9
10 import java.nio.charset.StandardCharsets;
11 import java.util.Base64;
12 import javax.inject.Inject;
13 import javax.inject.Singleton;
14 import javax.servlet.http.HttpServletRequest;
15 import org.apache.shiro.SecurityUtils;
16 import org.apache.shiro.ShiroException;
17 import org.apache.shiro.authc.AuthenticationException;
18 import org.apache.shiro.authc.UsernamePasswordToken;
19 import org.apache.shiro.session.Session;
20 import org.apache.shiro.session.UnknownSessionException;
21 import org.apache.shiro.subject.Subject;
22 import org.jolokia.osgi.security.Authenticator;
23 import org.osgi.service.component.annotations.Component;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * AAA hook for <code>odl-jolokia</code> configured w/ <code>org.jolokia.authMode=service-all</code>.
29  */
30 @Singleton
31 @Component(immediate = true)
32 public class ODLAuthenticator implements Authenticator {
33     private static final Logger LOG = LoggerFactory.getLogger(ODLAuthenticator.class);
34
35     @Inject
36     public ODLAuthenticator() {
37         // Exposed for DI
38     }
39
40     @Override
41     public boolean authenticate(final HttpServletRequest httpServletRequest) {
42         final String authorization = httpServletRequest.getHeader("Authorization");
43
44         LOG.trace("Incoming Jolokia authentication attempt: {}", authorization);
45
46         if (authorization == null || !authorization.startsWith("Basic")) {
47             return false;
48         }
49
50         try {
51             final String base64Creds = authorization.substring("Basic".length()).trim();
52             String credentials = new String(Base64.getDecoder().decode(base64Creds), StandardCharsets.UTF_8);
53             final String[] values = credentials.split(":", 2);
54             UsernamePasswordToken upt = new UsernamePasswordToken();
55             upt.setUsername(values[0]);
56             upt.setPassword(values[1].toCharArray());
57
58             try {
59                 return login(upt);
60             } catch (UnknownSessionException e) {
61                 LOG.debug("Couldn't log in {} - logging out and retrying...", upt, e);
62                 logout();
63                 return login(upt);
64             }
65         } catch (ArrayIndexOutOfBoundsException e) {
66             // FIXME: who throws this above and why do we need to catch it? Should this be error or warn?
67             LOG.trace("Formatting issue with basic auth credentials: {}", authorization, e);
68         }
69
70         return false;
71     }
72
73     private static void logout() {
74         final Subject subject = SecurityUtils.getSubject();
75         try {
76             subject.logout();
77             Session session = subject.getSession(false);
78             if (session != null) {
79                 session.stop();
80             }
81         } catch (ShiroException e) {
82             LOG.debug("Couldn't log out {}", subject, e);
83         }
84     }
85
86     private static boolean login(final UsernamePasswordToken upt) {
87         final Subject subject = SecurityUtils.getSubject();
88         try {
89             subject.login(upt);
90         } catch (AuthenticationException e) {
91             LOG.trace("Couldn't authenticate the subject: {}", subject, e);
92             return false;
93         }
94         return true;
95     }
96 }