001/*
002 * acme4j - Java ACME client
003 *
004 * Copyright (C) 2018 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;
015
016import static java.util.Objects.requireNonNull;
017
018import java.net.URL;
019import java.security.KeyPair;
020import java.util.Objects;
021
022import org.shredzone.acme4j.challenge.Challenge;
023import org.shredzone.acme4j.connector.Connection;
024import org.shredzone.acme4j.exception.AcmeException;
025import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
026import org.shredzone.acme4j.exception.AcmeProtocolException;
027import org.shredzone.acme4j.toolbox.JSON;
028
029/**
030 * A {@link Login} is a {@link Session} that is connected to an {@link Account} at the
031 * ACME server. It contains the account's {@link KeyPair} and the {@link URL} of the
032 * account.
033 * <p>
034 * Note that {@link Login} objects are not serializable, as they contain a keypair and
035 * volatile data.
036 */
037public class Login {
038
039    private final Session session;
040    private final URL accountLocation;
041    private final Account account;
042    private KeyPair keyPair;
043
044    /**
045     * Creates a new {@link Login}.
046     *
047     * @param accountLocation
048     *            Account location {@link URL}
049     * @param keyPair
050     *            {@link KeyPair} of the account
051     * @param session
052     *            {@link Session} to be used
053     */
054    public Login(URL accountLocation, KeyPair keyPair, Session session) {
055        this.accountLocation = Objects.requireNonNull(accountLocation, "accountLocation");
056        this.keyPair = Objects.requireNonNull(keyPair, "keyPair");
057        this.session = Objects.requireNonNull(session, "session");
058        this.account = new Account(this);
059    }
060
061    /**
062     * Gets the {@link Session} that is used.
063     */
064    public Session getSession() {
065        return session;
066    }
067
068    /**
069     * Gets the {@link KeyPair} of the ACME account.
070     */
071    public KeyPair getKeyPair() {
072        return keyPair;
073    }
074
075    /**
076     * Gets the location {@link URL} of the account.
077     */
078    public URL getAccountLocation() {
079        return accountLocation;
080    }
081
082    /**
083     * Gets the {@link Account} that is bound to this login.
084     *
085     * @return {@link Account} bound to the login
086     */
087    public Account getAccount() {
088        return account;
089    }
090
091    /**
092     * Creates a new instance of {@link Authorization} and binds it to this login.
093     *
094     * @param location
095     *            Location of the Authorization
096     * @return {@link Authorization} bound to the login
097     */
098    public Authorization bindAuthorization(URL location) {
099        return new Authorization(this, requireNonNull(location, "location"));
100    }
101
102    /**
103     * Creates a new instance of {@link Certificate} and binds it to this login.
104     *
105     * @param location
106     *            Location of the Certificate
107     * @return {@link Certificate} bound to the login
108     */
109    public Certificate bindCertificate(URL location) {
110        return new Certificate(this, requireNonNull(location, "location"));
111    }
112
113    /**
114     * Creates a new instance of {@link Order} and binds it to this login.
115     *
116     * @param location
117     *            Location URL of the order
118     * @return {@link Order} bound to the login
119     */
120    public Order bindOrder(URL location) {
121        return new Order(this, requireNonNull(location, "location"));
122    }
123
124    /**
125     * Creates a new instance of {@link Challenge} and binds it to this login.
126     *
127     * @param location
128     *            Location URL of the challenge
129     * @return {@link Challenge} bound to the login
130     * @since 2.8
131     */
132    public Challenge bindChallenge(URL location) {
133        try {
134            Connection connect = session.connect();
135            connect.sendSignedPostAsGetRequest(location, this);
136            return createChallenge(connect.readJsonResponse());
137        } catch (AcmeException ex) {
138            throw new AcmeLazyLoadingException(Challenge.class, location, ex);
139        }
140    }
141
142    /**
143     * Creates a new instance of a challenge and binds it to this login.
144     *
145     * @param location
146     *         Location URL of the challenge
147     * @param type
148     *         Expected challenge type
149     * @return Challenge bound to the login
150     * @throws AcmeProtocolException
151     *         if the challenge found at the location does not match the expected
152     *         challenge type.
153     * @since 2.12
154     */
155    public <C extends Challenge> C bindChallenge(URL location, Class<C> type) {
156        Challenge challenge = bindChallenge(location);
157        if (!type.isInstance(challenge)) {
158            throw new AcmeProtocolException("Challenge type " + challenge.getType()
159                    + " does not match requested class " + type);
160        }
161        return type.cast(challenge);
162    }
163
164    /**
165     * Creates a {@link Challenge} instance for the given challenge data.
166     *
167     * @param data
168     *            Challenge JSON data
169     * @return {@link Challenge} instance
170     */
171    public Challenge createChallenge(JSON data) {
172        Challenge challenge = session.provider().createChallenge(this, data);
173        if (challenge == null) {
174            throw new AcmeProtocolException("Could not create challenge for: " + data);
175        }
176        return challenge;
177    }
178
179    /**
180     * Sets a different {@link KeyPair}.
181     */
182    protected void setKeyPair(KeyPair keyPair) {
183        this.keyPair = Objects.requireNonNull(keyPair, "keyPair");
184    }
185
186}