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

com.webauthn4j.data.client.Origin Maven / Gradle / Ivy

There is a newer version: 0.9.2.RELEASE
Show newest version
/*
 * Copyright 2018 the original author or authors.
 *
 * 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
 *
 *      https://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.webauthn4j.data.client;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.webauthn4j.util.AssertUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.net.URI;
import java.util.Objects;

/**
 * {@link Origin} contains the fully qualified origin of the requester, as provided to the authenticator
 * by the client.
 *
 * @see §5.10.1. Client Data Used in WebAuthn Signatures - origin
 */
public class Origin {

    private static final String SCHEME_HTTPS = "https";
    private static final String SCHEME_HTTP = "http";

    private final String scheme;
    private String host;
    private final Integer port;
    private final String schemeSpecificPart;
    private final boolean explicitPortNotation;

    public Origin(@NotNull String originUrl) {
        AssertUtil.notNull(originUrl, "originUrl must not be null");
        URI uri = URI.create(originUrl);

        //https://www.ietf.org/rfc/rfc1738.txt  section 2.1
        // For resiliency, programs interpreting URLs should treat upper case letters as equivalent to
        // lower case in scheme names (e.g., allow "HTTP" as well as "http").
        //
        //https://tools.ietf.org/html/rfc6454#section-4, Let uri-scheme be the scheme component of the URI, converted to
        //lowercase.

        this.scheme = toLowerCase(uri.getScheme());
        if (SCHEME_HTTPS.equals(this.scheme) || SCHEME_HTTP.equals(this.scheme)) {
            //https://tools.ietf.org/html/rfc3986#section-3.2.2 , host component is case insensitive
            this.host = toLowerCase(uri.getHost());
            int originPort = uri.getPort();
            if (originPort == -1) {
                explicitPortNotation = false;
                if (SCHEME_HTTPS.equals(this.scheme)) {
                    originPort = 443;
                }
                else { // SCHEME_HTTP
                    originPort = 80;
                }
            }
            else {
                explicitPortNotation = true;
            }

            //https://tools.ietf.org/html/rfc2396#section-3
            this.port = originPort;
            String schemeSpecificPartStr = "//" + this.host;
            if (explicitPortNotation) {
                schemeSpecificPartStr += ":" + this.port;
            }
            this.schemeSpecificPart = schemeSpecificPartStr;
        }
        else {
            this.explicitPortNotation = uri.getPort() != -1;
            this.port = null;
            this.schemeSpecificPart = uri.getSchemeSpecificPart();
        }
    }

    public static @NotNull Origin create(@NotNull String value) {
        try {
            return new Origin(value);
        } catch (NullPointerException e) {
            throw new IllegalArgumentException("value is out of range: null", e);
        }
    }

    @SuppressWarnings("unused")
    @JsonCreator
    private static @NotNull Origin deserialize(@NotNull String value) throws InvalidFormatException {
        try {
            return create(value);
        } catch (IllegalArgumentException e) {
            throw new InvalidFormatException(null, "value has an invalid syntax:'" + value + "'", value, Origin.class);
        }
    }

    private static @Nullable String toLowerCase(@Nullable String s) {
        return s == null ? null : s.toLowerCase();
    }

    public @NotNull String getScheme() {
        return scheme;
    }

    public @Nullable String getHost() {
        return host;
    }

    public @Nullable Integer getPort() {
        return port;
    }

    public @NotNull String getSchemeSpecificPart() {
        return schemeSpecificPart;
    }

    @JsonValue
    @Override
    public @NotNull String toString() {
        if (this.scheme == null) {
            return this.schemeSpecificPart;
        }
        String result;
        switch (this.scheme) {
            case SCHEME_HTTPS:
            case SCHEME_HTTP:
                result = this.scheme + "://" + this.host;
                if (this.explicitPortNotation) {
                    result += ":" + this.port;
                }
                return result;
            default:
                return this.scheme + ":" + this.schemeSpecificPart;
        }
    }

    @Override
    public boolean equals(@Nullable Object o) {
        // explicitPortNotation is not taken into count
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Origin origin = (Origin) o;
        if (SCHEME_HTTPS.equals(this.scheme) || SCHEME_HTTP.equals(this.scheme)) {
            return Objects.equals(scheme, origin.scheme) &&
                    Objects.equals(host, origin.host) &&
                    Objects.equals(port, origin.port);
        }
        else {
            return Objects.equals(scheme, origin.scheme) &&
                    Objects.equals(schemeSpecificPart, origin.schemeSpecificPart);
        }
    }

    @Override
    public int hashCode() {
        // explicitPortNotation is not taken into count
        if (SCHEME_HTTPS.equals(this.scheme) || SCHEME_HTTP.equals(this.scheme)) {
            return Objects.hash(scheme, host, port);
        }
        else {
            return Objects.hash(scheme, schemeSpecificPart);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy