/*-
 * ~~~~~~licensing~~~~~~
 * core-support-runtime
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.core.support.runtime.cxf.jose;

import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.rs.security.jose.common.JoseConstants;
import org.apache.cxf.rs.security.jose.common.JoseException;
import org.apache.cxf.rs.security.jose.jaxrs.JwtAuthenticationFilter;
import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.rs.security.jose.jwt.JwtToken;
import org.apache.cxf.security.SecurityContext;
import ru.entaxy.platform.core.support.runtime.cxf.ExtJwtTokenSecurityContext;
import ru.entaxy.platform.core.support.runtime.cxf.security.AnonymousSecurityContext;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.HttpHeaders;
import java.io.IOException;
import java.util.List;

public class ExtJwtAuthenticationFilter extends JwtAuthenticationFilter {
	private static final String DEFAULT_AUTH_SCHEME = "JWT";
    private String expectedAuthScheme = DEFAULT_AUTH_SCHEME;
    private String regexp;

    protected String getEncodedJwtToken(ContainerRequestContext requestContext) {
        String auth = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
        String[] parts = auth == null ? null : auth.split(" ");
        if (parts == null || !expectedAuthScheme.equals(parts[0]) || parts.length != 2) {
            throw new JoseException(expectedAuthScheme + " scheme is expected");
        }
        return parts[1];
    }

    public void filter(ContainerRequestContext requestContext) throws IOException {
        if (!(requestContext.getSecurityContext() instanceof AnonymousSecurityContext) && !getHeaderDisplayServiceSchema(requestContext)) {
            String encodedJwtToken = this.getEncodedJwtToken(requestContext);
            JwtToken token = super.getJwtToken(encodedJwtToken);
            SecurityContext securityContext = this.configureSecurityContext(token);
            if (securityContext != null) {
                JAXRSUtils.getCurrentMessage().put(SecurityContext.class, securityContext);
            }
        }
    }

    private boolean getHeaderDisplayServiceSchema(ContainerRequestContext context) {
        List<String> ntxIsDisplayServiceSchema = context.getHeaders().get(AnonymousSecurityContext.HEADER_IS_DISPLAY_SERVICE_SERVICE);
        if (ntxIsDisplayServiceSchema != null && !ntxIsDisplayServiceSchema.isEmpty())
            return Boolean.parseBoolean(ntxIsDisplayServiceSchema.get(0));
        return false;
    }

    public void setExpectedAuthScheme(String expectedAuthScheme) {
        this.expectedAuthScheme = expectedAuthScheme;
    }
    
    protected SecurityContext configureSecurityContext(JwtToken jwt) {
        Message m = JAXRSUtils.getCurrentMessage();
        boolean enableUnsignedJwt =
            MessageUtils.getContextualBoolean(m, JoseConstants.ENABLE_UNSIGNED_JWT_PRINCIPAL, false);

        // The token must be signed/verified with a public key to set up the security context,
        // unless we directly configure otherwise
        if (jwt.getClaims().getSubject() != null
            && (isVerifiedWithAPublicKey(jwt) || enableUnsignedJwt)) {
            return new ExtJwtTokenSecurityContext(jwt, getRoleClaim(), regexp);
        }
        return null;
    }
    
    private boolean isVerifiedWithAPublicKey(JwtToken jwt) {
        if (isJwsRequired()) {
            String alg = (String)jwt.getJwsHeader(JoseConstants.HEADER_ALGORITHM);
            SignatureAlgorithm sigAlg = SignatureAlgorithm.getAlgorithm(alg);
            return SignatureAlgorithm.isPublicKeyAlgorithm(sigAlg);
        }

        return false;
    }
    
    public void setRegexp(String regexp) {
        this.regexp = regexp;
    }
}
