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

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

There is a newer version: 0.20.1
Show newest version
/*
 * Copyright (c) 2015 - 2017 Brocade Communications Systems, Inc. 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 com.google.common.base.Strings;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.opendaylight.aaa.api.Authentication;
import org.opendaylight.aaa.api.AuthenticationService;
import org.opendaylight.aaa.api.TokenAuth;
import org.opendaylight.aaa.api.TokenStore;
import org.opendaylight.aaa.api.shiro.principal.ODLPrincipal;
import org.opendaylight.aaa.shiro.principal.ODLPrincipalImpl;
import org.opendaylight.aaa.shiro.realm.util.TokenUtils;
import org.opendaylight.aaa.shiro.realm.util.http.header.HeaderUtils;
import org.opendaylight.aaa.shiro.tokenauthrealm.auth.TokenAuthenticators;
import org.opendaylight.aaa.shiro.web.env.ThreadLocals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * TokenAuthRealm is an adapter between the AAA shiro subsystem and the existing
 * TokenAuth mechanisms. Thus, one can enable use of
 * IDMStore and IDMMDSALStore.
 */
public class TokenAuthRealm extends AuthorizingRealm {

    /**
     * The unique identifying name for TokenAuthRealm.
     */
    private static final String TOKEN_AUTH_REALM_DEFAULT_NAME = "TokenAuthRealm";

    /**
     * The message that is displayed if no TokenAuth interface is available yet.
     */
    private static final String AUTHENTICATION_SERVICE_UNAVAILABLE_MESSAGE =
            "{\"error\":\"Authentication service unavailable\"}";

    /**
     * The message that is displayed if credentials are missing or malformed.
     */
    private static final String FATAL_ERROR_DECODING_CREDENTIALS = "{\"error\":\"Unable to decode credentials\"}";

    /**
     * The message that is displayed if non-Basic Auth is attempted.
     */
    private static final String FATAL_ERROR_BASIC_AUTH_ONLY
            = "{\"error\":\"Only basic authentication is supported by TokenAuthRealm\"}";

    /**
     * The purposefully generic message displayed if TokenAuth is
     * unable to validate the given credentials.
     */
    private static final String UNABLE_TO_AUTHENTICATE = "{\"error\":\"Could not authenticate\"}";

    private static final Logger LOG = LoggerFactory.getLogger(TokenAuthRealm.class);

    private final AuthenticationService authenticationService;
    private final TokenStore tokenStore;
    private final TokenAuthenticators tokenAuthenticators;

    public TokenAuthRealm() {
        super.setName(TOKEN_AUTH_REALM_DEFAULT_NAME);
        authenticationService = Objects.requireNonNull(ThreadLocals.AUTH_SETVICE_TL.get());
        tokenStore = ThreadLocals.TOKEN_STORE_TL.get();
        tokenAuthenticators = Objects.requireNonNull(ThreadLocals.TOKEN_AUTHENICATORS_TL.get());
    }

    /*
     * (non-Javadoc)
     *
     * Roles are derived from TokenAuth.authenticate(). Shiro roles
     * are identical to existing IDM roles.
     *
     * @see
     * org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache
     * .shiro.subject.PrincipalCollection)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
        final Object primaryPrincipal = principalCollection.getPrimaryPrincipal();
        final ODLPrincipal odlPrincipal;
        try {
            odlPrincipal = (ODLPrincipal) primaryPrincipal;
            return new SimpleAuthorizationInfo(odlPrincipal.getRoles());
        } catch (ClassCastException e) {
            LOG.error("Couldn't decode authorization request", e);
        }
        return new SimpleAuthorizationInfo();
    }

    /*
     * (non-Javadoc)
     *
     * Authenticates against any TokenAuth registered with the
     * ServiceLocator
     *
     * @see
     * org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org
     * .apache.shiro.authc.AuthenticationToken)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            final AuthenticationToken authenticationToken) throws AuthenticationException {

        final String username;
        final String password;
        final String domain;

        try {
            final String possiblyQualifiedUser = TokenUtils.extractUsername(authenticationToken);
            username = HeaderUtils.extractUsername(possiblyQualifiedUser);
            domain = HeaderUtils.extractDomain(possiblyQualifiedUser);
            password = TokenUtils.extractPassword(authenticationToken);

        } catch (NullPointerException e) {
            throw new AuthenticationException(FATAL_ERROR_DECODING_CREDENTIALS, e);
        } catch (ClassCastException e) {
            throw new AuthenticationException(FATAL_ERROR_BASIC_AUTH_ONLY, e);
        }

        // if the password is empty, this is an OAuth2 request, not a Basic HTTP
        // Auth request
        if (!Strings.isNullOrEmpty(password)) {
            Map> headers = HeaderUtils.formHeaders(username, password, domain);
            // iterate over TokenAuth implementations and
            // attempt to
            // authentication with each one
            for (TokenAuth ta : tokenAuthenticators.getTokenAuthCollection()) {
                try {
                    LOG.debug("Authentication attempt using {}", ta.getClass().getName());
                    final Authentication auth = ta.validate(headers);
                    if (auth != null) {
                        LOG.debug("Authentication attempt successful");
                        authenticationService.set(auth);
                        final ODLPrincipal odlPrincipal = ODLPrincipalImpl.createODLPrincipal(auth);
                        return new SimpleAuthenticationInfo(odlPrincipal, password.toCharArray(), getName());
                    }
                } catch (AuthenticationException ae) {
                    LOG.debug("Authentication attempt unsuccessful");
                    throw new AuthenticationException(UNABLE_TO_AUTHENTICATE, ae);
                }
            }
        }

        // extract the authentication token and attempt validation of the token
        final String token = TokenUtils.extractUsername(authenticationToken);
        try {
            final Authentication auth = validate(token);
            final ODLPrincipal odlPrincipal = ODLPrincipalImpl.createODLPrincipal(auth);
            return new SimpleAuthenticationInfo(odlPrincipal, "", getName());
        } catch (AuthenticationException e) {
            LOG.debug("Unknown OAuth2 Token Access Request", e);
        }

        LOG.debug("Authentication failed: exhausted TokenAuth resources");
        return null;
    }

    private Authentication validate(final String token) {
        if (tokenStore == null) {
            throw new AuthenticationException("Token store not available, could not validate the token " + token);
        }

        final Authentication auth = tokenStore.get(token);
        if (auth == null) {
            throw new AuthenticationException("Could not validate the token " + token);
        }
        authenticationService.set(auth);
        return auth;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy