001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.enforcer.rules;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027import java.util.Objects;
028
029import org.apache.maven.artifact.repository.ArtifactRepository;
030import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
031import org.apache.maven.project.MavenProject;
032
033/**
034 * This rule checks that this project's maven session whether have banned repositories.
035 *
036 * @author <a href="mailto:wangyf2010@gmail.com">Simon Wang</a>
037 */
038@Named("bannedRepositories")
039public final class BannedRepositories extends AbstractStandardEnforcerRule {
040
041    // ----------------------------------------------------------------------
042    // Mojo parameters
043    // ----------------------------------------------------------------------
044
045    /**
046     * Specify explicitly banned non-plugin repositories. This is a list of repository url patterns. Support wildcard
047     * "*".
048     */
049    private List<String> bannedRepositories = Collections.emptyList();
050
051    /**
052     * Specify explicitly banned plugin repositories. This is a list of repository url patterns. Support wildcard "*".
053     */
054    private List<String> bannedPluginRepositories = Collections.emptyList();
055
056    /**
057     * Specify explicitly allowed non-plugin repositories, then all others repositories would be banned. This is a list
058     * of repository url patterns. Support wildcard "*".
059     */
060    private List<String> allowedRepositories = Collections.emptyList();
061
062    /**
063     * Specify explicitly allowed plugin repositories, then all others repositories would be banned. This is a list of
064     * repository url patterns. Support wildcard "*".
065     */
066    private List<String> allowedPluginRepositories = Collections.emptyList();
067
068    private final MavenProject project;
069
070    @Inject
071    public BannedRepositories(MavenProject project) {
072        this.project = Objects.requireNonNull(project);
073    }
074
075    // ----------------------------------------------------------------------
076    // Public methods
077    // ----------------------------------------------------------------------
078
079    @Override
080    public void execute() throws EnforcerRuleException {
081
082        List<ArtifactRepository> resultBannedRepos = checkRepositories(
083                project.getRemoteArtifactRepositories(), this.allowedRepositories, this.bannedRepositories);
084
085        List<ArtifactRepository> resultBannedPluginRepos = checkRepositories(
086                project.getPluginArtifactRepositories(), this.allowedPluginRepositories, this.bannedPluginRepositories);
087
088        String repoErrMsg = populateErrorMessage(resultBannedRepos, " ");
089        String pluginRepoErrMsg = populateErrorMessage(resultBannedPluginRepos, " plugin ");
090
091        String errMsg = repoErrMsg + pluginRepoErrMsg;
092
093        if (errMsg != null && !errMsg.isEmpty()) {
094            throw new EnforcerRuleException(errMsg);
095        }
096    }
097
098    // ----------------------------------------------------------------------
099    // Protected methods
100    // ----------------------------------------------------------------------
101
102    protected void setBannedRepositories(List<String> bannedRepositories) {
103        this.bannedRepositories = bannedRepositories;
104    }
105
106    protected void setAllowedRepositories(List<String> allowedRepositories) {
107        this.allowedRepositories = allowedRepositories;
108    }
109
110    protected void setAllowedPluginRepositories(List<String> allowedPluginRepositories) {
111        this.allowedPluginRepositories = allowedPluginRepositories;
112    }
113
114    // ----------------------------------------------------------------------
115    // Private methods
116    // ----------------------------------------------------------------------
117
118    /**
119     * Check whether specified repositories have banned repositories.
120     *
121     * @param repositories: candidate repositories.
122     * @param includes : 'include' patterns.
123     * @param excludes : 'exclude' patterns.
124     * @return Banned repositories.
125     */
126    private List<ArtifactRepository> checkRepositories(
127            List<ArtifactRepository> repositories, List<String> includes, List<String> excludes) {
128
129        getLog().debug(() -> String.format(
130                "Check repositories: %s, for includes=%s and excludes=%s", repositories, includes, excludes));
131
132        List<ArtifactRepository> bannedRepos = new ArrayList<>();
133
134        for (ArtifactRepository repo : repositories) {
135            String url = repo.getUrl().trim();
136            if (includes.size() > 0 && !match(url, includes)) {
137                bannedRepos.add(repo);
138                continue;
139            }
140
141            if (excludes.size() > 0 && match(url, excludes)) {
142                bannedRepos.add(repo);
143            }
144        }
145
146        return bannedRepos;
147    }
148
149    private boolean match(String url, List<String> patterns) {
150        for (String pattern : patterns) {
151            if (this.match(url, pattern)) {
152                return true;
153            }
154        }
155
156        return false;
157    }
158
159    private boolean match(String text, String pattern) {
160        return text.matches(pattern.replace("?", ".?").replace("*", ".*?"));
161    }
162
163    private String populateErrorMessage(List<ArtifactRepository> resultBannedRepos, String errorMessagePrefix) {
164        StringBuffer errMsg = new StringBuffer("");
165        if (!resultBannedRepos.isEmpty()) {
166            errMsg.append("Current maven session contains banned" + errorMessagePrefix
167                    + "repository urls, please double check your pom or settings.xml:" + System.lineSeparator()
168                    + getRepositoryUrlString(resultBannedRepos) + System.lineSeparator() + System.lineSeparator());
169        }
170
171        return errMsg.toString();
172    }
173
174    private String getRepositoryUrlString(List<ArtifactRepository> resultBannedRepos) {
175        StringBuilder urls = new StringBuilder("");
176        for (ArtifactRepository repo : resultBannedRepos) {
177            urls.append(repo.getId() + " - " + repo.getUrl() + System.lineSeparator());
178        }
179        return urls.toString();
180    }
181
182    @Override
183    public String toString() {
184        return String.format(
185                "BannedRepositories[bannedRepositories=%s, bannedPluginRepositories=%s, allowedRepositories=%s, allowedPluginRepositories=%s",
186                bannedRepositories, bannedPluginRepositories, allowedRepositories, allowedPluginRepositories);
187    }
188}