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; 015 016import static java.util.stream.Collectors.toList; 017 018import java.net.URI; 019import java.net.URL; 020import java.time.Duration; 021import java.util.Collection; 022import java.util.Optional; 023import java.util.Set; 024 025import org.shredzone.acme4j.exception.AcmeNotSupportedException; 026import org.shredzone.acme4j.toolbox.JSON; 027import org.shredzone.acme4j.toolbox.JSON.Value; 028 029/** 030 * A collection of metadata related to the CA provider. 031 */ 032public class Metadata { 033 034 private final JSON meta; 035 036 /** 037 * Creates a new {@link Metadata} instance. 038 * 039 * @param meta 040 * JSON map of metadata 041 */ 042 public Metadata(JSON meta) { 043 this.meta = meta; 044 } 045 046 /** 047 * Returns an {@link URI} of the current terms of service, or empty if not available. 048 */ 049 public Optional<URI> getTermsOfService() { 050 return meta.get("termsOfService").map(Value::asURI); 051 } 052 053 /** 054 * Returns an {@link URL} of a website providing more information about the ACME 055 * server. Empty if not available. 056 */ 057 public Optional<URL> getWebsite() { 058 return meta.get("website").map(Value::asURL); 059 } 060 061 /** 062 * Returns a collection of hostnames, which the ACME server recognises as referring to 063 * itself for the purposes of CAA record validation. Empty if not available. 064 */ 065 public Collection<String> getCaaIdentities() { 066 return meta.get("caaIdentities") 067 .asArray() 068 .stream() 069 .map(Value::asString) 070 .collect(toList()); 071 } 072 073 /** 074 * Returns whether an external account is required by this CA. 075 */ 076 public boolean isExternalAccountRequired() { 077 return meta.get("externalAccountRequired").map(Value::asBoolean).orElse(false); 078 } 079 080 /** 081 * Returns whether the CA supports short-term auto-renewal of certificates. 082 * 083 * @since 2.3 084 */ 085 public boolean isAutoRenewalEnabled() { 086 return meta.get("auto-renewal").isPresent(); 087 } 088 089 /** 090 * Returns the minimum acceptable value for the maximum validity of a certificate 091 * before auto-renewal. 092 * 093 * @since 2.3 094 * @throws AcmeNotSupportedException if the server does not support auto-renewal. 095 */ 096 public Duration getAutoRenewalMinLifetime() { 097 return meta.getFeature("auto-renewal") 098 .map(Value::asObject) 099 .orElseGet(JSON::empty) 100 .get("min-lifetime") 101 .asDuration(); 102 } 103 104 /** 105 * Returns the maximum delta between auto-renewal end date and auto-renewal start 106 * date. 107 * 108 * @since 2.3 109 * @throws AcmeNotSupportedException if the server does not support auto-renewal. 110 */ 111 public Duration getAutoRenewalMaxDuration() { 112 return meta.getFeature("auto-renewal") 113 .map(Value::asObject) 114 .orElseGet(JSON::empty) 115 .get("max-duration") 116 .asDuration(); 117 } 118 119 /** 120 * Returns whether the CA also allows to fetch STAR certificates via GET request. 121 * 122 * @since 2.6 123 * @throws AcmeNotSupportedException if the server does not support auto-renewal. 124 */ 125 public boolean isAutoRenewalGetAllowed() { 126 return meta.getFeature("auto-renewal").optional() 127 .map(Value::asObject) 128 .orElseGet(JSON::empty) 129 .get("allow-certificate-get") 130 .optional() 131 .map(Value::asBoolean) 132 .orElse(false); 133 } 134 135 /** 136 * Returns whether the CA supports the profile feature. 137 * 138 * @since 3.5.0 139 * @draft This method is currently based on RFC draft draft-aaron-acme-profiles. It 140 * may be changed or removed without notice to reflect future changes to the draft. 141 * SemVer rules do not apply here. 142 */ 143 public boolean isProfileAllowed() { 144 return meta.get("profiles").isPresent(); 145 } 146 147 /** 148 * Returns whether the CA supports the requested profile. 149 * <p> 150 * Also returns {@code false} if profiles are not allowed in general. 151 * 152 * @since 3.5.0 153 * @draft This method is currently based on RFC draft draft-aaron-acme-profiles. It 154 * may be changed or removed without notice to reflect future changes to the draft. 155 * SemVer rules do not apply here. 156 */ 157 public boolean isProfileAllowed(String profile) { 158 return getProfiles().contains(profile); 159 } 160 161 /** 162 * Returns all profiles supported by the CA. May be empty if the CA does not support 163 * profiles. 164 * 165 * @since 3.5.0 166 * @draft This method is currently based on RFC draft draft-aaron-acme-profiles. It 167 * may be changed or removed without notice to reflect future changes to the draft. 168 * SemVer rules do not apply here. 169 */ 170 public Set<String> getProfiles() { 171 return meta.get("profiles") 172 .optional() 173 .map(Value::asObject) 174 .orElseGet(JSON::empty) 175 .keySet(); 176 } 177 178 /** 179 * Returns a description of the requested profile. This can be a human-readable string 180 * or a URL linking to a documentation. 181 * <p> 182 * Empty if the profile is not allowed. 183 * 184 * @since 3.5.0 185 * @draft This method is currently based on RFC draft draft-aaron-acme-profiles. It 186 * may be changed or removed without notice to reflect future changes to the draft. 187 * SemVer rules do not apply here. 188 */ 189 public Optional<String> getProfileDescription(String profile) { 190 return meta.get("profiles").optional() 191 .map(Value::asObject) 192 .orElseGet(JSON::empty) 193 .get(profile) 194 .optional() 195 .map(Value::asString); 196 } 197 198 /** 199 * Returns whether the CA supports subdomain auth according to RFC9444. 200 * 201 * @since 3.3.0 202 */ 203 public boolean isSubdomainAuthAllowed() { 204 return meta.get("subdomainAuthAllowed").map(Value::asBoolean).orElse(false); 205 } 206 207 /** 208 * Returns the JSON representation of the metadata. This is useful for reading 209 * proprietary metadata properties. 210 */ 211 public JSON getJSON() { 212 return meta; 213 } 214 215}