// 
// Decompiled by Procyon v0.6.0
// 

package com.nimbusds.jwt.proc;

import java.util.Date;
import java.util.SortedSet;
import java.util.Iterator;
import java.util.List;
import com.nimbusds.jwt.util.DateUtils;
import java.util.Objects;
import java.util.TreeSet;
import com.nimbusds.jose.util.CollectionUtils;
import java.util.Collection;
import java.util.HashSet;
import java.util.Collections;
import com.nimbusds.jwt.JWTClaimsSet;
import java.util.Set;
import com.nimbusds.jose.shaded.jcip.ThreadSafe;
import com.nimbusds.jose.proc.SecurityContext;

@ThreadSafe
public class DefaultJWTClaimsVerifier<C extends SecurityContext> implements JWTClaimsSetVerifier<C>, ClockSkewAware
{
    public static final int DEFAULT_MAX_CLOCK_SKEW_SECONDS = 60;
    private int maxClockSkew;
    private final Set<String> acceptedAudienceValues;
    private final JWTClaimsSet exactMatchClaims;
    private final Set<String> requiredClaims;
    private final Set<String> prohibitedClaims;
    
    @Deprecated
    public DefaultJWTClaimsVerifier() {
        this(null, null, null, null);
    }
    
    public DefaultJWTClaimsVerifier(final JWTClaimsSet exactMatchClaims, final Set<String> requiredClaims) {
        this(null, exactMatchClaims, requiredClaims, null);
    }
    
    public DefaultJWTClaimsVerifier(final String requiredAudience, final JWTClaimsSet exactMatchClaims, final Set<String> requiredClaims) {
        this((requiredAudience != null) ? Collections.singleton(requiredAudience) : null, exactMatchClaims, requiredClaims, null);
    }
    
    public DefaultJWTClaimsVerifier(final Set<String> acceptedAudience, final JWTClaimsSet exactMatchClaims, final Set<String> requiredClaims, final Set<String> prohibitedClaims) {
        this.maxClockSkew = 60;
        this.acceptedAudienceValues = ((acceptedAudience != null) ? Collections.unmodifiableSet((Set<? extends String>)acceptedAudience) : null);
        this.exactMatchClaims = ((exactMatchClaims != null) ? exactMatchClaims : new JWTClaimsSet.Builder().build());
        final Set<String> requiredClaimsCopy = new HashSet<String>(this.exactMatchClaims.getClaims().keySet());
        if (this.acceptedAudienceValues != null && !CollectionUtils.containsNull(this.acceptedAudienceValues)) {
            requiredClaimsCopy.add("aud");
        }
        if (requiredClaims != null) {
            requiredClaimsCopy.addAll(requiredClaims);
        }
        this.requiredClaims = Collections.unmodifiableSet((Set<? extends String>)requiredClaimsCopy);
        this.prohibitedClaims = ((prohibitedClaims != null) ? Collections.unmodifiableSet((Set<? extends String>)prohibitedClaims) : Collections.emptySet());
    }
    
    public Set<String> getAcceptedAudienceValues() {
        return this.acceptedAudienceValues;
    }
    
    public JWTClaimsSet getExactMatchClaims() {
        return this.exactMatchClaims;
    }
    
    public Set<String> getRequiredClaims() {
        return this.requiredClaims;
    }
    
    public Set<String> getProhibitedClaims() {
        return this.prohibitedClaims;
    }
    
    @Override
    public int getMaxClockSkew() {
        return this.maxClockSkew;
    }
    
    @Override
    public void setMaxClockSkew(final int maxClockSkewSeconds) {
        this.maxClockSkew = maxClockSkewSeconds;
    }
    
    @Override
    public void verify(final JWTClaimsSet claimsSet, final C context) throws BadJWTException {
        if (this.acceptedAudienceValues != null) {
            final List<String> audList = claimsSet.getAudience();
            if (audList != null && !audList.isEmpty()) {
                boolean audMatch = false;
                for (final String aud : audList) {
                    if (this.acceptedAudienceValues.contains(aud)) {
                        audMatch = true;
                        break;
                    }
                }
                if (!audMatch) {
                    throw new BadJWTException("JWT aud claim rejected");
                }
            }
            else if (!CollectionUtils.containsNull(this.acceptedAudienceValues)) {
                throw new BadJWTException("JWT missing required aud claim");
            }
        }
        if (!claimsSet.getClaims().keySet().containsAll(this.requiredClaims)) {
            final SortedSet<String> missingClaims = new TreeSet<String>(this.requiredClaims);
            missingClaims.removeAll(claimsSet.getClaims().keySet());
            throw new BadJWTException("JWT missing required claims: " + missingClaims);
        }
        final SortedSet<String> presentProhibitedClaims = new TreeSet<String>();
        for (final String prohibited : this.prohibitedClaims) {
            if (claimsSet.getClaims().containsKey(prohibited)) {
                presentProhibitedClaims.add(prohibited);
            }
        }
        if (!presentProhibitedClaims.isEmpty()) {
            throw new BadJWTException("JWT has prohibited claims: " + presentProhibitedClaims);
        }
        for (final String exactMatch : this.exactMatchClaims.getClaims().keySet()) {
            final Object actualClaim = claimsSet.getClaim(exactMatch);
            final Object expectedClaim = this.exactMatchClaims.getClaim(exactMatch);
            if (!Objects.equals(expectedClaim, actualClaim)) {
                throw new BadJWTException("JWT " + exactMatch + " claim value rejected");
            }
        }
        final Date now = this.currentTime();
        if (now != null) {
            final Date exp = claimsSet.getExpirationTime();
            if (exp != null && !DateUtils.isAfter(exp, now, this.maxClockSkew)) {
                throw new ExpiredJWTException("Expired JWT");
            }
            final Date nbf = claimsSet.getNotBeforeTime();
            if (nbf != null && !DateUtils.isBefore(nbf, now, this.maxClockSkew)) {
                throw new BadJWTException("JWT before use time");
            }
        }
    }
    
    protected Date currentTime() {
        return new Date();
    }
}
