1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.rundeck.api;
17
18 import java.io.ByteArrayInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.security.KeyManagementException;
22 import java.security.KeyStoreException;
23 import java.security.NoSuchAlgorithmException;
24 import java.security.UnrecoverableKeyException;
25 import java.security.cert.CertificateException;
26 import java.security.cert.X509Certificate;
27 import java.util.ArrayList;
28 import java.util.List;
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.http.HttpResponse;
31 import org.apache.http.NameValuePair;
32 import org.apache.http.ParseException;
33 import org.apache.http.client.HttpClient;
34 import org.apache.http.client.entity.UrlEncodedFormEntity;
35 import org.apache.http.client.methods.HttpDelete;
36 import org.apache.http.client.methods.HttpGet;
37 import org.apache.http.client.methods.HttpPost;
38 import org.apache.http.client.methods.HttpRequestBase;
39 import org.apache.http.conn.scheme.Scheme;
40 import org.apache.http.conn.ssl.SSLSocketFactory;
41 import org.apache.http.conn.ssl.TrustStrategy;
42 import org.apache.http.impl.client.DefaultHttpClient;
43 import org.apache.http.message.BasicNameValuePair;
44 import org.apache.http.protocol.HTTP;
45 import org.apache.http.util.EntityUtils;
46 import org.dom4j.Document;
47 import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
48 import org.rundeck.api.parser.ParserHelper;
49 import org.rundeck.api.parser.XmlNodeParser;
50 import org.rundeck.api.util.AssertUtil;
51
52
53
54
55
56
57 class ApiCall {
58
59 private final RundeckClient client;
60
61
62
63
64
65
66
67 public ApiCall(RundeckClient client) throws IllegalArgumentException {
68 super();
69 this.client = client;
70 AssertUtil.notNull(client, "The RunDeck Client must not be null !");
71 }
72
73
74
75
76
77
78 public void ping() throws RundeckApiException {
79 HttpClient httpClient = instantiateHttpClient();
80 try {
81 HttpResponse response = httpClient.execute(new HttpGet(client.getUrl()));
82 if (response.getStatusLine().getStatusCode() / 100 != 2) {
83 throw new RundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' when pinging "
84 + client.getUrl());
85 }
86 } catch (IOException e) {
87 throw new RundeckApiException("Failed to ping RunDeck instance at " + client.getUrl(), e);
88 } finally {
89 httpClient.getConnectionManager().shutdown();
90 }
91 }
92
93
94
95
96
97
98 public void testCredentials() throws RundeckApiLoginException {
99 HttpClient httpClient = instantiateHttpClient();
100 try {
101 login(httpClient);
102 } finally {
103 httpClient.getConnectionManager().shutdown();
104 }
105 }
106
107
108
109
110
111
112
113
114
115
116
117 public <T> T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
118 RundeckApiLoginException {
119 return execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
120 }
121
122
123
124
125
126
127
128
129
130
131 public InputStream get(ApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException {
132 ByteArrayInputStream response = execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath));
133
134
135 ParserHelper.loadDocument(response);
136 response.reset();
137
138 return response;
139 }
140
141
142
143
144
145
146
147
148
149
150
151 public <T> T delete(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
152 RundeckApiLoginException {
153 return execute(new HttpDelete(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
154 }
155
156
157
158
159
160
161
162
163
164
165
166 private <T> T execute(HttpRequestBase request, XmlNodeParser<T> parser) throws RundeckApiException,
167 RundeckApiLoginException {
168
169 InputStream response = execute(request);
170
171
172 Document xmlDocument = ParserHelper.loadDocument(response);
173 return parser.parseXmlNode(xmlDocument);
174 }
175
176
177
178
179
180
181
182
183
184 private ByteArrayInputStream execute(HttpRequestBase request) throws RundeckApiException, RundeckApiLoginException {
185 HttpClient httpClient = instantiateHttpClient();
186 try {
187 login(httpClient);
188
189
190 HttpResponse response = null;
191 try {
192 response = httpClient.execute(request);
193 } catch (IOException e) {
194 throw new RundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
195 + request.getURI(), e);
196 }
197
198
199
200 if (response.getStatusLine().getStatusCode() / 100 == 3
201 && HttpDelete.METHOD_NAME.equals(request.getMethod())) {
202 String newLocation = response.getFirstHeader("Location").getValue();
203 try {
204 EntityUtils.consume(response.getEntity());
205 } catch (IOException e) {
206 throw new RundeckApiException("Failed to consume entity (release connection)", e);
207 }
208 request = new HttpDelete(newLocation);
209 try {
210 response = httpClient.execute(request);
211 } catch (IOException e) {
212 throw new RundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
213 + request.getURI(), e);
214 }
215 }
216
217
218 if (response.getStatusLine().getStatusCode() / 100 != 2) {
219 throw new RundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' for "
220 + request.getURI());
221 }
222 if (response.getEntity() == null) {
223 throw new RundeckApiException("Empty RunDeck response ! HTTP status line is : "
224 + response.getStatusLine());
225 }
226
227
228 try {
229 return new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
230 } catch (IOException e) {
231 throw new RundeckApiException("Failed to consume entity and convert the inputStream", e);
232 }
233 } finally {
234 httpClient.getConnectionManager().shutdown();
235 }
236 }
237
238
239
240
241
242
243
244
245 private void login(HttpClient httpClient) throws RundeckApiLoginException {
246 String location = client.getUrl() + "/j_security_check";
247
248 while (true) {
249 HttpPost postLogin = new HttpPost(location);
250 List<NameValuePair> params = new ArrayList<NameValuePair>();
251 params.add(new BasicNameValuePair("j_username", client.getLogin()));
252 params.add(new BasicNameValuePair("j_password", client.getPassword()));
253 params.add(new BasicNameValuePair("action", "login"));
254
255 HttpResponse response = null;
256 try {
257 postLogin.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
258 response = httpClient.execute(postLogin);
259 } catch (IOException e) {
260 throw new RundeckApiLoginException("Failed to post login form on " + location, e);
261 }
262
263 if (response.getStatusLine().getStatusCode() / 100 == 3) {
264
265 location = response.getFirstHeader("Location").getValue();
266 try {
267 EntityUtils.consume(response.getEntity());
268 } catch (IOException e) {
269 throw new RundeckApiLoginException("Failed to consume entity (release connection)", e);
270 }
271 continue;
272 }
273 if (response.getStatusLine().getStatusCode() / 100 != 2) {
274 throw new RundeckApiLoginException("Invalid HTTP response '" + response.getStatusLine() + "' for "
275 + location);
276 }
277 try {
278 String content = EntityUtils.toString(response.getEntity(), HTTP.UTF_8);
279 if (StringUtils.contains(content, "j_security_check")) {
280 throw new RundeckApiLoginException("Login failed for user " + client.getLogin());
281 }
282 try {
283 EntityUtils.consume(response.getEntity());
284 } catch (IOException e) {
285 throw new RundeckApiLoginException("Failed to consume entity (release connection)", e);
286 }
287 } catch (IOException io) {
288 throw new RundeckApiLoginException("Failed to read RunDeck result", io);
289 } catch (ParseException p) {
290 throw new RundeckApiLoginException("Failed to parse RunDeck response", p);
291 }
292 break;
293 }
294 }
295
296
297
298
299
300
301 private HttpClient instantiateHttpClient() {
302 SSLSocketFactory socketFactory = null;
303 try {
304 socketFactory = new SSLSocketFactory(new TrustStrategy() {
305
306 @Override
307 public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
308 return true;
309 }
310 }, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
311 } catch (KeyManagementException e) {
312 throw new RuntimeException(e);
313 } catch (UnrecoverableKeyException e) {
314 throw new RuntimeException(e);
315 } catch (NoSuchAlgorithmException e) {
316 throw new RuntimeException(e);
317 } catch (KeyStoreException e) {
318 throw new RuntimeException(e);
319 }
320
321 HttpClient httpClient = new DefaultHttpClient();
322 httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
323 return httpClient;
324 }
325 }