Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request : adding <server> blocks with credentials copied from existing ones in global settings #6

Closed
UnasZole opened this issue Oct 26, 2023 · 5 comments

Comments

@UnasZole
Copy link

UnasZole commented Oct 26, 2023

Hi,

Context

My use case is the following :

  • My company uses an internal mirror of maven central, which requires authentication.
  • All users and CI build agents have a settings.xml that defines this <mirror>, as well as a <server> block with the corresponding credentials.
  • Internal projects can also create separate repositories to publish their artifacts, that require the same authentication credentials.

I would like to be able to use these separate repositories easily, by just adding a <repository> entry in my POM.
Unfortunately, maven does not allow me to map this new <repository> in the pom to an existing <server> without overriding the original <mirror> or <repository> declaration.

That's because maven made the (in my opinion, absurd) decision to use "repository.id" BOTH as a unique identifier for local cache AND as a key to retrieve credentials from the global settings.

  • If I give the ID "central" or the global mirror's ID to the repository defined in the pom, then I get the correct authentication, but maven completely overrides the repository definition - it will no longer find any artifact from maven central.
  • And if I give any other ID, then I don't get any authentication, unless my hundred of users and build agents update their settings with an additional <server> entry just for my project.

Your extension is a great opportunity to improve this !

Description of the feature

The required functionality would be to define additional <server> entries within the project settings.xml, but have the username/password field be references to an existing <server> entry from the global settings.

For example, my project settings could contain :

<?xml version="1.0" encoding="utf-8"?>
<settings
    xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
	<servers>
		<server>
			<id>myCustomProjectRepo</id>
			<username>companyMirror#username</username>
			<password>companyMirror#password</password>
		</server>
	</servers>
</settings>

(Then I could define the myCustomProjectRepo repository either in the same project settings.xml, or directly in the pom, and benefit from authentication, without requiring my users to update their personal settings.xml)

If you'd agree to integrate such a feature, let me know - I'd be happy to contribute a PR !

@gzm55
Copy link
Owner

gzm55 commented Oct 26, 2023

hi @UnasZole , according to apache/maven#1059 , maven 4.0 is having supported the project setting function, and also remove the project server.username/password for the info secure cause. even your purpose works, it will only works for maven 3 users, and fail the 4.0 users.

what about set combine.self="merge" on the tag?

@UnasZole
Copy link
Author

I don't understand, how do you suggest using combine.self ? Where would you put it ?

My goal is to define a custom repository for the project, while referencing existing credentials from the global settings.xml.
Unless maven provides another field than repository.id to link the repository to the credentials, then I need a way to copy the contents of a "server" block from the global settings into a new "server" block with a different ID in the same settings.

Hopefully Maven 4.0 will provide an actually good mechanism to do that, but my objective here is to have something that works in maven 3.

@gzm55
Copy link
Owner

gzm55 commented Oct 26, 2023

Now the extension behavior is, when you set in project settings:

server.id=companyMirror
server.url=myCustomProjectRepo-url

the effective settings for building would be:

server.id=companyMirror
server.url=myCustomProjectRepo-url
server.username=<username from user level settings.xml>
server.password=<password from user level settings.xml>

This is a deep merge of two settings.xml, @UnasZole could it help in your context?

@UnasZole
Copy link
Author

I'm sorry, but I still don't understand what you mean.

First, as far as I know, there is no "url" in "server".
The URL belongs within the "repository" element, not "server" - and the "repository.id" of the repository is matched with "server.id" to select a server definition with the credentials.

To sum up :

  • The user settings.xml contains
    mirrors[0].id=companyMirror
    mirrors[0].url=https://companyMirrorUrl
    mirrors[0].mirrorOf=central
    servers[0].id=companyMirror
    servers[0].username=aaa
    servers[0].password=bbb

  • The project contains
    repositories[0].id=myCustomProjectRepo
    repositories[0].url=https://customRepoUrl

The repository.id cannot be set to "companyMirror", because then Maven will mix up the local caches from both repositories (otherwise I wouldn't have the problem in the first place, and wouldn't need to resort to a plugin generating a new settings file).

I need a plugin that can generate a settings.xml file which would contain
servers[1].id=myCustomProjectRepo
servers[1].username=aaa (copied from the companyMirror server)
servers[1].password=bbb (copied from the companyMirror server)

@UnasZole
Copy link
Author

UnasZole commented Nov 6, 2023

Hi,

After studying on my side, in fact it's a lot simpler to build my own extension defining just a ConfigurationProcessor, that reads a properties file in .mvn to know which server definitions it has to clone and under which new name.

The code is as simple as :

@Named
public class CredentialsHelper implements ConfigurationProcessor {

    private static final Logger LOG = LoggerFactory.getLogger(CredentialsHelper.class);

    public void cloneServer(MavenExecutionRequest exec, String sourceId, String targetId) {
        Optional<Server> source = exec.getServers().stream().filter(s -> sourceId.equals(s.getId())).findFirst();

        if(!source.isPresent()) {
            LOG.warn("Did not find any server with ID '{}'", sourceId);
            return;
        }

        Server newServer = source.get().clone();
        newServer.setId(targetId);
        exec.addServer(newServer);
        LOG.info("Successfully cloned server definition '{}' into '{}'", sourceId, targetId);
    }

    @Override
    public void process(CliRequest cliRequest) throws Exception {
        MavenExecutionRequest exec = cliRequest.getRequest();

        File cloneDefsFile = new File(cliRequest.getMultiModuleProjectDirectory(), ".mvn/auth-clone-server.properties");
        if(cloneDefsFile.exists()) {
            LOG.info("Reading authentication clone file at {}", cloneDefsFile);
            try(FileInputStream fis = new FileInputStream(cloneDefsFile)) {
                Properties cloneDefs = new Properties();
                cloneDefs.load(fis);

                for(Map.Entry<Object, Object> cloneDef: cloneDefs.entrySet()) {
                    cloneServer(exec, cloneDef.getValue().toString(), cloneDef.getKey().toString());
                }
            }
        }
        else {
            LOG.info("No authentication clone file found at {}", cloneDefsFile);
        }
    }
}

So this ticket can be closed !

@UnasZole UnasZole closed this as completed Nov 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants