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

[JENKINS-73061] allow users with Overall/Manage to configure global plugin options #423

Merged
merged 1 commit into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>4.78</version>
<version>4.81</version>
<relativePath />
</parent>
<groupId>io.jenkins.plugins</groupId>
Expand Down Expand Up @@ -35,6 +35,8 @@
<gitHubRepo>jenkinsci/${project.artifactId}-plugin</gitHubRepo>
<spotless.check.skip>false</spotless.check.skip>
<hpi.compatibleSinceVersion>685</hpi.compatibleSinceVersion>
<!-- Jenkins.MANAGE is still considered beta -->
<useBeta>true</useBeta>
</properties>

<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,30 +512,30 @@
public ListBoxModel doFillServerNameItems(
@AncestorInPath SCMSourceOwner context, @QueryParameter String serverName) {
if (context == null) {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
if (!Jenkins.get().hasPermission(Jenkins.MANAGE)) {
// must have admin if you want the list without a context
ListBoxModel result = new ListBoxModel();
result.add(serverName);
return result;
}
} else {
if (!context.hasPermission(Item.EXTENDED_READ)) {
// must be able to read the configuration the list
ListBoxModel result = new ListBoxModel();
result.add(serverName);
return result;
}
}
return GitLabServers.get().getServerItems();
}

