2 * Copyright (c) 2016 Orange 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 com.google.gson.JsonElement;
11 import com.google.gson.JsonObject;
12 import com.google.gson.JsonParser;
13 import java.net.MalformedURLException;
15 import java.util.LinkedHashSet;
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;
36 * MoonRealm is a Shiro Realm that authenticates users from OPNFV/moon platform.
38 * @author Alioune BA alioune.ba@orange.com
40 public class MoonRealm extends AuthorizingRealm {
42 private static final Logger LOG = LoggerFactory.getLogger(MoonRealm.class);
44 private static final String MOON_DEFAULT_DOMAIN = "sdn";
45 private URL moonServerURL;
48 protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
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;
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);
66 final UsernamePasswordToken upt;
68 upt = (UsernamePasswordToken) authenticationToken;
69 } catch (final ClassCastException e) {
70 LOG.debug("doGetAuthenticationInfo() failed because the token was not a UsernamePasswordToken", e);
74 password = new String(upt.getPassword());
76 final MoonPrincipal moonPrincipal = moonAuthenticate(username, password, domain);
77 if (moonPrincipal != null) {
78 return new SimpleAuthenticationInfo(moonPrincipal, password.toCharArray(), getName());
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);
88 final String hostFromShiro = moonServerURL != null ? moonServerURL.getHost() : null;
90 if (hostFromShiro != null) {
91 server = hostFromShiro;
93 LOG.debug("moon server was not specified appropriately, cannot authenticate");
97 final int portFromShiro = moonServerURL != null ? moonServerURL.getPort() : -1;
99 if (portFromShiro > 0) {
100 port = Integer.toString(portFromShiro);
102 LOG.debug("moon server was not specified appropriately, cannot authetnicate");
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);
114 final JsonElement element = JsonParser.parseString(output);
115 if (!element.isJsonObject()) {
116 throw new IllegalStateException("Authentication error: returned output is not a JSON object");
119 final JsonObject object = element.getAsJsonObject();
120 final JsonObject error = object.get("error").getAsJsonObject();
122 throw new IllegalStateException("Authentication Error : " + error.get("title").getAsString());
125 final JsonElement token = object.get("token");
130 final String tokenValue = token.getAsString();
131 final String userID = username + "@" + domain;
133 final Set<String> userRoles = new LinkedHashSet<>();
134 final JsonElement roles = object.get("roles");
136 for (JsonElement role : roles.getAsJsonArray()) {
138 userRoles.add(role.getAsString());
139 } catch (final ClassCastException e) {
140 LOG.debug("Unable to cast role as String, skipping {}", role, e);
144 return new MoonPrincipal(username, domain, userID, userRoles, tokenValue);
148 * Injected from <code>shiro.ini</code>.
150 * @param moonServerURL specified in <code>shiro.ini</code>
152 public void setMoonServerURL(final String moonServerURL) {
154 this.moonServerURL = new URL(moonServerURL);
155 } catch (final MalformedURLException e) {
156 LOG.warn("The moon server URL could not be parsed", e);