3535import org .apache .http .impl .conn .PoolingHttpClientConnectionManager ;
3636import org .apache .http .impl .cookie .BasicClientCookie ;
3737import org .labkey .remoteapi .security .EnsureLoginCommand ;
38+ import org .labkey .remoteapi .security .ImpersonateUserCommand ;
3839import org .labkey .remoteapi .security .LogoutCommand ;
40+ import org .labkey .remoteapi .security .StopImpersonatingCommand ;
41+ import org .labkey .remoteapi .security .WhoAmICommand ;
3942
4043import java .io .IOException ;
4144import java .net .URI ;
4447import java .security .KeyStoreException ;
4548import java .security .NoSuchAlgorithmException ;
4649import java .util .Date ;
50+ import java .util .Objects ;
4751
4852/**
4953 * Represents connection information for a particular LabKey Server.
@@ -108,6 +112,11 @@ public class Connection
108112 private int _timeout = DEFAULT_TIMEOUT ;
109113 private String _proxyHost ;
110114 private Integer _proxyPort ;
115+ private String _csrf ;
116+
117+ // The user email when impersonating a user
118+ private String _impersonateUser ;
119+ private String _impersonatePath ;
111120
112121 /**
113122 * Constructs a new Connection object given a base URL and a credentials provider.
@@ -221,24 +230,21 @@ protected HttpClientBuilder clientBuilder()
221230
222231
223232
224- private String csrf = null ;
225-
226- protected void beforeExecute (HttpRequest request )
233+ protected void beforeExecute (HttpRequest request ) throws IOException , CommandException
227234 {
228- if (null == csrf && request instanceof HttpPost )
235+ if (null == _csrf && request instanceof HttpPost )
229236 {
230- // need to preemptively login
231- // we're not really using the login form, just getting a JSESSIONID
237+ // make a request to get a JSESSIONID
232238 try
233239 {
234- new Command ( "login" , "login" ).execute (this , "/" );
240+ new WhoAmICommand ( ).execute (this , "/" );
235241 }
236242 catch (Exception ignored )
237243 {
238244 }
239245 }
240- if (null != csrf )
241- request .setHeader ("X-LABKEY-CSRF" , csrf );
246+ if (null != _csrf )
247+ request .setHeader ("X-LABKEY-CSRF" , _csrf );
242248 }
243249
244250
@@ -248,7 +254,7 @@ protected void afterExecute()
248254 for (Cookie c : _httpClientContext .getCookieStore ().getCookies ())
249255 {
250256 if ("JSESSIONID" .equals (c .getName ()))
251- csrf = c .getValue ();
257+ _csrf = c .getValue ();
252258 }
253259 }
254260
@@ -278,7 +284,68 @@ public CloseableHttpClient logout() throws IOException, CommandException
278284 return getHttpClient ();
279285 }
280286
281- CloseableHttpResponse executeRequest (HttpUriRequest request , Integer timeout ) throws IOException , URISyntaxException , AuthenticationException
287+ /**
288+ * For site-admins only, start impersonating a user.
289+ *
290+ * Admins may impersonate other users to perform actions on their behalf.
291+ * Site admins may impersonate any user in any project. Project admins must
292+ * provide a <code>projectPath</code> and may only impersonate within the
293+ * project in which they have admin permission.
294+ *
295+ * @param email The user to impersonate
296+ * @see Connection#stopImpersonate()
297+ */
298+ public Connection impersonate (/*@NotNull*/ String email ) throws IOException , CommandException
299+ {
300+ return impersonate (email , null );
301+ }
302+
303+ /**
304+ * For site-admins or project-admins only, start impersonating a user.
305+ *
306+ * Admins may impersonate other users to perform actions on their behalf.
307+ * Site admins may impersonate any user in any project. Project admins must
308+ * provide a <code>projectPath</code> and may only impersonate within the
309+ * project in which they have admin permission.
310+ *
311+ * @param email The user to impersonate
312+ * @param projectPath The project path within which the user will be impersonated.
313+ * @see Connection#stopImpersonate()
314+ */
315+ public Connection impersonate (/*@NotNull*/ String email , /*@Nullable*/ String projectPath ) throws IOException , CommandException
316+ {
317+ Objects .requireNonNull (email , "email" );
318+
319+ CommandResponse resp = new ImpersonateUserCommand (email ).execute (this , projectPath );
320+ if (resp .getStatusCode () != 200 )
321+ throw new CommandException ("Failed to impersonate user" );
322+
323+ _impersonateUser = email ;
324+ _impersonatePath = projectPath ;
325+ return this ;
326+ }
327+
328+ /**
329+ * Stop impersonating a user.
330+ */
331+ public Connection stopImpersonate () throws IOException , CommandException
332+ {
333+ if (_impersonateUser != null )
334+ {
335+ CommandResponse resp = new StopImpersonatingCommand ().execute (this , _impersonatePath );
336+
337+ // on success, a 302 redirect response is returned
338+ if (resp .getStatusCode () != 302 )
339+ throw new CommandException ("Failed to stop impersonating" );
340+
341+ _impersonateUser = null ;
342+ _impersonatePath = null ;
343+ }
344+
345+ return this ;
346+ }
347+
348+ CloseableHttpResponse executeRequest (HttpUriRequest request , Integer timeout ) throws IOException , URISyntaxException , AuthenticationException , CommandException
282349 {
283350 // Delegate authentication setup to CredentialsProvider
284351 _credentialsProvider .configureRequest (getBaseUrl (), request , _httpClientContext );
0 commit comments