1+ /*
2+ * The MIT License
3+ *
4+ * Copyright 2015 Jesse Glick.
5+ *
6+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7+ * of this software and associated documentation files (the "Software"), to deal
8+ * in the Software without restriction, including without limitation the rights
9+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+ * copies of the Software, and to permit persons to whom the Software is
11+ * furnished to do so, subject to the following conditions:
12+ *
13+ * The above copyright notice and this permission notice shall be included in
14+ * all copies or substantial portions of the Software.
15+ *
16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+ * THE SOFTWARE.
23+ */
24+
25+ package org .jenkinsci .plugins .credentialsbinding .impl ;
26+
27+ import java .util .Collections ;
28+
29+ import org .jenkinsci .plugins .credentialsbinding .MultiBinding ;
30+ import org .jenkinsci .plugins .docker .commons .credentials .DockerServerCredentials ;
31+ import org .jenkinsci .plugins .docker .commons .credentials .DockerServerDomainSpecification ;
32+ import org .jenkinsci .plugins .workflow .cps .CpsFlowDefinition ;
33+ import org .jenkinsci .plugins .workflow .job .WorkflowJob ;
34+ import org .jenkinsci .plugins .workflow .job .WorkflowRun ;
35+ import org .jenkinsci .plugins .workflow .steps .StepConfigTester ;
36+ import org .jenkinsci .plugins .workflow .test .steps .SemaphoreStep ;
37+ import org .junit .Rule ;
38+ import org .junit .Test ;
39+ import org .junit .runners .model .Statement ;
40+ import org .jvnet .hudson .test .RestartableJenkinsRule ;
41+
42+ import com .cloudbees .plugins .credentials .CredentialsProvider ;
43+ import com .cloudbees .plugins .credentials .CredentialsScope ;
44+ import com .cloudbees .plugins .credentials .CredentialsStore ;
45+ import com .cloudbees .plugins .credentials .SystemCredentialsProvider ;
46+ import com .cloudbees .plugins .credentials .domains .Domain ;
47+ import com .cloudbees .plugins .credentials .domains .DomainSpecification ;
48+
49+ import hudson .FilePath ;
50+
51+ import static org .hamcrest .Matchers .instanceOf ;
52+ import static org .jenkinsci .plugins .credentialsbinding .impl .BindingStepTest .grep ;
53+ import static org .junit .Assert .*;
54+
55+ public class DockerServerCredentialsBindingTest {
56+
57+ @ Rule
58+ public RestartableJenkinsRule story = new RestartableJenkinsRule ();
59+
60+ @ Test
61+ public void configRoundTrip () throws Exception {
62+ story .addStep (new Statement () {
63+ @ SuppressWarnings ("rawtypes" )
64+ @ Override
65+ public void evaluate () throws Throwable {
66+ CredentialsStore store = CredentialsProvider .lookupStores (story .j .getInstance ()).iterator ().next ();
67+ assertThat (store , instanceOf (SystemCredentialsProvider .StoreImpl .class ));
68+ Domain domain = new Domain ("docker" , "A domain for docker credentials" ,
69+ Collections .<DomainSpecification > singletonList (new DockerServerDomainSpecification ()));
70+ DockerServerCredentials c = new DockerServerCredentials (CredentialsScope .GLOBAL ,
71+ "docker-client-cert" , "desc" , "clientKey" , "clientCertificate" , "serverCaCertificate" );
72+ store .addDomain (domain , c );
73+ BindingStep s = new StepConfigTester (story .j )
74+ .configRoundTrip (new BindingStep (Collections .<MultiBinding > singletonList (
75+ new DockerServerCredentialsBinding ("DOCKER_CERT_PATH" , "docker-client-cert" ))));
76+ story .j .assertEqualDataBoundBeans (s .getBindings (), Collections .singletonList (
77+ new DockerServerCredentialsBinding ("DOCKER_CERT_PATH" , "docker-client-cert" )));
78+ }
79+ });
80+ }
81+
82+ @ Test
83+ public void basics () throws Exception {
84+ story .addStep (new Statement () {
85+ @ Override
86+ public void evaluate () throws Throwable {
87+ DockerServerCredentials c = new DockerServerCredentials (CredentialsScope .GLOBAL ,
88+ "docker-client-cert" , "desc" , "clientKey" , "clientCertificate" , "serverCaCertificate" );
89+ CredentialsProvider .lookupStores (story .j .jenkins ).iterator ().next ().addCredentials (Domain .global (), c );
90+ WorkflowJob p = story .j .jenkins .createProject (WorkflowJob .class , "p" );
91+ p .setDefinition (new CpsFlowDefinition (""
92+ + "node {\n "
93+ + " withCredentials([[$class: 'DockerServerCredentialsBinding',\n "
94+ + " variable: 'DOCKER_CERT_PATH',\n "
95+ + " credentialsId: 'docker-client-cert']]) {\n "
96+ + " semaphore 'basics'\n "
97+ + "\n "
98+ + " sh '''\n "
99+ + " set -e -x\n "
100+ + " # check permissions on the credentials dir and its parent\n "
101+ + " [ $(stat -c %a \" $DOCKER_CERT_PATH\" ) = 700 ]\n "
102+ + " [ $(stat -c %a \" $DOCKER_CERT_PATH\" /..) = 700 ]\n "
103+ + "\n "
104+ + " # check permissions and content of the certificate files\n "
105+ + " [ $(stat -c %a \" $DOCKER_CERT_PATH/key.pem\" ) = 600 ]\n "
106+ + " [ $(stat -c %a \" $DOCKER_CERT_PATH/cert.pem\" ) = 600 ]\n "
107+ + " [ $(stat -c %a \" $DOCKER_CERT_PATH/ca.pem\" ) = 600 ]\n "
108+ + " [ $(stat -c %s \" $DOCKER_CERT_PATH/key.pem\" ) = 9 ]\n "
109+ + " [ $(stat -c %s \" $DOCKER_CERT_PATH/cert.pem\" ) = 17 ]\n "
110+ + " [ $(stat -c %s \" $DOCKER_CERT_PATH/ca.pem\" ) = 19 ]\n "
111+ + "\n "
112+ + " # keep location of the certificate dir for the next step\n "
113+ + " echo \" $DOCKER_CERT_PATH\" > cert-path"
114+ + " '''\n "
115+ + " }\n "
116+ + "\n "
117+ + " sh '''\n "
118+ + " set -e +x\n "
119+ + " # make sure the credentials dir have been deleted\n "
120+ + " cert_path=$(cat cert-path)\n "
121+ + " if [ -e \" $cert_path\" ] ; then\n "
122+ + " echo \" $cert_path still exists!!!\" >&2\n "
123+ + " exit 1\n "
124+ + " fi\n "
125+ + " '''\n "
126+ + "}" , true ));
127+ WorkflowRun b = p .scheduleBuild2 (0 ).waitForStart ();
128+ SemaphoreStep .waitForStart ("basics/1" , b );
129+ }
130+ });
131+ story .addStep (new Statement () {
132+ @ Override
133+ public void evaluate () throws Throwable {
134+ WorkflowJob p = story .j .jenkins .getItemByFullName ("p" , WorkflowJob .class );
135+ assertNotNull (p );
136+ WorkflowRun b = p .getBuildByNumber (1 );
137+ assertNotNull (b );
138+ SemaphoreStep .success ("basics/1" , null );
139+ while (b .isBuilding ()) { // TODO 1.607+ use waitForCompletion
140+ Thread .sleep (100 );
141+ }
142+ story .j .assertBuildStatusSuccess (b );
143+ FilePath certPathFile = story .j .jenkins .getWorkspaceFor (p ).child ("cert-path" );
144+ assertTrue (certPathFile .exists ());
145+ String certPath = certPathFile .readToString ().trim ();
146+ // expected .../workspace/p@tmp/secretFiles/<36-chars-UUID>
147+ assertTrue (certPath .matches (".*/workspace/p@tmp/secretFiles/[-a-f0-9]{36}" ));
148+ // this path is a secret, it shouldn't appear in the logs (although it doesn't really matter)
149+ assertEquals (Collections .<String > emptySet (), grep (b .getRootDir (), certPath ));
150+ }
151+ });
152+ }
153+
154+ }
0 commit comments