Skip to content

Commit

Permalink
Merge pull request #29 from delinea-rajani/Dev.rajani.credProvider
Browse files Browse the repository at this point in the history
Implemented a credential provider for Secret Server Vault.
  • Loading branch information
tylerezimmerman authored Aug 22, 2024
2 parents 3aa4898 + 94ec88e commit 0df2b54
Show file tree
Hide file tree
Showing 27 changed files with 405 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changes/1.0.9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 1.0.9 - 2024-08-19
### 🎉 Feature

- Implemented a credential provider for Secret Server Vault.
- Rebranded all Thycotic reference to Delinea.
4 changes: 2 additions & 2 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ jobs:
- name: Upload HPI File
uses: actions/upload-artifact@v1
with:
name: thycotic-secret-server.hpi
path: target/thycotic-secret-server.hpi
name: delinea-secret-server.hpi
path: target/delinea-secret-server.hpi
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## 1.0.9 - 2024-08-19
### 🎉 Feature

- Implemented a credential provider for Secret Server Vault.
- Rebranded all Thycotic reference to Delinea
## 1.0.8 - 2024-05-22
### 🐛 Bug Fix

Expand Down
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Thycotic Secret Server
# Delinea Secret Server

[![Jenkins Plugin Build](https://github.com/jenkinsci/thycotic-secret-server-plugin/actions/workflows/package.yml/badge.svg)](https://github.com/jenkinsci/thycotic-secret-server-plugin/actions/workflows/package.yml)
[![Jenkins Plugin Build](https://github.com/jenkinsci/delinea-secret-server-plugin/actions/workflows/package.yml/badge.svg)](https://github.com/jenkinsci/delinea-secret-server-plugin/actions/workflows/package.yml)

The Thycotic Secret Server Jenkins Plugin allows you to access and reference your Secret Server secrets for use in Jenkins builds.
The Delinea Secret Server Jenkins Plugin allows you to access and reference your Secret Server secrets for use in Jenkins builds.

## Usage

This plugin add the ability to include Secret Server Secrets into your build environment.
1. This plugin add the ability to include Secret Server Secrets into your build environment.

![build-environment](images/jenkins-build-environment.jpg)

Expand All @@ -21,3 +21,24 @@ You will now have the option to change the `kind` of credential you wish to add,
After you have added your credentials to the build environment you can can use the secret in your build/s.

> IMPORTANT: By default, this plugin will add a `TSS_` prefix to the environment variables. You should leave the `Environment Variable Prefix` field blank in the Jenkins UI when consuming your credential.

2. This plugin add the ability to include Secret Server Secrets into global credentials.

![add-Secret-Server-vault-credential](images/jenkins-vault-provider.jpg)

1. Create Credentials:
Create a `Secret Server user credentials` that contains the Secret Server service account credentials.

2. Configure Credentials:
Enter the `Vault URL, Secret ID,` and select the previously created Secret Server user credential in the `Credential ID` field.
> Note: that the Username and Password fields are read-only.
3. Test Connection:
After filling in the required fields, click the `Test Connection` button.
If all inputs are correct, a `Connection Successful` message will appear. Otherwise, an error message will be displayed.

4. Create and Fetch Secrets:
Once the connection test is successful, click `Create` to fetch the secret from Secret.
The fetched secret will include the username and password.

4 changes: 2 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ tasks:
- |
if ! gh config get prompt; then
echo "configuring github cli"
gh repo set-default jenkinsci/thycotic-secret-server-plugin
gh repo set-default jenkinsci/delinea-secret-server-plugin
fi
bump:
desc: bump the version using changie
cmds:
- changie batch 1.0.8
- changie batch 1.0.9
- changie merge
- git add .changes/*
- git add CHANGELOG.md
Expand Down
Binary file added images/jenkins-vault-provider.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<relativePath />
</parent>
<groupId>io.jenkins.plugins</groupId>
<artifactId>thycotic-secret-server</artifactId>
<version>1.0.8</version>
<artifactId>delinea-secret-server</artifactId>
<version>1.0.9</version>
<packaging>hpi</packaging>
<properties>
<!-- Baseline Jenkins version you use to build the plugin. Users must have this version or newer to run. -->
Expand All @@ -21,7 +21,7 @@
~ stapler-plugin.version: The Stapler Maven plugin version required by the plugin. -->
<spring.version>6.0.15</spring.version>
</properties>
<name>Thycotic Secret Server Plugin</name>
<name>Delinea Secret Server Plugin</name>
<!-- The default licence for Jenkins OSS Plugins is MIT. Substitute for the applicable one if needed. -->
<licenses>
<license>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package com.delinea.secrets.jenkins.global.cred;

import java.io.IOException;

import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.verb.POST;

import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import com.delinea.secrets.jenkins.global.cred.VaultClient.UsernamePassword;
import com.delinea.secrets.jenkins.wrapper.cred.UserCredentials;

import hudson.Extension;
import hudson.model.Item;
import hudson.security.ACL;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.Secret;
import jenkins.model.Jenkins;

public class SecretServerCredentials extends UsernamePasswordCredentialsImpl implements StandardCredentials {

private static final long serialVersionUID = 1L;
private final String vaultUrl;
private final String credentialId;
private final String secretId;
private transient UsernamePassword vaultCredential;

/**
* Constructor to initialize the SecretServerCredentials object.
*
* @param scope - The scope of the credentials (GLOBAL, SYSTEM, etc.).
* @param id - The unique ID for the credentials.
* @param description - A description for the credentials.
* @param vaultUrl - The URL of the Secret Server.
* @param credentialId- The ID of the credentials stored in Jenkins.
* @param secretId - The ID of the secret stored in the Secret Server.
*/
@DataBoundConstructor
public SecretServerCredentials(CredentialsScope scope, String id, String description, String vaultUrl,
String credentialId, String secretId) {
super(scope, id, description, null, null);
this.vaultUrl = vaultUrl;
this.credentialId = credentialId;
this.secretId = secretId;
this.vaultCredential = null;
}

public String getVaultUrl() {
return vaultUrl;
}

public String getCredentialId() {
return credentialId;
}

public String getSecretId() {
return secretId;
}

/**
* Fetches the username from the Secret Server.
*
* @return The username fetched from the Secret Server.
*/
@Override
public String getUsername() {
return getVaultCredential().getUsername();
}

/**
* Fetches the password from the Secret Server.
*
* @return The password fetched from the Secret Server, wrapped in a Secret
* object.
*/
@Override
public Secret getPassword() {
return Secret.fromString(getVaultCredential().getPassword());
}

/**
* Fetches the credentials (username and password) from the Secret Server only
* once and caches it.
*
* @return The UsernamePassword object containing the fetched credentials.
* @throws RuntimeException if the credentials cannot be fetched from the Secret
* Server.
*/
private UsernamePassword getVaultCredential() {
if (vaultCredential == null) { // Fetch only if not already cached
try {
UserCredentials credential = UserCredentials.get(credentialId, null);
vaultCredential = new VaultClient().fetchCredentials(vaultUrl, secretId, credential.getUsername(),
credential.getPassword().getPlainText());
} catch (Exception e) {
throw new RuntimeException("Failed to fetch credentials from vault. " + e.getMessage());
}
}
return vaultCredential;

Check warning on line 107 in src/main/java/com/delinea/secrets/jenkins/global/cred/SecretServerCredentials.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 49-107 are not covered by tests
}

@Extension
public static class DescriptorImpl extends BaseStandardCredentialsDescriptor {

@Override
public String getDisplayName() {
return "Secret Server Vault Credentials";
}

/**
* Populates the list of available Credential IDs for the dropdown in the
* Jenkins UI.
*
* @param item - The Jenkins item context.
* @return A ListBoxModel containing the available Credential IDs.
*/
@POST
public ListBoxModel doFillCredentialIdItems(@AncestorInPath final Item item) {
if (item == null && !Jenkins.get().hasPermission(Jenkins.ADMINISTER)
|| item != null && !item.hasPermission(Item.CONFIGURE)) {
return new StandardListBoxModel();
}
return new StandardListBoxModel().includeAs(ACL.SYSTEM, item, UserCredentials.class).includeEmptyValue();
}

/**
* Validates the Credential ID input by the user.
*/
@POST
public FormValidation doCheckCredentialId(@QueryParameter final String value)
throws IOException, ServletException {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
return FormValidation.error("You do not have permission to perform this action");
}
if (StringUtils.isBlank(value)) {
return FormValidation.error("Credential ID is required.");
}
return FormValidation.ok();
}

/**
* Validates the Secret ID input by the user.
*/
@POST
public FormValidation doCheckSecretId(@QueryParameter final String value) throws IOException, ServletException {
if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
return FormValidation.error("You do not have permission to perform this action");
}
if (StringUtils.isBlank(value)) {
return FormValidation.error("Secret ID is required.");
}
try {
Integer.parseInt(value);
} catch (NumberFormatException e) {
return FormValidation.error("ID must be an integer.");
}
return FormValidation.ok();
}

/**
* Tests the connection to the Secret Server using the provided parameters.
*
* @param owner - The Jenkins item context.
* @param vaultUrl - The URL of the Secret Server.
* @param credentialId - The ID of the credentials stored in Jenkins.
* @param secretId - The ID of the secret stored in theSecret Server.
* @return FormValidation indicating whether the connection was successful or
* not.
*/
@POST
public FormValidation doTestConnection(@AncestorInPath Item owner,
@QueryParameter("vaultUrl") final String vaultUrl,
@QueryParameter("credentialId") final String credentialId,
@QueryParameter("secretId") final String secretId) {

// Check for necessary permissions
if (owner == null) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
} else {
owner.checkPermission(Item.CONFIGURE);
}

// Validate inputs
if (StringUtils.isBlank(credentialId)) {
return FormValidation.error("Credential ID is required to test the connection.");
}

if (StringUtils.isBlank(vaultUrl)) {
return FormValidation.error("Vault URL cannot be blank.");
}

try {
// Attempt to fetch credentials from Secret Server
UserCredentials credential = UserCredentials.get(credentialId, null);
new VaultClient().fetchCredentials(vaultUrl, secretId, credential.getUsername(),
credential.getPassword().getPlainText());
return FormValidation.ok("Connection successful.");
} catch (Exception e) {
return FormValidation.error("Failed to establish connection: " + e.getMessage());

Check warning on line 207 in src/main/java/com/delinea/secrets/jenkins/global/cred/SecretServerCredentials.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 127-207 are not covered by tests
}
}
}
}
Loading

0 comments on commit 0df2b54

Please sign in to comment.