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 static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.collect.ImmutableSet;
13 import com.google.gson.JsonParser;
14 import java.net.MalformedURLException;
16 import javax.ws.rs.client.ClientBuilder;
17 import javax.ws.rs.client.Entity;
18 import javax.ws.rs.client.WebTarget;
19 import javax.ws.rs.core.MediaType;
20 import org.apache.shiro.authc.AuthenticationException;
21 import org.apache.shiro.authc.AuthenticationInfo;
22 import org.apache.shiro.authc.AuthenticationToken;
23 import org.apache.shiro.authc.SimpleAuthenticationInfo;
24 import org.apache.shiro.authc.UsernamePasswordToken;
25 import org.apache.shiro.authz.AuthorizationInfo;
26 import org.apache.shiro.realm.AuthorizingRealm;
27 import org.apache.shiro.subject.PrincipalCollection;
28 import org.opendaylight.aaa.shiro.moon.MoonPrincipal;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
33 * MoonRealm is a Shiro Realm that authenticates users from OPNFV/moon platform.
35 * @author Alioune BA alioune.ba@orange.com
37 public class MoonRealm extends AuthorizingRealm {
38 private static final Logger LOG = LoggerFactory.getLogger(MoonRealm.class);
39 private static final String MOON_DEFAULT_DOMAIN = "sdn";
41 private volatile WebTarget moonServer;
44 protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
49 protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authenticationToken)
50 throws AuthenticationException {
51 final var principal = authenticationToken.getPrincipal();
52 if (!(principal instanceof String)) {
53 throw new AuthenticationException("Non-string principal " + principal);
56 if (!(authenticationToken instanceof UsernamePasswordToken)) {
57 throw new AuthenticationException("Token is not UsernamePasswordToken: " + authenticationToken);
60 final var password = new String(((UsernamePasswordToken) authenticationToken).getPassword());
61 // FIXME: make the domain name configurable
62 final var moonPrincipal = moonAuthenticate((String) principal, password, MOON_DEFAULT_DOMAIN);
63 return moonPrincipal == null ? null
64 : new SimpleAuthenticationInfo(moonPrincipal, password.toCharArray(), getName());
67 public MoonPrincipal moonAuthenticate(final String username, final String password, final String domain) {
68 final var moon = moonServer;
70 LOG.debug("moon server not specified, cannot authenticate");
74 final var element = JsonParser.parseString(moon.request(MediaType.APPLICATION_JSON).post(
75 // FIXME: String literal when we have JDK17
76 Entity.entity("{\"username\": \"" + username + "\",\n"
77 + " \"password\": \"" + password + "\",\n"
78 + " \"project\": \"" + domain + "\"\n}",
79 MediaType.APPLICATION_JSON),
81 if (!element.isJsonObject()) {
82 throw new IllegalStateException("Authentication error: returned output is not a JSON object");
85 final var object = element.getAsJsonObject();
86 final var error = object.get("error").getAsJsonObject();
88 throw new IllegalStateException("Authentication Error : " + error.get("title").getAsString());
91 final var token = object.get("token");
96 final var userRoles = ImmutableSet.<String>builder();
97 final var roles = object.get("roles");
99 for (var role : roles.getAsJsonArray()) {
101 userRoles.add(role.getAsString());
102 } catch (ClassCastException e) {
103 LOG.debug("Unable to cast role as String, skipping {}", role, e);
107 return new MoonPrincipal(username, domain, username + "@" + domain, userRoles.build(), token.getAsString());
111 * Injected from {@code shiro.ini}.
113 * @param moonServerURL specified in {@code shiro.ini}
114 * @throws NullPointerException If {@code moonServerURL} is {@code null}
115 * @throws IllegalArgumentException If the given string violates RFC 2396 or it does not specify a host
118 public void setMoonServerURL(final String moonServerURL) {
121 url = new URL(moonServerURL);
122 } catch (MalformedURLException e) {
123 throw new IllegalArgumentException(e);
126 final var uriHost = url.getHost();
127 checkArgument(uriHost != null, "moon host not specified in %s", url);
128 final var uriPort = url.getPort();
129 checkArgument(uriPort >= 0, "moon port not specified in %s", url);
131 final var port = Integer.toString(uriPort);
132 // FIXME: allow HTTPS!
133 // FIXME: allow authentication: and that really means configuring a Client!
134 final var server = String.format("http://%s:%s/moon/auth/tokens", uriHost, port);
135 LOG.debug("Moon server is at: {}:{} and will be accessed through {}", uriHost, port, server);
136 moonServer = ClientBuilder.newClient().target(server);