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