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 org.assertj.core.api.Assertions.assertThat;
017import static org.junit.jupiter.api.Assertions.assertThrows;
018import static org.mockito.Mockito.*;
019import static org.shredzone.acme4j.toolbox.TestUtils.getJSON;
020import static org.shredzone.acme4j.toolbox.TestUtils.url;
021
022import java.io.IOException;
023import java.net.HttpURLConnection;
024import java.net.URL;
025
026import org.junit.jupiter.api.Test;
027import org.mockito.ArgumentMatchers;
028import org.shredzone.acme4j.challenge.Challenge;
029import org.shredzone.acme4j.challenge.Dns01Challenge;
030import org.shredzone.acme4j.challenge.Http01Challenge;
031import org.shredzone.acme4j.exception.AcmeException;
032import org.shredzone.acme4j.exception.AcmeProtocolException;
033import org.shredzone.acme4j.provider.AcmeProvider;
034import org.shredzone.acme4j.provider.TestableConnectionProvider;
035import org.shredzone.acme4j.toolbox.JSON;
036import org.shredzone.acme4j.toolbox.JSONBuilder;
037import org.shredzone.acme4j.toolbox.TestUtils;
038
039/**
040 * Unit tests for {@link Login}.
041 */
042public class LoginTest {
043
044    private final URL resourceUrl = url("https://example.com/acme/resource/123");
045
046    /**
047     * Test the constructor.
048     */
049    @Test
050    public void testConstructor() throws IOException {
051        var location = url(TestUtils.ACCOUNT_URL);
052        var keypair = TestUtils.createKeyPair();
053        var session = TestUtils.session();
054
055        var login = new Login(location, keypair, session);
056        assertThat(login.getAccountLocation()).isEqualTo(location);
057        assertThat(login.getKeyPair()).isEqualTo(keypair);
058        assertThat(login.getSession()).isEqualTo(session);
059
060        assertThat(login.getAccount()).isNotNull();
061        assertThat(login.getAccount().getLogin()).isEqualTo(login);
062        assertThat(login.getAccount().getLocation()).isEqualTo(location);
063        assertThat(login.getAccount().getSession()).isEqualTo(session);
064    }
065
066    /**
067     * Test the simple binders.
068     */
069    @Test
070    public void testBinder() throws IOException {
071        var location = url(TestUtils.ACCOUNT_URL);
072        var keypair = TestUtils.createKeyPair();
073        var session = TestUtils.session();
074
075        var login = new Login(location, keypair, session);
076
077        var auth = login.bindAuthorization(resourceUrl);
078        assertThat(auth).isNotNull();
079        assertThat(auth.getLogin()).isEqualTo(login);
080        assertThat(auth.getLocation()).isEqualTo(resourceUrl);
081
082        var cert = login.bindCertificate(resourceUrl);
083        assertThat(cert).isNotNull();
084        assertThat(cert.getLogin()).isEqualTo(login);
085        assertThat(cert.getLocation()).isEqualTo(resourceUrl);
086
087        var order = login.bindOrder(resourceUrl);
088        assertThat(order).isNotNull();
089        assertThat(order.getLogin()).isEqualTo(login);
090        assertThat(order.getLocation()).isEqualTo(resourceUrl);
091    }
092
093    /**
094     * Test that the account's keypair can be changed.
095     */
096    @Test
097    public void testKeyChange() throws IOException {
098        var location = url(TestUtils.ACCOUNT_URL);
099        var keypair = TestUtils.createKeyPair();
100        var session = TestUtils.session();
101
102        var login = new Login(location, keypair, session);
103        assertThat(login.getKeyPair()).isEqualTo(keypair);
104
105        var keypair2 = TestUtils.createKeyPair();
106        login.setKeyPair(keypair2);
107        assertThat(login.getKeyPair()).isEqualTo(keypair2);
108    }
109
110    /**
111     * Test that challenges are correctly created via provider.
112     */
113    @Test
114    public void testCreateChallenge() throws Exception {
115        var challengeType = Http01Challenge.TYPE;
116        var challengeUrl = url("https://example.com/acme/authz/0");
117
118        var data = new JSONBuilder()
119                        .put("type", challengeType)
120                        .put("url", challengeUrl)
121                        .toJSON();
122
123        var mockChallenge = mock(Http01Challenge.class);
124        var mockProvider = mock(AcmeProvider.class);
125
126        when(mockProvider.createChallenge(
127                        ArgumentMatchers.any(Login.class),
128                        ArgumentMatchers.eq(data)))
129                .thenReturn(mockChallenge);
130
131        var location = url(TestUtils.ACCOUNT_URL);
132        var keypair = TestUtils.createKeyPair();
133        var session = TestUtils.session(mockProvider);
134
135        var login = new Login(location, keypair, session);
136        var challenge = login.createChallenge(data);
137        assertThat(challenge).isInstanceOf(Http01Challenge.class);
138        assertThat(challenge).isSameAs(mockChallenge);
139
140        verify(mockProvider).createChallenge(login, data);
141    }
142
143    /**
144     * Test that binding to a challenge invokes createChallenge
145     */
146    @Test
147    public void testBindChallenge() throws Exception {
148        var locationUrl = new URL("https://example.com/acme/challenge/1");
149
150        var mockChallenge = mock(Http01Challenge.class);
151        when(mockChallenge.getType()).thenReturn(Http01Challenge.TYPE);
152        var httpChallenge = getJSON("httpChallenge");
153        var provider  = new TestableConnectionProvider() {
154            @Override
155            public int sendSignedPostAsGetRequest(URL url, Login login) {
156                assertThat(url).isEqualTo(locationUrl);
157                return HttpURLConnection.HTTP_OK;
158            }
159
160            @Override
161            public JSON readJsonResponse() {
162                return httpChallenge;
163            }
164
165            @Override
166            public Challenge createChallenge(Login login, JSON json) {
167                assertThat(json).isEqualTo(httpChallenge);
168                return mockChallenge;
169            }
170        };
171
172        var login = provider.createLogin();
173        var challenge = login.bindChallenge(locationUrl);
174        assertThat(challenge).isInstanceOf(Http01Challenge.class);
175        assertThat(challenge).isSameAs(mockChallenge);
176
177        var challenge2 = login.bindChallenge(locationUrl, Http01Challenge.class);
178        assertThat(challenge2).isSameAs(mockChallenge);
179
180        var ex = assertThrows(AcmeProtocolException.class,
181                () -> login.bindChallenge(locationUrl, Dns01Challenge.class));
182        assertThat(ex.getMessage()).isEqualTo("Challenge type http-01 does not match" +
183                " requested class class org.shredzone.acme4j.challenge.Dns01Challenge");
184    }
185
186    /**
187     * Test that a new order can be created.
188     */
189    @Test
190    public void testNewOrder() throws AcmeException, IOException {
191        var provider = new TestableConnectionProvider();
192        var login = provider.createLogin();
193
194        assertThat(login.newOrder()).isNotNull();
195
196        provider.close();
197    }
198
199}