View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.transport.minio;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  
24  import java.util.Map;
25  
26  import org.eclipse.aether.RepositorySystemSession;
27  import org.eclipse.aether.repository.RemoteRepository;
28  import org.eclipse.aether.spi.connector.transport.Transporter;
29  import org.eclipse.aether.spi.connector.transport.TransporterFactory;
30  import org.eclipse.aether.spi.io.PathProcessor;
31  import org.eclipse.aether.transfer.NoTransporterException;
32  import org.eclipse.aether.util.ConfigUtils;
33  
34  import static java.util.Objects.requireNonNull;
35  
36  /**
37   * A transporter factory for repositories using the S3 API object storage using Minio.
38   *
39   * @since 2.0.2
40   */
41  @Named(MinioTransporterFactory.NAME)
42  public final class MinioTransporterFactory implements TransporterFactory {
43      public static final String NAME = "minio";
44  
45      private static final float DEFAULT_PRIORITY = 0.0f;
46  
47      private float priority = DEFAULT_PRIORITY;
48  
49      private final Map<String, ObjectNameMapperFactory> objectNameMapperFactories;
50  
51      private final PathProcessor pathProcessor;
52  
53      @Inject
54      public MinioTransporterFactory(
55              Map<String, ObjectNameMapperFactory> objectNameMapperFactories, PathProcessor pathProcessor) {
56          this.objectNameMapperFactories = requireNonNull(objectNameMapperFactories, "objectNameMapperFactories");
57          this.pathProcessor = requireNonNull(pathProcessor, "pathProcessor");
58      }
59  
60      @Override
61      public float getPriority() {
62          return priority;
63      }
64  
65      public MinioTransporterFactory setPriority(float priority) {
66          this.priority = priority;
67          return this;
68      }
69  
70      @Override
71      public Transporter newInstance(RepositorySystemSession session, RemoteRepository repository)
72              throws NoTransporterException {
73          requireNonNull(session, "session cannot be null");
74          requireNonNull(repository, "repository cannot be null");
75  
76          // this check is here only to support "minio+http" and "s3+http" protocols by default. But also when
77          // raised priorities by user, allow to "overtake" plain HTTP repositories, if needed.
78          RemoteRepository adjusted = repository;
79          boolean prefixStripped = false;
80          if ("minio+http".equalsIgnoreCase(repository.getProtocol())
81                  || "minio+https".equalsIgnoreCase(repository.getProtocol())) {
82              adjusted = new RemoteRepository.Builder(repository)
83                      .setUrl(repository.getUrl().substring("minio+".length()))
84                      .build();
85              prefixStripped = true;
86          } else if ("s3+http".equalsIgnoreCase(repository.getProtocol())
87                  || "s3+https".equalsIgnoreCase(repository.getProtocol())) {
88              adjusted = new RemoteRepository.Builder(repository)
89                      .setUrl(repository.getUrl().substring("s3+".length()))
90                      .build();
91              prefixStripped = true;
92          } else if (priority == DEFAULT_PRIORITY) {
93              throw new NoTransporterException(
94                      repository,
95                      "To use Minio transport with plain HTTP/HTTPS repositories, increase the Minio transport priority");
96          }
97          // Plaintext endpoints from "minio+http"/"s3+http" URLs are not matched by "external:http:*" mirror
98          // blocking (the repository protocol string is not "http") and offer no transport integrity: artifacts
99          // and their checksum objects travel over the same tamperable channel. Refuse them unless the operator
100         // explicitly opted in.
101         if (prefixStripped && "http".equalsIgnoreCase(adjusted.getProtocol())) {
102             boolean allowInsecureProtocol = ConfigUtils.getBoolean(
103                     session,
104                     MinioTransporterConfigurationKeys.DEFAULT_ALLOW_INSECURE_PROTOCOL,
105                     MinioTransporterConfigurationKeys.CONFIG_PROP_ALLOW_INSECURE_PROTOCOL + "." + repository.getId(),
106                     MinioTransporterConfigurationKeys.CONFIG_PROP_ALLOW_INSECURE_PROTOCOL);
107             if (!allowInsecureProtocol) {
108                 throw new NoTransporterException(
109                         repository,
110                         "Plaintext HTTP object storage endpoints are insecure (no transport integrity; not subject"
111                                 + " to 'external:http:*' mirror blocking) and are refused by default: use the"
112                                 + " 'minio+https'/'s3+https' URL form, or explicitly opt in by setting the"
113                                 + " configuration property '"
114                                 + MinioTransporterConfigurationKeys.CONFIG_PROP_ALLOW_INSECURE_PROTOCOL
115                                 + "' (optionally suffixed with '." + repository.getId() + "') to 'true'");
116             }
117         }
118         String objectNameMapperConf = ConfigUtils.getString(
119                 session,
120                 MinioTransporterConfigurationKeys.DEFAULT_OBJECT_NAME_MAPPER,
121                 MinioTransporterConfigurationKeys.CONFIG_PROP_OBJECT_NAME_MAPPER + "." + repository.getId(),
122                 MinioTransporterConfigurationKeys.CONFIG_PROP_OBJECT_NAME_MAPPER);
123         ObjectNameMapperFactory objectNameMapperFactory = objectNameMapperFactories.get(objectNameMapperConf);
124         if (objectNameMapperFactory == null) {
125             throw new IllegalArgumentException("Unknown object name mapper configured '" + objectNameMapperConf
126                     + "' for repository " + repository.getId());
127         }
128         return new MinioTransporter(session, adjusted, objectNameMapperFactory, pathProcessor);
129     }
130 }