View Javadoc
1   /*
2    * Copyright (c) 2012-2022, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.simpledb;
31  
32  import com.amazonaws.auth.AWSStaticCredentialsProvider;
33  import com.amazonaws.auth.BasicAWSCredentials;
34  import com.amazonaws.client.builder.AwsClientBuilder;
35  import com.amazonaws.regions.Regions;
36  import com.amazonaws.services.simpledb.AmazonSimpleDB;
37  import com.amazonaws.services.simpledb.AmazonSimpleDBClientBuilder;
38  import com.jcabi.aspects.Immutable;
39  import com.jcabi.aspects.Loggable;
40  import javax.validation.constraints.NotNull;
41  import lombok.EqualsAndHashCode;
42  import org.apache.commons.lang3.Validate;
43  
44  /**
45   * Amazon SimpleDB credentials.
46   *
47   * <p>It is recommended to use {@link Credentials.Simple} in most cases.
48   *
49   * @since 0.1
50   */
51  @Immutable
52  public interface Credentials {
53  
54      /**
55       * Test credentials, for unit testing mostly.
56       */
57      Credentials TEST = new Credentials.Simple(
58          "AAAAAAAAAAAAAAAAAAAA",
59          "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
60      );
61  
62      /**
63       * Build AWS client.
64       *
65       * @return Amazon Dynamo DB client
66       */
67      @NotNull
68      AmazonSimpleDB aws();
69  
70      /**
71       * Simple implementation.
72       *
73       * @since 0.1
74       */
75      @Immutable
76      @Loggable(Loggable.DEBUG)
77      @EqualsAndHashCode(of = { "key", "secret", "region" })
78      final class Simple implements Credentials {
79          /**
80           * AWS key.
81           */
82          private final transient String key;
83  
84          /**
85           * AWS secret.
86           */
87          private final transient String secret;
88  
89          /**
90           * Region name.
91           */
92          private final transient String region;
93  
94          /**
95           * Public ctor, with "us-east-1" region.
96           * @param akey AWS key
97           * @param scrt Secret
98           */
99          public Simple(@NotNull final String akey, @NotNull final String scrt) {
100             this(akey, scrt, Regions.US_EAST_1.getName());
101         }
102 
103         /**
104          * Public ctor.
105          * @param akey AWS key
106          * @param scrt Secret
107          * @param reg Region
108          */
109         @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors")
110         public Simple(
111             @NotNull(message = "AWS key can't be NULL") final String akey,
112             @NotNull(message = "AWS secret can't be NULL") final String scrt,
113             @NotNull(message = "AWS region can't be NULL") final String reg) {
114             Validate.matchesPattern(
115                 akey, "[A-Z0-9]{20}",
116                 "Invalid AWS key '%s'", akey
117             );
118             this.key = akey;
119             Validate.matchesPattern(
120                 scrt, "[a-zA-Z0-9+/=]{40}",
121                 "Invalid AWS secret key '%s'", scrt
122             );
123             this.secret = scrt;
124             Validate.matchesPattern(
125                 reg, "[-a-z0-9]+",
126                 "Invalid AWS region name '%s'", reg
127             );
128             this.region = reg;
129         }
130 
131         @Override
132         public String toString() {
133             return String.format("%s/%s", this.region, this.key);
134         }
135 
136         @Override
137         @NotNull
138         public AmazonSimpleDB aws() {
139             return AmazonSimpleDBClientBuilder.standard()
140                 .withRegion(this.region)
141                 .withCredentials(
142                     new AWSStaticCredentialsProvider(
143                         new BasicAWSCredentials(this.key, this.secret)
144                     )
145                 ).build();
146         }
147     }
148 
149     /**
150      * Assumed AWS IAM role.
151      *
152      * @since 0.1
153      * @see <a href="http://docs.aws.amazon.com/IAM/latest/UserGuide/role-usecase-ec2app.html">Granting Applications that Run on Amazon EC2 Instances Access to AWS Resources</a>
154      */
155     @Immutable
156     @Loggable(Loggable.DEBUG)
157     @EqualsAndHashCode(of = "region")
158     final class Assumed implements Credentials {
159         /**
160          * Region name.
161          */
162         private final transient String region;
163 
164         /**
165          * Public ctor.
166          */
167         public Assumed() {
168             this(Regions.US_EAST_1.getName());
169         }
170 
171         /**
172          * Public ctor.
173          * @param reg Region
174          */
175         @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors")
176         public Assumed(@NotNull(message = "SimpleDB region can't be NULL")
177             final String reg) {
178             Validate.matchesPattern(
179                 reg, "[-0-9a-z]+",
180                 "Invalid AWS region name: '%s'", reg
181             );
182             this.region = reg;
183         }
184 
185         @Override
186         public String toString() {
187             return this.region;
188         }
189 
190         @Override
191         @NotNull
192         public AmazonSimpleDB aws() {
193             return AmazonSimpleDBClientBuilder.standard()
194                 .withRegion(this.region)
195                 .build();
196         }
197     }
198 
199     /**
200      * With explicitly specified endpoint.
201      *
202      * @since 0.1
203      */
204     @Immutable
205     @Loggable(Loggable.DEBUG)
206     @EqualsAndHashCode(of = { "origin", "endpoint" })
207     final class Direct implements Credentials {
208         /**
209          * Original credentials.
210          */
211         private final transient Credentials.Simple origin;
212 
213         /**
214          * Endpoint.
215          */
216         private final transient String endpoint;
217 
218         /**
219          * Public ctor.
220          * @param creds Original credentials
221          * @param pnt Endpoint
222          */
223         public Direct(
224             @NotNull(message = "credentials is NULL") final Credentials.Simple creds,
225             @NotNull(message = "end point can't be NULL") final String pnt) {
226             this.origin = creds;
227             this.endpoint = pnt;
228         }
229 
230         /**
231          * Public ctor.
232          * @param creds Original credentials
233          * @param port Port number for localhost
234          */
235         public Direct(@NotNull final Credentials.Simple creds, final int port) {
236             this(creds, String.format("http://localhost:%d", port));
237         }
238 
239         @Override
240         public String toString() {
241             return String.format("%s at %s", this.origin, this.endpoint);
242         }
243 
244         @Override
245         @NotNull
246         public AmazonSimpleDB aws() {
247             return AmazonSimpleDBClientBuilder.standard()
248                 .withEndpointConfiguration(
249                     new AwsClientBuilder.EndpointConfiguration(
250                         this.endpoint, Regions.US_EAST_1.getName()
251                     )
252                 )
253                 .withCredentials(
254                     new AWSStaticCredentialsProvider(
255                         new BasicAWSCredentials(
256                             this.origin.key, this.origin.secret
257                         )
258                     )
259                 )
260                 .build();
261         }
262     }
263 
264 }