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.opendaylight.aaa.shiro.moon.MoonPrincipal;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
35 * MoonRealm is a Shiro Realm that authenticates users from OPNFV/moon platform.
37 * @author Alioune BA alioune.ba@orange.com
39 public class MoonRealm extends AuthorizingRealm {
41 private static final Logger LOG = LoggerFactory.getLogger(MoonRealm.class);
43 private static final String MOON_DEFAULT_DOMAIN = "sdn";
44 private URL moonServerURL;
47 protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
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;
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);
65 final UsernamePasswordToken upt;
67 upt = (UsernamePasswordToken) authenticationToken;
68 } catch (final ClassCastException e) {
69 LOG.debug("doGetAuthenticationInfo() failed because the token was not a UsernamePasswordToken", e);
73 password = new String(upt.getPassword());
75 final MoonPrincipal moonPrincipal = moonAuthenticate(username, password, domain);
76 if (moonPrincipal != null) {
77 return new SimpleAuthenticationInfo(moonPrincipal, password.toCharArray(), getName());
83 public MoonPrincipal moonAuthenticate(final String username, final String password, final String domain) {
84 final Client client = ClientBuilder.newClient();
86 final String hostFromShiro = moonServerURL != null ? moonServerURL.getHost() : null;
88 if (hostFromShiro != null) {
89 server = hostFromShiro;
91 LOG.debug("moon server was not specified appropriately, cannot authenticate");
95 final int portFromShiro = moonServerURL != null ? moonServerURL.getPort() : -1;
97 if (portFromShiro > 0) {
98 port = Integer.toString(portFromShiro);
100 LOG.debug("moon server was not specified appropriately, cannot authetnicate");
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);
112 final JsonElement element = JsonParser.parseString(output);
113 if (!element.isJsonObject()) {
114 throw new IllegalStateException("Authentication error: returned output is not a JSON object");
117 final JsonObject object = element.getAsJsonObject();
118 final JsonObject error = object.get("error").getAsJsonObject();
120 throw new IllegalStateException("Authentication Error : " + error.get("title").getAsString());
123 final JsonElement token = object.get("token");
128 final String tokenValue = token.getAsString();
129 final String userID = username + "@" + domain;
131 final Set<String> userRoles = new LinkedHashSet<>();
132 final JsonElement roles = object.get("roles");
134 for (JsonElement role : roles.getAsJsonArray()) {
136 userRoles.add(role.getAsString());
137 } catch (final ClassCastException e) {
138 LOG.debug("Unable to cast role as String, skipping {}", role, e);
142 return new MoonPrincipal(username, domain, userID, userRoles, tokenValue);
146 * Injected from <code>shiro.ini</code>.
148 * @param moonServerURL specified in <code>shiro.ini</code>
150 public void setMoonServerURL(final String moonServerURL) {
152 this.moonServerURL = new URL(moonServerURL);
153 } catch (final MalformedURLException e) {
154 LOG.warn("The moon server URL could not be parsed", e);