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

com.predic8.membrane.core.interceptor.authentication.xen.XenAuthenticationInterceptor Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/* Copyright 2019 predic8 GmbH, www.predic8.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. */
package com.predic8.membrane.core.interceptor.authentication.xen;

import com.bornium.security.oauth2openid.Constants;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.predic8.membrane.annot.MCAttribute;
import com.predic8.membrane.annot.MCChildElement;
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.annot.MCTextContent;
import com.predic8.membrane.core.Router;
import com.predic8.membrane.core.config.security.Blob;
import com.predic8.membrane.core.exchange.Exchange;
import com.predic8.membrane.core.interceptor.AbstractInterceptor;
import com.predic8.membrane.core.interceptor.Outcome;
import com.predic8.membrane.core.interceptor.authentication.session.UserDataProvider;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
import org.jose4j.lang.JoseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.predic8.membrane.annot.Required;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@MCElement(name = "xenAuthentication")
public class XenAuthenticationInterceptor extends AbstractInterceptor {

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

    private String user, password;
    private UserDataProvider userDataProvider;

    private XenSessionManager sessionManager;

    @Override
    public void init(Router router) throws Exception {
        super.init(router);
        userDataProvider.init(router);
        sessionManager.init(router);
    }

    @Override
    public Outcome handleRequest(Exchange exc) throws Exception {
        XenCredentialAccessor.XenLoginData login = new XenCredentialAccessor().getLogin(exc);
        if (login != null) {
            // this is a login message
            userDataProvider.verify(ImmutableMap.of("username", login.username, "password", login.password));

            login.username = user;
            login.password = password;
            new XenCredentialAccessor().replaceLogin(exc, login);
            return Outcome.CONTINUE;
        }

        // map session ids
        String ourSessionId = new XenSessionIdAccessor().getSessionId(exc, Flow.REQUEST);
        String xenSessionId = sessionManager.getXenSessionId(ourSessionId);
        if (xenSessionId == null)
            throw new RuntimeException("Session not found.");
        new XenSessionIdAccessor().replaceSessionId(exc, xenSessionId, Flow.REQUEST);

        return Outcome.CONTINUE;
    }

    @Override
    public Outcome handleResponse(Exchange exc) throws Exception {
        // map session ids
        String sessionId = new XenSessionIdAccessor().getSessionId(exc, Flow.RESPONSE);
        if (sessionId == null || sessionId.length() == 0)
            return Outcome.CONTINUE;

        String newSessionId = sessionManager.getExistingSessionId(sessionId);

        if (newSessionId == null) {
            // generate session ID mapping
            newSessionId = sessionManager.createSessionId(sessionId);
        }

        new XenSessionIdAccessor().replaceSessionId(exc, newSessionId, Flow.RESPONSE);

        return Outcome.CONTINUE;
    }

    public interface XenSessionManager {
        void init(Router router) throws Exception;
        String getXenSessionId(String ourSessionId);
        String getExistingSessionId(String xenSessionId);
        String createSessionId(String xenSessionId);
    }

    @MCElement(name = "inMemorySessionManager", topLevel = false)
    public static class InMemorySessionManager implements XenSessionManager {
        private Map ourSessionIds = new ConcurrentHashMap<>();
        private Map xenSessionIds = new ConcurrentHashMap<>();

        public void init(Router router) throws Exception {
        }

        public String getXenSessionId(String ourSessionId) {
            return ourSessionIds.get(ourSessionId);
        }

        public String getExistingSessionId(String xenSessionId) {
            return xenSessionIds.get(xenSessionId);
        }

        public String createSessionId(String xenSessionId) {
            String newSessionId = UUID.randomUUID().toString();
            xenSessionIds.put(xenSessionId, newSessionId);
            ourSessionIds.put(newSessionId, xenSessionId);
            return newSessionId;
        }
    }

    @MCElement(name = "jwtSessionManager", topLevel = false, id = "xenAuthentication-jwtSessionManager")
    public static class JwtSessionManager implements XenSessionManager {
        private String audience;

        private Jwk jwk;

        private RsaJsonWebKey rsaJsonWebKey;

        private final SecureRandom random = new SecureRandom();

        public void init(Router router) throws Exception {
            String key = jwk.get(router.getResolverMap(), router.getBaseLocation());
            if (key == null || key.length() == 0)
                rsaJsonWebKey = generateKey();
            else
                rsaJsonWebKey = new RsaJsonWebKey(JsonUtil.parseJson(key));
        }

        private RsaJsonWebKey generateKey() throws JoseException {
            RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
            rsaJsonWebKey.setKeyId(new BigInteger(130, random).toString(32));
            rsaJsonWebKey.setUse("sig");
            rsaJsonWebKey.setAlgorithm("RS256");

            LOG.warn("Using dynamically genererated key, you should write this as " + rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE) + " .");

            return rsaJsonWebKey;
        }

        public String getXenSessionId(String ourSessionId) {
            JwtConsumer consumer = new JwtConsumerBuilder()
                    .setExpectedAudience(true, audience)
                    .setVerificationKeyResolver(new JwksVerificationKeyResolver(Lists.newArrayList(rsaJsonWebKey)))
                    .build();

            try {
                JwtClaims claims = consumer.processToClaims(ourSessionId);
                return claims.getSubject();
            } catch (InvalidJwtException | MalformedClaimException e) {
                throw new RuntimeException(e);
            }
        }

        public String getExistingSessionId(String xenSessionId) {
            return null; // always issue a new JWT
        }

        public String createSessionId(String xenSessionId) {
            JwtClaims jwtClaims = createClaims(xenSessionId);

            JsonWebSignature jws = new JsonWebSignature();
            jws.setPayload(jwtClaims.toJson());
            jws.setKey(rsaJsonWebKey.getPrivateKey());
            jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
            jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);

            try {
                return jws.getCompactSerialization();
            } catch (JoseException e) {
                throw new RuntimeException(e);
            }
        }

        private JwtClaims createClaims(String xenSessionId) {
            JwtClaims jwtClaims = new JwtClaims();
            jwtClaims.setSubject(xenSessionId);
            jwtClaims.setAudience(audience);
            jwtClaims.setIssuedAtToNow();
            NumericDate expiration = NumericDate.now();
            expiration.addSeconds(24 * 60 * 60);
            jwtClaims.setExpirationTime(expiration);
            jwtClaims.setNotBeforeMinutesInThePast(2);
            jwtClaims.setClaim(Constants.CLAIM_NONCE, random.nextLong());
            return jwtClaims;
        }

        public String getAudience() {
            return audience;
        }

        @MCAttribute
        public void setAudience(String audience) {
            this.audience = audience;
        }

        public Jwk getJwk() {
            return jwk;
        }

        @Required
        @MCChildElement
        public void setJwk(Jwk jwk) {
            this.jwk = jwk;
        }

        @MCElement(name="jwk", mixed = true, topLevel = false, id="xenAuthentication-jwtSessionManager-jwk")
        public static class Jwk extends Blob {

        }
    }

    public String getUser() {
        return user;
    }

    @MCAttribute
    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    @MCAttribute
    public void setPassword(String password) {
        this.password = password;
    }

    public UserDataProvider getUserDataProvider() {
        return userDataProvider;
    }

    @MCChildElement(order = 10)
    public void setUserDataProvider(UserDataProvider userDataProvider) {
        this.userDataProvider = userDataProvider;
    }

    public XenSessionManager getSessionManager() {
        return sessionManager;
    }

    @MCChildElement(order = 20)
    public void setSessionManager(XenSessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy