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

org.soulwing.jwt.api.BiPredicateAssertions Maven / Gradle / Ivy

There is a newer version: 1.7.5
Show newest version
/*
 * File created on Mar 8, 2019
 *
 * Copyright (c) 2019 Carl Harris, Jr
 * and others as noted
 *
 * 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 org.soulwing.jwt.api;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * An {@link Assertions} implementation that evaluates a list of
 * {@link BiPredicate} lambdas.
 *
 * @author Carl Harris
 */
public final class BiPredicateAssertions implements Assertions {

  private final List> assertions = new ArrayList<>();

  private  void addClaimAssertion(Function> accessor,
      Predicate condition) {
     assertions.add((claims, clock) ->
        accessor.apply(claims).map(condition::test).orElse(false));
  }

  private  void addClaimAssertion(Function> accessor,
      BiPredicate condition) {
    assertions.add((claims, clock) ->
        accessor.apply(claims)
            .map(v -> condition.test(v, clock))
            .orElse(false));
  }

  public static final class Builder implements Assertions.Builder {

    private final BiPredicateAssertions assertions;

    Builder(BiPredicateAssertions assertions) {
      this.assertions = assertions;
    }

    @Override
    public Assertions.Builder requireId() {
      return requireIdSatisfies(v -> v != null && !v.trim().isEmpty());
    }

    @Override
    public Assertions.Builder requireIdSatisfies(Predicate condition) {
      return requireSatisfies(Claims.JTI, String.class, condition);
    }

    @Override
    public Assertions.Builder requireLifetimeNotExceeded(Duration lifetime) {
      return requireIssuedAtSatisfies((t, clock) ->
          clock.instant().minus(lifetime).isBefore(t));
    }

    @Override
    public Assertions.Builder requireIssuedAtSatisfies(
        BiPredicate condition) {
      return requireInstantSatisfies(Claims.IAT, condition);
    }

    @Override
    public Assertions.Builder requireNotExpired(Duration tolerance) {
      return requireExpirationSatisfies((t, clock) ->
          clock.instant().minus(tolerance).isBefore(t));
    }

    @Override
    public Assertions.Builder requireExpirationSatisfies(
        BiPredicate condition) {
      return requireInstantSatisfies(Claims.EXP, condition);
    }

    @Override
    public Assertions.Builder requireIssuer(String issuer, String... otherIssuers) {
      return requireEquals(Claims.ISS, issuer, (Object[]) otherIssuers);
    }

    @Override
    public Assertions.Builder requireIssuerSatisfies(Predicate condition) {
      return requireSatisfies(Claims.ISS, String.class, condition);
    }

    @Override
    public Assertions.Builder requireAudience(String audience,
        String... otherAudiences) {
      return requireContains(Claims.AUD, audience, (Object[]) otherAudiences);
    }

    @Override
    public Assertions.Builder requireAudienceSatisfies(Predicate condition) {
      assertions.addClaimAssertion(listAccessor(Claims.AUD), condition);
      return this;
    }

    @Override
    public Assertions.Builder requireSubject(String subject, String... otherSubjects) {
      return requireEquals(Claims.SUB, subject, (Object[]) otherSubjects);
    }

    @Override
    public Assertions.Builder requireSubjectSatisfies(Predicate condition) {
      return requireSatisfies(Claims.SUB, String.class, condition);
    }

    @Override
    public Assertions.Builder requireEquals(
        String name, Object value, Object... otherValues) {
      return requireSatisfies(name, Object.class,
          v -> v.equals(value) || Arrays.asList(otherValues).contains(v));
    }

    @Override
    public Assertions.Builder requireContains(
        String name, Object value, Object... otherValues) {
      assertions.addClaimAssertion(listAccessor(name),
          v -> v != null
              && (v.contains(value)
                  || Arrays.stream(otherValues).anyMatch(v::contains)));
      return this;
    }

    @Override
    @SuppressWarnings("unchecked")
    public final  Assertions.Builder requireSatisfies(
        String name, Class type, Predicate condition) {
      assertions.addClaimAssertion(valueAccessor(name),
          (v) -> type.isInstance(v) && condition.test((T) v));
      return this;
    }

    @Override
    public final Assertions.Builder requireInstantSatisfies(
        String name, BiPredicate condition) {
      assertions.addClaimAssertion(valueAccessor(name),
          (v, clock) -> v instanceof Number
              && condition.test(
                    Instant.ofEpochSecond(((Number) v).longValue()), clock));
      return this;
    }

    private Function> valueAccessor(String name) {
      return (claims) -> claims.claim(name, Object.class);
    }

    private Function> listAccessor(String name) {
      return (claims) ->
          claims.claim(name, Object.class)
              .map(v -> v instanceof List ?
                  (List) v : Collections.singletonList(v));
    }

    @Override
    public BiPredicateAssertions build() {
      return assertions;
    }

  }

  /**
   * Gets a builder for a new instance.
   * @return builder
   */
  public static Builder builder() {
    return new Builder(new BiPredicateAssertions());
  }

  @Override
  public boolean test(Claims claims, Clock clock) {
    return assertions.stream()
        .allMatch(assertion -> assertion.test(claims, clock));
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy