646416fbb89141305c15bdce2b80ed20652911ac
[aaa.git] / aaa-shiro / impl / src / main / java / org / opendaylight / aaa / shiro / realm / MoonRealm.java
1 /*
2  * Copyright (c) 2016 Orange 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.shiro.realm;
9
10 import com.google.gson.JsonElement;
11 import com.google.gson.JsonObject;
12 import com.google.gson.JsonParser;
13 import java.net.MalformedURLException;
14 import java.net.URL;
15 import java.util.LinkedHashSet;
16 import java.util.Set;
17 import javax.ws.rs.client.Client;
18 import javax.ws.rs.client.ClientBuilder;
19 import javax.ws.rs.client.Entity;
20 import javax.ws.rs.client.WebTarget;
21 import javax.ws.rs.core.MediaType;
22 import org.apache.shiro.authc.AuthenticationException;
23 import org.apache.shiro.authc.AuthenticationInfo;
24 import org.apache.shiro.authc.AuthenticationToken;
25 import org.apache.shiro.authc.SimpleAuthenticationInfo;
26 import org.apache.shiro.authc.UsernamePasswordToken;
27 import org.apache.shiro.authz.AuthorizationInfo;
28 import org.apache.shiro.realm.AuthorizingRealm;
29 import org.apache.shiro.subject.PrincipalCollection;
30 import org.opendaylight.aaa.shiro.moon.MoonPrincipal;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * MoonRealm is a Shiro Realm that authenticates users from OPNFV/moon platform.
36  *
37  * @author Alioune BA alioune.ba@orange.com
38  */
39 public class MoonRealm extends AuthorizingRealm {
40
41     private static final Logger LOG = LoggerFactory.getLogger(MoonRealm.class);
42
43     private static final String MOON_DEFAULT_DOMAIN = "sdn";
44     private URL moonServerURL;
45
46     @Override
47     protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
48         return null;
49     }
50
51     @Override
52     protected AuthenticationInfo doGetAuthenticationInfo(
53             final AuthenticationToken authenticationToken) throws AuthenticationException {
54         final String username;
55         final String password;
56         final String domain = MOON_DEFAULT_DOMAIN;
57
58         try {
59             username = (String) authenticationToken.getPrincipal();
60         } catch (final ClassCastException e) {
61             LOG.debug("doGetAuthenticationInfo() failed because the principal couldn't be cast as a String", e);
62             throw e;
63         }
64
65         final UsernamePasswordToken upt;
66         try {
67             upt = (UsernamePasswordToken) authenticationToken;
68         } catch (final ClassCastException e) {
69             LOG.debug("doGetAuthenticationInfo() failed because the token was not a UsernamePasswordToken", e);
70             throw e;
71         }
72
73         password = new String(upt.getPassword());
74
75         final MoonPrincipal moonPrincipal = moonAuthenticate(username, password, domain);
76         if (moonPrincipal != null) {
77             return new SimpleAuthenticationInfo(moonPrincipal, password.toCharArray(), getName());
78         } else {
79             return null;
80         }
81     }
82
83     public MoonPrincipal moonAuthenticate(final String username, final String password, final String domain) {
84         final Client client = ClientBuilder.newClient();
85
86         final String hostFromShiro = moonServerURL != null ? moonServerURL.getHost() : null;
87         final String server;
88         if (hostFromShiro != null) {
89             server = hostFromShiro;
90         } else {
91             LOG.debug("moon server was not specified appropriately, cannot authenticate");
92             return null;
93         }
94
95         final int portFromShiro = moonServerURL != null ? moonServerURL.getPort() : -1;
96         final String port;
97         if (portFromShiro > 0) {
98             port = Integer.toString(portFromShiro);
99         } else {
100             LOG.debug("moon server was not specified appropriately, cannot authetnicate");
101             return null;
102         }
103
104         final String url = String.format("http://%s:%s/moon/auth/tokens", server, port);
105         LOG.debug("Moon server is at: {}:{} and will be accessed through {}", server, port, url);
106         WebTarget webTarget = client.target(url);
107         final String input = "{\"username\": \"" + username + "\"," + "\"password\":" + "\"" + password + "\","
108                 + "\"project\":" + "\"" + domain + "\"" + "}";
109         final String output = webTarget.request(MediaType.APPLICATION_JSON)
110                 .post(Entity.entity(input, MediaType.APPLICATION_JSON), String.class);
111
112         final JsonElement element = JsonParser.parseString(output);
113         if (!element.isJsonObject()) {
114             throw new IllegalStateException("Authentication error: returned output is not a JSON object");
115         }
116
117         final JsonObject object = element.getAsJsonObject();
118         final JsonObject error = object.get("error").getAsJsonObject();
119         if (error != null) {
120             throw new IllegalStateException("Authentication Error : " + error.get("title").getAsString());
121         }
122
123         final JsonElement token = object.get("token");
124         if (token == null) {
125             return null;
126         }
127
128         final String tokenValue = token.getAsString();
129         final String userID = username + "@" + domain;
130
131         final Set<String> userRoles = new LinkedHashSet<>();
132         final JsonElement roles = object.get("roles");
133         if (roles != null) {
134             for (JsonElement role : roles.getAsJsonArray()) {
135                 try {
136                     userRoles.add(role.getAsString());
137                 } catch (final ClassCastException e) {
138                     LOG.debug("Unable to cast role as String, skipping {}", role, e);
139                 }
140             }
141         }
142         return new MoonPrincipal(username, domain, userID, userRoles, tokenValue);
143     }
144
145     /**
146      * Injected from <code>shiro.ini</code>.
147      *
148      * @param moonServerURL specified in <code>shiro.ini</code>
149      */
150     public void setMoonServerURL(final String moonServerURL) {
151         try {
152             this.moonServerURL = new URL(moonServerURL);
153         } catch (final MalformedURLException e) {
154             LOG.warn("The moon server URL could not be parsed", e);
155         }
156     }
157 }