All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.aaa.shiro.realm.MoonRealm Maven / Gradle / Ivy

There is a newer version: 0.20.3
Show newest version
/*
 * Copyright (c) 2016 Orange and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.aaa.shiro.realm;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;

import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonParser;
import java.net.MalformedURLException;
import java.net.URL;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.opendaylight.aaa.shiro.moon.MoonPrincipal;
import org.opendaylight.aaa.web.servlet.ServletSupport;
import org.opendaylight.yangtools.concepts.Registration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * MoonRealm is a Shiro Realm that authenticates users from OPNFV/moon platform.
 *
 * @author Alioune BA [email protected]
 */
public class MoonRealm extends AuthorizingRealm {
    private static final Logger LOG = LoggerFactory.getLogger(MoonRealm.class);
    private static final ThreadLocal SERVLET_SUPPORT_TL = new ThreadLocal<>();
    private static final String MOON_DEFAULT_DOMAIN = "sdn";

    private final ServletSupport servletSupport;
    private volatile WebTarget moonServer;

    public MoonRealm() {
        this(verifyNotNull(SERVLET_SUPPORT_TL.get(), "MoonRealm loading not prepared"));
    }

    public MoonRealm(final ServletSupport servletSupport) {
        this.servletSupport = requireNonNull(servletSupport);
    }

    public static Registration prepareForLoad(final ServletSupport jaxrsSupport) {
        SERVLET_SUPPORT_TL.set(requireNonNull(jaxrsSupport));
        return SERVLET_SUPPORT_TL::remove;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authenticationToken)
            throws AuthenticationException {
        final var principal = authenticationToken.getPrincipal();
        if (!(principal instanceof String stringPrincipal)) {
            throw new AuthenticationException("Non-string principal " + principal);
        }

        if (!(authenticationToken instanceof UsernamePasswordToken userPassToken)) {
            throw new AuthenticationException("Token is not UsernamePasswordToken: " + authenticationToken);
        }

        final var password = new String(userPassToken.getPassword());
        // FIXME: make the domain name configurable
        final var moonPrincipal = moonAuthenticate(stringPrincipal, password, MOON_DEFAULT_DOMAIN);
        return moonPrincipal == null ? null
            : new SimpleAuthenticationInfo(moonPrincipal, password.toCharArray(), getName());
    }

    public MoonPrincipal moonAuthenticate(final String username, final String password, final String domain) {
        final var moon = moonServer;
        if (moon == null) {
            LOG.debug("moon server not specified, cannot authenticate");
            return null;
        }

        final var element = JsonParser.parseString(moon.request(MediaType.APPLICATION_JSON).post(
            // FIXME: String literal when we have JDK17
            Entity.entity("{\"username\": \"" + username + "\",\n"
                + "  \"password\": \"" + password + "\",\n"
                + "  \"project\": \"" + domain + "\"\n}",
                MediaType.APPLICATION_JSON),
            String.class));
        if (!element.isJsonObject()) {
            throw new IllegalStateException("Authentication error: returned output is not a JSON object");
        }

        final var object = element.getAsJsonObject();
        final var error = object.get("error").getAsJsonObject();
        if (error != null) {
            throw new IllegalStateException("Authentication Error : " + error.get("title").getAsString());
        }

        final var token = object.get("token");
        if (token == null) {
            return null;
        }

        final var userRoles = ImmutableSet.builder();
        final var roles = object.get("roles");
        if (roles != null) {
            for (var role : roles.getAsJsonArray()) {
                try {
                    userRoles.add(role.getAsString());
                } catch (ClassCastException e) {
                    LOG.debug("Unable to cast role as String, skipping {}", role, e);
                }
            }
        }
        return new MoonPrincipal(username, domain, username + "@" + domain, userRoles.build(), token.getAsString());
    }

    /**
     * Injected from {@code shiro.ini}.
     *
     * @param moonServerURL specified in {@code shiro.ini}
     * @throws NullPointerException If {@code moonServerURL} is {@code null}
     * @throws IllegalArgumentException If the given string violates RFC 2396 or it does not specify a host
     *                                  and a port.
     */
    public void setMoonServerURL(final String moonServerURL) {
        final URL url;
        try {
            url = new URL(moonServerURL);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException(e);
        }

        final var uriHost = url.getHost();
        checkArgument(uriHost != null, "moon host not specified in %s", url);
        final var uriPort = url.getPort();
        checkArgument(uriPort >= 0, "moon port not specified in %s", url);

        final var port = Integer.toString(uriPort);
        // FIXME: allow HTTPS!
        // FIXME: allow authentication: and that really means configuring a Client!
        final var server = String.format("http://%s:%s/moon/auth/tokens", uriHost, port);
        LOG.debug("Moon server is at: {}:{} and will be accessed through {}", uriHost, port, server);
        moonServer = servletSupport.newClientBuilder().build().target(server);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy