001/*
002 * acme4j - Java ACME client
003 *
004 * Copyright (C) 2016 Richard "Shred" Körber
005 *   http://acme4j.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.acme4j.provider;
015
016import java.io.IOException;
017import java.net.URI;
018import java.net.URL;
019import java.time.Instant;
020import java.util.HashMap;
021import java.util.Map;
022import java.util.Optional;
023import java.util.function.BiFunction;
024
025import org.shredzone.acme4j.Login;
026import org.shredzone.acme4j.Session;
027import org.shredzone.acme4j.challenge.Challenge;
028import org.shredzone.acme4j.connector.Connection;
029import org.shredzone.acme4j.connector.DummyConnection;
030import org.shredzone.acme4j.connector.NetworkSettings;
031import org.shredzone.acme4j.connector.Resource;
032import org.shredzone.acme4j.toolbox.JSON;
033import org.shredzone.acme4j.toolbox.JSONBuilder;
034import org.shredzone.acme4j.toolbox.TestUtils;
035
036/**
037 * Test implementation of {@link AcmeProvider}. It also implements a dummy implementation
038 * of {@link Connection} that is always returned on {@link #connect(URI, NetworkSettings)}.
039 */
040public class TestableConnectionProvider extends DummyConnection implements AcmeProvider {
041    private final Map<String, BiFunction<Login, JSON, Challenge>> creatorMap = new HashMap<>();
042    private final Map<String, Challenge> createdMap = new HashMap<>();
043    private final JSONBuilder directory = new JSONBuilder();
044    private JSONBuilder metadata = null;
045
046    /**
047     * Register a {@link Resource} mapping.
048     *
049     * @param r
050     *            {@link Resource} to be mapped
051     * @param u
052     *            {@link URL} to be returned
053     */
054    public void putTestResource(Resource r, URL u) {
055        directory.put(r.path(), u);
056    }
057
058    /**
059     * Add a property to the metadata registry.
060     *
061     * @param key
062     *            Metadata key
063     * @param value
064     *            Metadata value
065     */
066    public void putMetadata(String key, Object value) {
067        if (metadata == null) {
068            metadata = directory.object("meta");
069        }
070        metadata.put(key, value);
071    }
072
073    /**
074     * Register a {@link Challenge}.
075     *
076     * @param type
077     *            Challenge type to register.
078     * @param creator
079     *            Creator {@link BiFunction} that creates a matching {@link Challenge}
080     */
081    public void putTestChallenge(String type, BiFunction<Login, JSON, Challenge> creator) {
082        creatorMap.put(type, creator);
083    }
084
085    /**
086     * Returns the {@link Challenge} instance that has been created. Fails if no such
087     * challenge was created.
088     *
089     * @param type Challenge type
090     * @return Created {@link Challenge} instance
091     */
092    public Challenge getChallenge(String type) {
093        if (!createdMap.containsKey(type)) {
094            throw new IllegalArgumentException("No challenge of type " + type + " was created");
095        }
096        return createdMap.get(type);
097    }
098
099    /**
100     * Creates a {@link Session} that uses this {@link AcmeProvider}.
101     */
102    public Session createSession() {
103        return TestUtils.session(this);
104    }
105
106    /**
107     * Creates a {@link Login} that uses this {@link AcmeProvider}.
108     */
109    public Login createLogin() throws IOException {
110        var session = createSession();
111        return session.login(new URL(TestUtils.ACCOUNT_URL), TestUtils.createKeyPair());
112    }
113
114    @Override
115    public Optional<Instant> getRetryAfter() {
116        return Optional.empty();
117    }
118
119    @Override
120    public boolean accepts(URI serverUri) {
121        throw new UnsupportedOperationException();
122    }
123
124    @Override
125    public URL resolve(URI serverUri) {
126        throw new UnsupportedOperationException();
127    }
128
129    @Override
130    public Connection connect(URI serverUri, NetworkSettings networkSettings) {
131        return this;
132    }
133
134    @Override
135    public JSON directory(Session session, URI serverUri) {
136        if (directory.toMap().isEmpty()) {
137            throw new UnsupportedOperationException();
138        }
139        return directory.toJSON();
140    }
141
142    @Override
143    public Challenge createChallenge(Login login, JSON data) {
144        if (creatorMap.isEmpty()) {
145            throw new UnsupportedOperationException();
146        }
147
148        Challenge created;
149
150        var type = data.get("type").asString();
151        if (creatorMap.containsKey(type)) {
152            created = creatorMap.get(type).apply(login, data);
153        } else {
154            created = new Challenge(login, data);
155        }
156
157        createdMap.put(type, created);
158
159        return created;
160    }
161
162}