View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.resource.maven.repository;
17  
18  import java.io.BufferedInputStream;
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.List;
25  import java.util.logging.Logger;
26  
27  import org.apache.commons.io.IOUtils;
28  import org.apache.maven.settings.Mirror;
29  import org.apache.maven.settings.Server;
30  import org.apache.maven.settings.Settings;
31  import org.apache.maven.settings.io.xpp3.SettingsXpp3Reader;
32  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
33  import org.sonatype.aether.graph.DependencyFilter;
34  import org.sonatype.aether.repository.Authentication;
35  import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
36  import org.sonatype.plexus.components.cipher.PlexusCipherException;
37  import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
38  import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
39  import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
40  
41  import de.smartics.properties.resource.repository.RepositoryException;
42  
43  /**
44   * Builds {@link MavenRepository} based on information found in a
45   * <code>settings.xml</code>.
46   */
47  final class SettingsMavenRepoBuilder
48  {
49    // ********************************* Fields *********************************
50  
51    // --- constants ------------------------------------------------------------
52  
53    // --- members --------------------------------------------------------------
54  
55    /**
56     * Reference to the logger for this instance.
57     */
58    private final Logger log = Logger.getLogger(SettingsMavenRepoBuilder.class
59        .getName());
60  
61    // ****************************** Initializer *******************************
62  
63    // ****************************** Constructors ******************************
64  
65    // ****************************** Inner Classes *****************************
66  
67    // ********************************* Methods ********************************
68  
69    // --- init -----------------------------------------------------------------
70  
71    // --- get&set --------------------------------------------------------------
72  
73    // --- business -------------------------------------------------------------
74  
75    /**
76     * Creates an instance of the maven repository based on the given settings
77     * file.
78     *
79     * @param settingsName the file name of the <code>settings.xml</code> file to
80     *          read.
81     * @return the instance.
82     * @throws RepositoryException if the settings cannot be read to create an
83     *           instance of a resource repository successfully.
84     */
85    public MavenRepository build(final String settingsName,
86        final List<DependencyFilter> filters) throws RepositoryException
87    {
88      final File settingsFile = new File(settingsName);
89      if (settingsFile.canRead())
90      {
91        InputStream input = null;
92  
93        try
94        {
95          input = new BufferedInputStream(new FileInputStream(settingsFile));
96          final SettingsXpp3Reader reader = new SettingsXpp3Reader();
97          final Settings settings = reader.read(input);
98          return createRepository(settingsFile, settings, filters);
99        }
100       catch (final FileNotFoundException e)
101       {
102         throw new RepositoryException(new FileMessageBean(
103             MavenResourceCode.CANNOT_FIND_SETTINGS, e, settingsFile));
104       }
105       catch (final IOException e)
106       {
107         throw new RepositoryException(new FileMessageBean(
108             MavenResourceCode.CANNOT_READ_SETTINGS, e, settingsFile));
109       }
110       catch (final XmlPullParserException e)
111       {
112         throw new RepositoryException(new FileMessageBean(
113             MavenResourceCode.CANNOT_READ_SETTINGS, e, settingsFile));
114       }
115       finally
116       {
117         IOUtils.closeQuietly(input);
118       }
119     }
120     throw new RepositoryException(new FileMessageBean(
121         MavenResourceCode.CANNOT_FIND_SETTINGS, settingsFile));
122   }
123 
124   private MavenRepository createRepository(final File settingsFile,
125       final Settings settings, final List<DependencyFilter> filters)
126   {
127     final String localRepositoryPath = settings.getLocalRepository();
128     final List<Mirror> mirrors = settings.getMirrors();
129     final String remoteRepositoryUrl;
130     Authentication auth = null;
131     if (!mirrors.isEmpty())
132     {
133       final Mirror mirror = mirrors.get(0);
134       remoteRepositoryUrl = mirror.getUrl();
135       final Server server = settings.getServer(mirror.getId());
136       if (server != null)
137       {
138         final String password =
139             decodePassword(settingsFile, server.getPassword());
140         auth =
141             new Authentication(server.getUsername(), password,
142                 server.getPrivateKey(), server.getPassphrase());
143       }
144     }
145     else
146     {
147       remoteRepositoryUrl = null;
148     }
149 
150     final boolean offline = settings.isOffline();
151 
152     return new MavenRepository(remoteRepositoryUrl, auth, localRepositoryPath,
153         filters, offline);
154   }
155 
156   private File createSettingsSecurityFile(final File settingsFile)
157   {
158     final File file =
159         new File(settingsFile.getParentFile(), "settings-security.xml");
160     return file;
161   }
162 
163   private String decodePassword(final File settingsFile,
164       final String possiblyEncodedPassword)
165   {
166     if (!isEncoded(possiblyEncodedPassword))
167     {
168       return possiblyEncodedPassword;
169     }
170 
171     final File settingsSecurityFile = createSettingsSecurityFile(settingsFile);
172 
173     if (settingsSecurityFile.exists())
174     {
175       try
176       {
177         final SettingsSecurity security =
178             SecUtil.read(settingsSecurityFile.getAbsolutePath(), true);
179         final String encodedMasterPassword = security.getMaster();
180         final String plainTextMasterPassword =
181             decodePassword(encodedMasterPassword,
182                 DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION);
183         final String decodedPassword =
184             decodePassword(possiblyEncodedPassword, plainTextMasterPassword);
185         return decodedPassword;
186       }
187       catch (final Exception e)
188       {
189         log.warning("Cannot decrypt passwords in settings.xml:"
190                     + e.getMessage());
191       }
192     }
193     return possiblyEncodedPassword;
194   }
195 
196   private boolean isEncoded(final String possiblyEncodedPassword)
197   {
198     if (possiblyEncodedPassword == null)
199     {
200       return false;
201     }
202 
203     final int length = possiblyEncodedPassword.length();
204     return length > 2 && possiblyEncodedPassword.charAt(0) == '{'
205            && possiblyEncodedPassword.charAt(length - 1) == '}';
206   }
207 
208   private static String decodePassword(final String encodedPassword,
209       final String key) throws PlexusCipherException
210   {
211     final DefaultPlexusCipher cipher = new DefaultPlexusCipher();
212     return cipher.decryptDecorated(encodedPassword, key);
213   }
214 
215   // --- object basics --------------------------------------------------------
216 
217 }