Skip to content

Commit 36d40d0

Browse files
committed
fix(#532)!: Add dynamic credential handling for withCredentials
Adjusted the BasePipelineTest to support dynamic credentials processing by type, including `usernamePassword` and `string`. Updated tests and job scripts to reflect these changes. This fix may cause test cases to fail when the output of the 'withCredentials' step is checked. This is because additional credential binding attributes are now output. Fixes #532
1 parent 4d4538b commit 36d40d0

File tree

8 files changed

+131
-46
lines changed

8 files changed

+131
-46
lines changed

src/main/groovy/com/lesfurets/jenkins/unit/BasePipelineTest.groovy

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,29 @@ abstract class BasePipelineTest {
2525
addParam(desc.name, desc.defaultValue, false)
2626
}
2727

28-
def stringInterceptor = { Map desc->
28+
def stringInterceptor = { Map desc ->
2929
if (desc) {
3030
// we are in context of parameters { string(...)}
3131
if (desc.name) {
3232
addParam(desc.name, desc.defaultValue, false)
3333
}
3434
// we are in context of withCredentials([string()..]) { }
35-
if(desc.variable) {
36-
return desc.variable
35+
if(desc.credentialsId && desc.variable) {
36+
return [
37+
credentialsType: 'string',
38+
credentialsId: desc.credentialsId,
39+
variable: desc.variable
40+
]
3741
}
3842
}
3943
}
4044

41-
def usernamePasswordInterceptor = { m -> [m.usernameVariable, m.passwordVariable] }
45+
def usernamePasswordInterceptor = { m -> [
46+
credentialsType: 'usernamePassword',
47+
credentialsId: m.credentialsId,
48+
usernameVariable: m.usernameVariable,
49+
passwordVariable: m.passwordVariable
50+
]}
4251

4352
def withCredentialsInterceptor = { list, closure ->
4453
def previousValues = [:]
@@ -67,6 +76,9 @@ abstract class BasePipelineTest {
6776
previousValues[password] = null
6877
}
6978
binding.setVariable(password, password)
79+
} else if (creds instanceof Map && creds.get("credentialsType")) {
80+
// Delegate handling of known credential types
81+
handleCredentialBinding(creds, previousValues)
7082
} else {
7183
creds.each { var ->
7284
try {
@@ -560,4 +572,60 @@ abstract class BasePipelineTest {
560572
Map credentials = binding.getVariable('credentials') as Map
561573
credentials[key] = val
562574
}
575+
576+
void addUsernamePasswordCredential(String credentialId, String username, String password) {
577+
addCredential(credentialId, "${username}:${password}")
578+
}
579+
580+
void addStringCredential(String credentialId, String val) {
581+
addCredential(credentialId, val)
582+
}
583+
584+
/**
585+
* Handles the processing and binding for different types of credentials dynamically.
586+
*/
587+
void handleCredentialBinding(Map creds, Map previousValues) {
588+
def credentialsId = creds.get('credentialsId')
589+
def credentials = binding.getVariable('credentials')
590+
if (credentials == null || !credentials.containsKey(credentialsId)) {
591+
throw new AssertionError("The configuration for the credentials ID '${credentialsId}' may be missing.", null)
592+
}
593+
594+
switch (creds.get('credentialsType')) {
595+
case 'usernamePassword':
596+
def usernamePassword = credentials.get(credentialsId).split(':')
597+
def usernameVariable = creds.get('usernameVariable')
598+
def passwordVariable = creds.get('passwordVariable')
599+
600+
try {
601+
previousValues[usernameVariable] = binding.getVariable(usernameVariable)
602+
} catch (MissingPropertyException ignored) {
603+
previousValues[usernameVariable] = null
604+
}
605+
binding.setVariable(usernameVariable, usernamePassword[0])
606+
607+
try {
608+
previousValues[passwordVariable] = binding.getVariable(passwordVariable)
609+
} catch (MissingPropertyException ignored) {
610+
previousValues[passwordVariable] = null
611+
}
612+
binding.setVariable(passwordVariable, usernamePassword[1])
613+
break
614+
615+
case 'string':
616+
def variable = creds.get('variable')
617+
def value = credentials.get(credentialsId)
618+
619+
try {
620+
previousValues[variable] = binding.getVariable(variable)
621+
} catch (MissingPropertyException ignored) {
622+
previousValues[variable] = null
623+
}
624+
binding.setVariable(variable, value)
625+
break
626+
627+
default:
628+
throw new UnsupportedOperationException("Unsupported credentials type: ${creds.get('credentialsType')}")
629+
}
630+
}
563631
}

src/test/groovy/com/lesfurets/jenkins/TestWithCredentialsAndParametersJob.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ class TestWithCredentialsAndParametersJob extends BaseRegressionTest {
1515

1616
@Test
1717
void should_run_script_with_parameters() {
18+
// given:
19+
addStringCredential('my-gitlab-api-token', 'gitlab-api-token')
20+
1821
// when:
1922
runScript("job/withCredentialsAndParameters.jenkins")
2023

src/test/groovy/com/lesfurets/jenkins/TestWithCredentialsJob.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ class TestWithCredentialsJob extends BaseRegressionTest {
1717

1818
@Test
1919
void should_run_script_with_credentials() {
20+
// given:
21+
addUsernamePasswordCredential('my_cred_id-1', 'test-user-1', 'test-password-1')
22+
addUsernamePasswordCredential('my_cred_id-2', 'test-user-2', 'test-password-2')
23+
addStringCredential('docker_cred-1', 'docker-pass-1')
24+
addStringCredential('docker_cred-2', 'docker-pass-2')
25+
addStringCredential('ssh_cred-1', 'ssh-pass-1')
26+
addStringCredential('ssh_cred-2', 'ssh-pass-2')
27+
2028
// when:
2129
runScript("job/withCredentials.jenkins")
2230

src/test/groovy/com/lesfurets/jenkins/unit/declarative/TestDeclaraticeWithCredentials.groovy

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,32 @@ class TestDeclaraticeWithCredentials extends DeclarativePipelineTest {
1818

1919
@Test
2020
void should_run_script_with_credentials() {
21+
// given:
22+
addUsernamePasswordCredential('my_cred_id-1', 'test-user-1', 'test-password-1')
23+
addUsernamePasswordCredential('my_cred_id-2', 'test-user-2', 'test-password-2')
24+
addStringCredential('docker_cred-1', 'docker-pass-1')
25+
addStringCredential('docker_cred-2', 'docker-pass-2')
26+
addStringCredential('ssh_cred-1', 'ssh-pass-1')
27+
addStringCredential('ssh_cred-2', 'ssh-pass-2')
28+
2129
// when:
2230
runScript("withCredentials_Jenkinsfile")
2331

2432
// then:
2533
assertJobStatusSuccess()
26-
assertCallStack().contains("echo(User/Pass = user/pass)")
27-
assertCallStack().contains("echo(Nested User/Pass = user/pass)")
28-
assertCallStack().contains("echo(Restored User/Pass = user/pass)")
34+
assertCallStack().contains("echo(User/Pass = test-user-1/test-password-1)")
35+
assertCallStack().contains("echo(Nested User/Pass = test-user-2/test-password-2)")
36+
assertCallStack().contains("echo(Restored User/Pass = test-user-1/test-password-1)")
2937
assertCallStack().contains("echo(Cleared User/Pass = null/null)")
3038

31-
assertCallStack().contains("echo(Docker = docker_pass)")
32-
assertCallStack().contains("echo(Nested Docker = docker_pass)")
33-
assertCallStack().contains("echo(Restored Docker = docker_pass)")
39+
assertCallStack().contains("echo(Docker = docker-pass-1)")
40+
assertCallStack().contains("echo(Nested Docker = docker-pass-2)")
41+
assertCallStack().contains("echo(Restored Docker = docker-pass-1)")
3442
assertCallStack().contains("echo(Cleared Docker = null)")
3543

36-
assertCallStack().contains("echo(SSH = ssh_pass)")
37-
assertCallStack().contains("echo(Nested SSH = ssh_pass)")
38-
assertCallStack().contains("echo(Restored SSH = ssh_pass)")
44+
assertCallStack().contains("echo(SSH = ssh-pass-1)")
45+
assertCallStack().contains("echo(Nested SSH = ssh-pass-2)")
46+
assertCallStack().contains("echo(Restored SSH = ssh-pass-1)")
3947
assertCallStack().contains("echo(Cleared SSH = null)")
40-
41-
4248
}
4349
}

src/test/jenkins/jenkinsfiles/withCredentials_Jenkinsfile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ pipeline {
44
stages {
55
stage("run") {
66
withCredentials([
7-
usernamePassword( credentialsId: 'my_cred_id', usernameVariable: 'user', passwordVariable: 'pass' ),
8-
string( credentialsId: 'docker_cred', variable: 'docker_pass' ),
9-
string( credentialsId: 'ssh_cred', variable: 'ssh_pass' )
7+
usernamePassword( credentialsId: 'my_cred_id-1', usernameVariable: 'user', passwordVariable: 'pass' ),
8+
string( credentialsId: 'docker_cred-1', variable: 'docker_pass' ),
9+
string( credentialsId: 'ssh_cred-1', variable: 'ssh_pass' )
1010
]) {
1111
// Ensure values are set on entry
1212
echo "User/Pass = $user/$pass"
1313
echo "Docker = $docker_pass"
1414
echo "SSH = $ssh_pass"
1515

1616
withCredentials([
17-
usernamePassword( credentialsId: 'my_cred_id', usernameVariable: 'user', passwordVariable: 'pass' ),
18-
string( credentialsId: 'docker_cred', variable: 'docker_pass' ),
19-
string( credentialsId: 'ssh_cred', variable: 'ssh_pass' )
17+
usernamePassword( credentialsId: 'my_cred_id-2', usernameVariable: 'user', passwordVariable: 'pass' ),
18+
string( credentialsId: 'docker_cred-2', variable: 'docker_pass' ),
19+
string( credentialsId: 'ssh_cred-2', variable: 'ssh_pass' )
2020
]) {
2121
// Ensure nested values are in place
2222
echo "Nested User/Pass = $user/$pass"

src/test/jenkins/job/withCredentials.jenkins

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
node {
22
withCredentials([
3-
usernamePassword( credentialsId: 'my_cred_id', usernameVariable: 'user', passwordVariable: 'pass' ),
4-
string( credentialsId: 'docker_cred', variable: 'docker_pass' ),
5-
string( credentialsId: 'ssh_cred', variable: 'ssh_pass' )
3+
usernamePassword( credentialsId: 'my_cred_id-1', usernameVariable: 'user', passwordVariable: 'pass' ),
4+
string( credentialsId: 'docker_cred-1', variable: 'docker_pass' ),
5+
string( credentialsId: 'ssh_cred-1', variable: 'ssh_pass' )
66
]) {
77
// Ensure values are set on entry
88
echo "User/Pass = $user/$pass"
99
echo "Docker = $docker_pass"
1010
echo "SSH = $ssh_pass"
1111

1212
withCredentials([
13-
usernamePassword( credentialsId: 'my_cred_id', usernameVariable: 'user', passwordVariable: 'pass' ),
14-
string( credentialsId: 'docker_cred', variable: 'docker_pass' ),
15-
string( credentialsId: 'ssh_cred', variable: 'ssh_pass' )
13+
usernamePassword( credentialsId: 'my_cred_id-2', usernameVariable: 'user', passwordVariable: 'pass' ),
14+
string( credentialsId: 'docker_cred-2', variable: 'docker_pass' ),
15+
string( credentialsId: 'ssh_cred-2', variable: 'ssh_pass' )
1616
]) {
1717
// Ensure nested values are in place
1818
echo "Nested User/Pass = $user/$pass"

src/test/resources/callstacks/TestWithCredentialsAndParametersJob_withCredentialsAndParameters.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
withCredentialsAndParameters.properties([null])
66
withCredentialsAndParameters.echo('myStringParam' value is default: my default value)
77
withCredentialsAndParameters.string({credentialsId=my-gitlab-api-token, variable=GITLAB_API_TOKEN})
8-
withCredentialsAndParameters.withCredentials([GITLAB_API_TOKEN], groovy.lang.Closure)
9-
withCredentialsAndParameters.echo('my-gitlab-api-token' credential variable value: GITLAB_API_TOKEN)
8+
withCredentialsAndParameters.withCredentials([{credentialsType=string, credentialsId=my-gitlab-api-token, variable=GITLAB_API_TOKEN}], groovy.lang.Closure)
9+
withCredentialsAndParameters.echo('my-gitlab-api-token' credential variable value: gitlab-api-token)
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
withCredentials.run()
22
withCredentials.node(groovy.lang.Closure)
3-
withCredentials.usernamePassword({credentialsId=my_cred_id, usernameVariable=user, passwordVariable=pass})
4-
withCredentials.string({credentialsId=docker_cred, variable=docker_pass})
5-
withCredentials.string({credentialsId=ssh_cred, variable=ssh_pass})
6-
withCredentials.withCredentials([[user, pass], docker_pass, ssh_pass], groovy.lang.Closure)
7-
withCredentials.echo(User/Pass = user/pass)
8-
withCredentials.echo(Docker = docker_pass)
9-
withCredentials.echo(SSH = ssh_pass)
10-
withCredentials.usernamePassword({credentialsId=my_cred_id, usernameVariable=user, passwordVariable=pass})
11-
withCredentials.string({credentialsId=docker_cred, variable=docker_pass})
12-
withCredentials.string({credentialsId=ssh_cred, variable=ssh_pass})
13-
withCredentials.withCredentials([[user, pass], docker_pass, ssh_pass], groovy.lang.Closure)
14-
withCredentials.echo(Nested User/Pass = user/pass)
15-
withCredentials.echo(Nested Docker = docker_pass)
16-
withCredentials.echo(Nested SSH = ssh_pass)
17-
withCredentials.echo(Restored User/Pass = user/pass)
18-
withCredentials.echo(Restored Docker = docker_pass)
19-
withCredentials.echo(Restored SSH = ssh_pass)
3+
withCredentials.usernamePassword({credentialsId=my_cred_id-1, usernameVariable=user, passwordVariable=pass})
4+
withCredentials.string({credentialsId=docker_cred-1, variable=docker_pass})
5+
withCredentials.string({credentialsId=ssh_cred-1, variable=ssh_pass})
6+
withCredentials.withCredentials([{credentialsType=usernamePassword, credentialsId=my_cred_id-1, usernameVariable=user, passwordVariable=pass}, {credentialsType=string, credentialsId=docker_cred-1, variable=docker_pass}, {credentialsType=string, credentialsId=ssh_cred-1, variable=ssh_pass}], groovy.lang.Closure)
7+
withCredentials.echo(User/Pass = test-user-1/test-password-1)
8+
withCredentials.echo(Docker = docker-pass-1)
9+
withCredentials.echo(SSH = ssh-pass-1)
10+
withCredentials.usernamePassword({credentialsId=my_cred_id-2, usernameVariable=user, passwordVariable=pass})
11+
withCredentials.string({credentialsId=docker_cred-2, variable=docker_pass})
12+
withCredentials.string({credentialsId=ssh_cred-2, variable=ssh_pass})
13+
withCredentials.withCredentials([{credentialsType=usernamePassword, credentialsId=my_cred_id-2, usernameVariable=user, passwordVariable=pass}, {credentialsType=string, credentialsId=docker_cred-2, variable=docker_pass}, {credentialsType=string, credentialsId=ssh_cred-2, variable=ssh_pass}], groovy.lang.Closure)
14+
withCredentials.echo(Nested User/Pass = test-user-2/test-password-2)
15+
withCredentials.echo(Nested Docker = docker-pass-2)
16+
withCredentials.echo(Nested SSH = ssh-pass-2)
17+
withCredentials.echo(Restored User/Pass = test-user-1/test-password-1)
18+
withCredentials.echo(Restored Docker = docker-pass-1)
19+
withCredentials.echo(Restored SSH = ssh-pass-1)
2020
withCredentials.echo(Cleared User/Pass = null/null)
2121
withCredentials.echo(Cleared Docker = null)
2222
withCredentials.echo(Cleared SSH = null)

0 commit comments

Comments
 (0)