public ListBoxModel doFillCredentialsIdItems(
@AncestorInPath SCMSourceOwner context,
@QueryParameter String serverName,
@QueryParameter String credentialsId) {
StandardListBoxModel result = new StandardListBoxModel();
if (context == null) {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
if (!Jenkins.get().hasPermission(Jenkins.MANAGE)) {

Check warning on line 538 in src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 515-538 are not covered by tests
// must have admin if you want the list without a context
result.includeCurrentValue(credentialsId);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,31 +819,31 @@
public ListBoxModel doFillServerNameItems(
@AncestorInPath SCMSourceOwner context, @QueryParameter String serverName) {
if (context == null) {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
if (!Jenkins.get().hasPermission(Jenkins.MANAGE)) {
// must have admin if you want the list without a context
ListBoxModel result = new ListBoxModel();
result.add(serverName);
return result;
}
} else {
if (!context.hasPermission(Item.EXTENDED_READ)) {
// must be able to read the configuration the list
ListBoxModel result = new ListBoxModel();
result.add(serverName);
return result;
}
}
return GitLabServers.get().getServerItems();
}

public ListBoxModel doFillCredentialsIdItems(
@AncestorInPath SCMSourceOwner context,
@QueryParameter String serverName,
@QueryParameter String credentialsId) {
StandardListBoxModel result = new StandardListBoxModel();
if (context == null) {
// must have admin if you want the list without a context
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
if (!Jenkins.get().hasPermission(Jenkins.MANAGE)) {

Check warning on line 846 in src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 822-846 are not covered by tests
result.includeCurrentValue(credentialsId);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
@QueryParameter String credentialsId) {
StandardListBoxModel result = new StandardListBoxModel();
if (context == null) {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
if (!Jenkins.get().hasPermission(Jenkins.MANAGE)) {

Check warning on line 91 in src/main/java/io/jenkins/plugins/gitlabbranchsource/SSHCheckoutTrait.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 91 is not covered by tests
// must have admin if you want the list without a context
result.includeCurrentValue(credentialsId);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@

@RequirePOST
public HttpResponse doServerList() {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
if (!Jenkins.get().hasPermission(Jenkins.MANAGE)) {
return HttpResponses.errorJSON("no permission to get Gitlab server list");
}

JSONArray servers = new JSONArray();
GitLabServers.get().getServers().forEach(server -> {
JSONObject serverObj = new JSONObject();
serverObj.put("name", server.getName());
serverObj.put("url", server.getServerUrl());
servers.add(serverObj);
});

return HttpResponses.okJSON(servers);
}

@RequirePOST
public HttpResponse doProjectList(
@AncestorInPath SCMSourceOwner context, @QueryParameter String server, @QueryParameter String owner) {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
if (!Jenkins.get().hasPermission(Jenkins.MANAGE)) {

Check warning on line 55 in src/main/java/io/jenkins/plugins/gitlabserverconfig/action/GitlabAction.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 37-55 are not covered by tests
return HttpResponses.errorJSON("no permission to get Gitlab server list");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,165 +507,165 @@
*/
@POST
public static FormValidation doCheckServerUrl(@QueryParameter String serverUrl) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
Jenkins.get().checkPermission(Jenkins.MANAGE);
try {
new URL(serverUrl);
} catch (MalformedURLException e) {
LOGGER.log(Level.SEVERE, String.format("Incorrect url: %s", serverUrl));
return FormValidation.error("Malformed url (%s)", e.getMessage());
}
if (GITLAB_SERVER_URL.equals(serverUrl)) {
LOGGER.log(Level.FINEST, String.format("Community version of GitLab: %s", serverUrl));
}
GitLabApi gitLabApi = new GitLabApi(serverUrl, "", null, getProxyConfig(serverUrl));
try {
gitLabApi.getProjectApi().getProjects(1, 1);
return FormValidation.ok();
} catch (GitLabApiException e) {
LOGGER.log(Level.FINEST, String.format("Invalid GitLab Server Url: %s", serverUrl));
return FormValidation.error(Messages.GitLabServer_invalidUrl(serverUrl));
}
}

/**
* Checks that the supplied URL looks like a valid Jenkins root URL.
*
* @param hooksRootUrl the URL to check.
* @return the validation results.
*/
public static FormValidation doCheckHooksRootUrl(@QueryParameter String hooksRootUrl) {
if (StringUtils.isBlank(hooksRootUrl)) {
return FormValidation.ok();
}
try {
new URL(hooksRootUrl);
} catch (MalformedURLException e) {
LOGGER.log(Level.FINEST, "Malformed hooks root URL: {0}", hooksRootUrl);
return FormValidation.error("Malformed url (%s)", e.getMessage());
}
if (hooksRootUrl.endsWith("/post")
|| hooksRootUrl.contains("/gitlab-webhook")
|| hooksRootUrl.contains("/gitlab-systemhook")) {
LOGGER.log(Level.FINEST, "Dubious hooks root URL: {0}", hooksRootUrl);
return FormValidation.warning("This looks like a full webhook URL, it should only be a root URL.");
}
return FormValidation.ok();
}

/**
* Checks that the supplied hook trigger delay is valid.
*
* @param hookTriggerDelay the delay to be checked.
* @return the validation results.
*/
public static FormValidation doCheckHookTriggerDelay(@QueryParameter String hookTriggerDelay) {
try {
if (!hookTriggerDelay.isEmpty()) {
Integer.parseInt(hookTriggerDelay);
}
return FormValidation.ok();
} catch (NumberFormatException e) {
LOGGER.log(Level.FINEST, "Invalid hook trigger delay: {0}", hookTriggerDelay);
return FormValidation.error("Invalid hook trigger delay (%s)", e.getMessage());
}
}

@NonNull
@Override
public String getDisplayName() {
return Messages.GitLabServer_displayName();
}

@RequirePOST
@Restricted(DoNotUse.class)
@SuppressWarnings("unused")
public FormValidation doTestConnection(@QueryParameter String serverUrl, @QueryParameter String credentialsId) {
StandardCredentials credentials = getCredentials(serverUrl, credentialsId);
String privateToken = getPrivateTokenAsPlainText(credentials);
if (privateToken.equals(EMPTY_TOKEN)) {
GitLabApi gitLabApi = new GitLabApi(serverUrl, EMPTY_TOKEN, null, getProxyConfig(serverUrl));
try {
/*
* In order to validate a GitLab Server without personal access token,
* we are fetching 1 project from the GitLab Server. If no project exists,
* it returns an empty list. If no server exists at the specified endpoint,
* it raises GitLabAPIException.
*/
gitLabApi.getProjectApi().getProjects(1, 1);
return FormValidation.ok("Valid GitLab Server but no credentials specified");
} catch (GitLabApiException e) {
LOGGER.log(Level.SEVERE, "Invalid GitLab Server Url");
return FormValidation.errorWithMarkup(
Messages.GitLabServer_credentialsNotResolved(Util.escape(credentialsId)));
}
} else {
GitLabApi gitLabApi = new GitLabApi(serverUrl, privateToken, null, getProxyConfig(serverUrl));
try {
User user = gitLabApi.getUserApi().getCurrentUser();
LOGGER.log(
Level.FINEST,
String.format("Connection established with the GitLab Server for %s", user.getUsername()));
return FormValidation.ok(String.format("Credentials verified for user %s", user.getUsername()));
} catch (GitLabApiException e) {
LOGGER.log(
Level.SEVERE, String.format("Failed to connect with GitLab Server - %s", e.getMessage()));
return FormValidation.error(e, Messages.GitLabServer_failedValidation(Util.escape(e.getMessage())));
}
}
}

/**
* Stapler form completion.
*
* @param serverUrl the server URL.
* @param credentialsId the credentials Id
* @return the available credentials.
*/
@Restricted(NoExternalUse.class) // stapler
@SuppressWarnings("unused")
public ListBoxModel doFillCredentialsIdItems(
@QueryParameter String serverUrl, @QueryParameter String credentialsId) {
Jenkins jenkins = Jenkins.get();
if (!jenkins.hasPermission(Jenkins.ADMINISTER)) {
if (!jenkins.hasPermission(Jenkins.MANAGE)) {
return new StandardListBoxModel().includeCurrentValue(credentialsId);
}
return new StandardListBoxModel()
.includeEmptyValue()
.includeMatchingAs(
ACL.SYSTEM,
jenkins,
StandardCredentials.class,
fromUri(serverUrl).build(),
CREDENTIALS_MATCHER);
}

/**
* Stapler form completion.
*
* @param webhookSecretCredentialsId the webhook secret credentials Id
* @return the available credentials.
*/
@Restricted(NoExternalUse.class) // stapler
@SuppressWarnings("unused")
public ListBoxModel doFillWebhookSecretCredentialsIdItems(
@QueryParameter String serverUrl, @QueryParameter String webhookSecretCredentialsId) {
Jenkins jenkins = Jenkins.get();
if (!jenkins.hasPermission(Jenkins.ADMINISTER)) {
if (!jenkins.hasPermission(Jenkins.MANAGE)) {
return new StandardListBoxModel().includeCurrentValue(webhookSecretCredentialsId);
}
return new StandardListBoxModel()
.includeEmptyValue()
.includeMatchingAs(
ACL.SYSTEM,
jenkins,
StringCredentials.class,
fromUri(serverUrl).build(),
WEBHOOK_SECRET_CREDENTIALS_MATCHER);
}

private StandardCredentials getCredentials(String serverUrl, String credentialsId) {
Jenkins jenkins = Jenkins.get();
jenkins.checkPermission(Jenkins.ADMINISTER);
jenkins.checkPermission(Jenkins.MANAGE);

Check warning on line 668 in src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServer.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 510-668 are not covered by tests
return StringUtils.isBlank(credentialsId)
? null
: CredentialsMatchers.firstOrNull(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import hudson.ExtensionList;
import hudson.model.Descriptor;
import hudson.model.PersistentDescriptor;
import hudson.security.Permission;
import hudson.util.ListBoxModel;
import io.jenkins.plugins.gitlabserverconfig.servers.helpers.GitLabPersonalAccessTokenCreator;
import java.util.ArrayList;
Expand Down Expand Up @@ -38,6 +39,12 @@
*/
private List<GitLabServer> servers;

@NonNull
@Override
public Permission getRequiredGlobalConfigPagePermission() {
return Jenkins.MANAGE;

Check warning on line 45 in src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServers.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 45 is not covered by tests
}

/**
* Gets the {@link GitLabServers} singleton.
*
Expand Down Expand Up @@ -96,7 +103,7 @@
* @param servers the list of endpoints.
*/
public void setServers(@CheckForNull List<? extends GitLabServer> servers) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
Jenkins.get().checkPermission(Jenkins.MANAGE);
this.servers = fixNull(servers).stream()
.filter(distinctByKey(GitLabServer::getName))
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,82 +81,82 @@
public ListBoxModel doFillCredentialsIdItems(
@QueryParameter String serverUrl, @QueryParameter String credentialsId) {
Jenkins jenkins = Jenkins.get();
if (!jenkins.hasPermission(Jenkins.ADMINISTER)) {
if (!jenkins.hasPermission(Jenkins.MANAGE)) {
return new StandardListBoxModel().includeCurrentValue(credentialsId);
}
return new StandardUsernameListBoxModel()
.includeEmptyValue()
.includeMatchingAs(
ACL.SYSTEM,
jenkins,
StandardUsernamePasswordCredentials.class,
fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL))
.build(),
CredentialsMatchers.always())
.includeMatchingAs(
Jenkins.getAuthentication(),
jenkins,
StandardUsernamePasswordCredentials.class,
fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL))
.build(),
CredentialsMatchers.always());
}

@SuppressWarnings("unused")
@RequirePOST
public FormValidation doCreateTokenByCredentials(
@QueryParameter String serverUrl, @QueryParameter String credentialsId) {

Jenkins jenkins = Jenkins.get();
jenkins.checkPermission(Jenkins.ADMINISTER);
jenkins.checkPermission(Jenkins.MANAGE);
if (isEmpty(credentialsId)) {
return FormValidation.error("Please specify credentials to create token");
}

StandardUsernamePasswordCredentials credentials = firstOrNull(
lookupCredentials(
StandardUsernamePasswordCredentials.class,
jenkins,
ACL.SYSTEM,
fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL))
.build()),
withId(credentialsId));

if (credentials == null) {
credentials = firstOrNull(
lookupCredentials(
StandardUsernamePasswordCredentials.class,
jenkins,
Jenkins.getAuthentication(),
fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL))
.build()),
withId(credentialsId));
}

if (Objects.isNull(credentials)) {
return FormValidation.error("Can't create GitLab token, credentials are null");
}
try {
String tokenName = UUID.randomUUID().toString();
String token = AccessTokenUtils.createPersonalAccessToken(
defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL),
credentials.getUsername(),
Secret.toString(credentials.getPassword()),
tokenName,
GL_PLUGIN_REQUIRED_SCOPE);
tokenName = getShortName(tokenName);
createCredentials(serverUrl, token, credentials.getUsername(), tokenName);
return FormValidation.ok("Created credentials with id %s ", tokenName);
} catch (GitLabApiException e) {
return FormValidation.error(e, "Can't create GL token - %s", e.getMessage());
}
}

@SuppressWarnings("unused")
@RequirePOST
public FormValidation doCreateTokenByPassword(
@QueryParameter String serverUrl, @QueryParameter String login, @QueryParameter String password) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
Jenkins.get().checkPermission(Jenkins.MANAGE);

Check warning on line 159 in src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/helpers/GitLabPersonalAccessTokenCreator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 84-159 are not covered by tests
try {
String tokenName = UUID.randomUUID().toString();
String token = AccessTokenUtils.createPersonalAccessToken(
Expand Down