package com.github.andreptb.jenkins.security;
import hudson.Extension;
import hudson.model.Descriptor;
import hudson.security.AbstractPasswordBasedSecurityRealm;
import hudson.security.GroupDetails;
import hudson.security.SecurityRealm;
import hudson.util.FormValidation;
import hudson.util.Messages;
import hudson.util.Secret;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationServiceException;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonParseException;
import org.gitlab.api.GitlabAPI;
import org.gitlab.api.models.GitlabSession;
import org.gitlab.api.models.GitlabUser;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.springframework.dao.DataAccessException;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GitLabSecurityRealm extends AbstractPasswordBasedSecurityRealm {
private static Logger LOGGER = Logger.getLogger(GitLabSecurityRealm.class.getName());
private static final String GITLAB_URL_ENV_VAR_KEY = "JENKINS_GITLAB_URL";
private String gitLabUrl;
private String apiToken;
private GitLabUserDetailsBuilder userDetailsBuilder = new GitLabUserDetailsBuilder();
@DataBoundConstructor
public GitLabSecurityRealm(String gitLabUrl, String apiToken) {
this.gitLabUrl = gitLabUrl;
this.apiToken = apiToken;
}
@Override
protected UserDetails authenticate(String username, String password) throws AuthenticationException {
try {
LOGGER.info("Trying to authenticate with username: " + username);
GitlabSession session = GitlabAPI.connect(this.gitLabUrl, username, password);
return this.userDetailsBuilder.buildUserDetails(this.gitLabUrl, session, session.getPrivateToken());
} catch(Exception e) {
this.LOGGER.log(Level.WARNING, "Authentication request failed for username: " + username, e);
throw new AuthenticationServiceException("Unable to process authentication for username: " + username, e);
}
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
GitlabAPI api = GitlabAPI.connect(this.gitLabUrl, this.apiToken);
try {
GitlabUser[] users = api.retrieve().with("search", username).to(GitlabUser.URL, GitlabUser[].class);
if(ArrayUtils.isNotEmpty(users)) {
return this.userDetailsBuilder.buildUserDetails(this.gitLabUrl, users[0], null);
}
throw new UsernameNotFoundException("No user found: " + username);
} catch (IOException e) {
throw new UsernameNotFoundException("Couldn't find user: " + username, e);
}
}
@Override
public GroupDetails loadGroupByGroupname(final String groupName) throws UsernameNotFoundException, DataAccessException {
return new GroupDetails() {
@Override
public String getName() {
return groupName;
}
};
}
public String getGitLabUrl() {
return gitLabUrl;
}
public String getApiToken() {
return apiToken;
}
@Extension
public static final class DescriptorImpl extends Descriptor<SecurityRealm> {
public FormValidation doCheckGitLabUrl(@QueryParameter String value) throws IOException, ServletException {
return FormValidation.validateRequired(value);
}
public FormValidation doCheckApiToken(@QueryParameter Secret value, @QueryParameter String gitLabUrl) throws IOException, ServletException {
String apiToken = value.getPlainText();
if (StringUtils.isBlank(apiToken)) {
return FormValidation.error(Messages.FormValidation_ValidateRequired());
}
if (StringUtils.isBlank(gitLabUrl)) {
return FormValidation.error("Please inform GitLab's Server URL");
}
try {
GitlabAPI api = GitlabAPI.connect(gitLabUrl, apiToken).ignoreCertificateErrors(true);
GitlabUser userFromToken = api.getCurrentSession();
String username = userFromToken.getName();
if (!userFromToken.isAdmin()) {
return FormValidation.errorWithMarkup("API token owner <b>" + username + "</b> must have administrative privileges");
}
return FormValidation.okWithMarkup("Connection established succesfully (API token from owner <b>" + username + "</b>)");
} catch (JsonParseException e) {
return FormValidation.error(e, "Unexpected response from server, please confirm if GitLab is responding properly");
} catch (Exception e) {
return FormValidation.error(e, "Connection with GitLab failed");
}
}
public static String getDefaultGitLabUrl() {
return System.getenv(GitLabSecurityRealm.GITLAB_URL_ENV_VAR_KEY);
}
/**
* Gives the name to be displayed by the Jenkins view in the security configuration page.
*
* @return the display name
*/
public String getDisplayName() {
return "GitLab";
}
}
}