2 * Copyright (c) 2015 - 2017 Brocade Communications Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.aaa.shiro.realm;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.base.Strings;
13 import java.util.List;
15 import org.apache.shiro.authc.AuthenticationException;
16 import org.apache.shiro.authc.AuthenticationInfo;
17 import org.apache.shiro.authc.AuthenticationToken;
18 import org.apache.shiro.authc.SimpleAuthenticationInfo;
19 import org.apache.shiro.authz.AuthorizationInfo;
20 import org.apache.shiro.authz.SimpleAuthorizationInfo;
21 import org.apache.shiro.realm.AuthorizingRealm;
22 import org.apache.shiro.subject.PrincipalCollection;
23 import org.opendaylight.aaa.api.Authentication;
24 import org.opendaylight.aaa.api.AuthenticationService;
25 import org.opendaylight.aaa.api.TokenAuth;
26 import org.opendaylight.aaa.api.TokenStore;
27 import org.opendaylight.aaa.api.shiro.principal.ODLPrincipal;
28 import org.opendaylight.aaa.shiro.principal.ODLPrincipalImpl;
29 import org.opendaylight.aaa.shiro.realm.util.TokenUtils;
30 import org.opendaylight.aaa.shiro.realm.util.http.header.HeaderUtils;
31 import org.opendaylight.aaa.shiro.web.env.ThreadLocals;
32 import org.opendaylight.aaa.tokenauthrealm.auth.TokenAuthenticators;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
37 * TokenAuthRealm is an adapter between the AAA shiro subsystem and the existing {@code TokenAuth} mechanisms. Thus, one
38 * can enable use of {@code IDMStore} and {@code IDMMDSALStore}.
40 public class TokenAuthRealm extends AuthorizingRealm {
41 private static final Logger LOG = LoggerFactory.getLogger(TokenAuthRealm.class);
43 private final AuthenticationService authenticationService;
44 private final TokenStore tokenStore;
45 private final TokenAuthenticators tokenAuthenticators;
47 public TokenAuthRealm() {
48 authenticationService = requireNonNull(ThreadLocals.AUTH_SETVICE_TL.get());
49 tokenStore = ThreadLocals.TOKEN_STORE_TL.get();
50 tokenAuthenticators = requireNonNull(ThreadLocals.TOKEN_AUTHENICATORS_TL.get());
51 super.setName("TokenAuthRealm");
58 * Roles are derived from {@code TokenAuth.authenticate()}. Shiro roles are identical to existing IDM roles.
61 protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
62 final var primaryPrincipal = principalCollection.getPrimaryPrincipal();
63 if (primaryPrincipal instanceof ODLPrincipal) {
64 return new SimpleAuthorizationInfo(((ODLPrincipal) primaryPrincipal).getRoles());
67 LOG.error("Could not decode authorization request: {} is not a known principal type", primaryPrincipal);
68 return new SimpleAuthorizationInfo();
75 * Authenticates against any {@code TokenAuth} registered with the {@code ServiceLocator}.
78 protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authenticationToken)
79 throws AuthenticationException {
80 if (authenticationToken == null) {
81 throw new AuthenticationException("{\"error\":\"Unable to decode credentials\"}");
84 final String username;
85 final String password;
89 final String possiblyQualifiedUser = TokenUtils.extractUsername(authenticationToken);
90 username = HeaderUtils.extractUsername(possiblyQualifiedUser);
91 domain = HeaderUtils.extractDomain(possiblyQualifiedUser);
92 password = TokenUtils.extractPassword(authenticationToken);
93 } catch (ClassCastException e) {
94 throw new AuthenticationException(
95 "{\"error\":\"Only basic authentication is supported by TokenAuthRealm\"}", e);
98 // if the password is empty, this is an OAuth2 request, not a Basic HTTP Auth request
99 if (!Strings.isNullOrEmpty(password)) {
100 Map<String, List<String>> headers = HeaderUtils.formHeaders(username, password, domain);
101 // iterate over <code>TokenAuth</code> implementations and
103 // authentication with each one
104 for (TokenAuth ta : tokenAuthenticators.getTokenAuthCollection()) {
106 LOG.debug("Authentication attempt using {}", ta.getClass().getName());
107 final Authentication auth = ta.validate(headers);
109 LOG.debug("Authentication attempt successful");
110 authenticationService.set(auth);
111 final ODLPrincipal odlPrincipal = ODLPrincipalImpl.createODLPrincipal(auth);
112 return new SimpleAuthenticationInfo(odlPrincipal, password.toCharArray(), getName());
114 } catch (AuthenticationException ae) {
115 LOG.debug("Authentication attempt unsuccessful", ae);
116 // Purposefully generic message
117 throw new AuthenticationException("{\"error\":\"Could not authenticate\"}", ae);
122 // extract the authentication token and attempt validation of the token
123 final String token = TokenUtils.extractUsername(authenticationToken);
125 final Authentication auth = validate(token);
126 final ODLPrincipal odlPrincipal = ODLPrincipalImpl.createODLPrincipal(auth);
127 return new SimpleAuthenticationInfo(odlPrincipal, "", getName());
128 } catch (AuthenticationException e) {
129 LOG.debug("Unknown OAuth2 Token Access Request", e);
132 LOG.debug("Authentication failed: exhausted TokenAuth resources");
136 private Authentication validate(final String token) {
137 if (tokenStore == null) {
138 throw new AuthenticationException("Token store not available, could not validate the token " + token);
141 final Authentication auth = tokenStore.get(token);
143 throw new AuthenticationException("Could not validate the token " + token);
145 authenticationService.set(auth);