Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support browser login requests #732

Merged
merged 2 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ public ProxyConfiguration getProxyConfiguration() {
return clientBuilder.getProxyConfiguration();
}

public void setNoAnonymousUser() {
clientBuilder.setNoAnonymousUser(true);
}

/**
* Release all connection and cleanup resources.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,10 @@ public void process(final HttpRequest request, final HttpContext context) throws
HttpHost targetHost = finalContext.getTargetHost();
Credentials creds = credsProvider.getCredentials(
new AuthScope(targetHost.getHostName(), targetHost.getPort()));
if (creds == null) {
throw new HttpException("No credentials for preemptive authentication");
if (creds != null) {
BasicScheme authScheme = new BasicScheme();
authState.update(authScheme, creds);
}
BasicScheme authScheme = new BasicScheme();
authState.update(authScheme, creds);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class PreemptiveHttpClientBuilder {
private String userAgent = StringUtils.EMPTY;
private String userName = StringUtils.EMPTY;
private String password = StringUtils.EMPTY;
// Set to false to use the "anonymous" user if no credentials provided.
private boolean noAnonymousUser;
private SSLContext sslContext;
private boolean insecureTls;
private HttpHost proxy;
Expand Down Expand Up @@ -91,6 +93,11 @@ public PreemptiveHttpClientBuilder setAccessToken(String accessToken) {
return this;
}

public PreemptiveHttpClientBuilder setNoAnonymousUser(boolean noAnonymousUser) {
this.noAnonymousUser = noAnonymousUser;
return this;
}

public PreemptiveHttpClientBuilder setTimeout(int timeout) {
this.timeout = timeout;
return this;
Expand All @@ -104,7 +111,7 @@ public PreemptiveHttpClientBuilder setProxyConfiguration(ProxyConfiguration prox
return this;
}

public ProxyConfiguration getProxyConfiguration(){
public ProxyConfiguration getProxyConfiguration() {
return proxyConfiguration;
}

Expand Down Expand Up @@ -150,6 +157,9 @@ protected void createCredentialsAndAuthCache() {

if (StringUtils.isEmpty(accessToken)) {
if (StringUtils.isEmpty(userName)) {
if (noAnonymousUser) {
return;
}
userName = "anonymous";
password = "";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
import org.jfrog.build.api.util.NullLog;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.*;

@Test
public class PreemptiveHttpClientBuilderTest {
private static final AuthScope ANY_AUTH = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT);

public void testCredentialsMaintainedWithProxy() {
String rtUser = "rt-user";
String rtPassword = "rt-password";
Expand All @@ -29,17 +30,25 @@ public void testCredentialsMaintainedWithProxy() {
.setProxyConfiguration(createProxyConfiguration(proxyHost, proxyPort, proxyUser, proxyPassword))
.setUserName(rtUser)
.setPassword(rtPassword);
PreemptiveHttpClient deployClient = clientBuilder.build();

// Assert both Artifactory and proxy credentials exist in the credentials provider.
BasicCredentialsProvider credentialsProvider = deployClient.basicCredentialsProvider;
Credentials rtCredentials = credentialsProvider.getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT));
assertNotNull(rtCredentials);
assertEquals(rtCredentials, new UsernamePasswordCredentials(rtUser, rtPassword));
try (PreemptiveHttpClient deployClient = clientBuilder.build()) {
// Assert both Artifactory and proxy credentials exist in the credentials provider.
BasicCredentialsProvider credentialsProvider = deployClient.basicCredentialsProvider;
Credentials rtCredentials = credentialsProvider.getCredentials(ANY_AUTH);
assertNotNull(rtCredentials);
assertEquals(rtCredentials, new UsernamePasswordCredentials(rtUser, rtPassword));

Credentials portCredentials = credentialsProvider.getCredentials(new AuthScope(proxyHost, proxyPort));
assertNotNull(portCredentials);
assertEquals(portCredentials, new UsernamePasswordCredentials(proxyUser, proxyPassword));
}
}

Credentials portCredentials = credentialsProvider.getCredentials(new AuthScope(proxyHost, proxyPort));
assertNotNull(portCredentials);
assertEquals(portCredentials, new UsernamePasswordCredentials(proxyUser, proxyPassword));
public void testAnonymousUser() {
try (PreemptiveHttpClient clientWithAnonymous = new PreemptiveHttpClientBuilder().build();
PreemptiveHttpClient clientWithoutAnonymous = new PreemptiveHttpClientBuilder().setNoAnonymousUser(true).build()) {
assertEquals("anonymous", clientWithAnonymous.basicCredentialsProvider.getCredentials(ANY_AUTH).getUserPrincipal().getName());
assertNull(clientWithoutAnonymous.basicCredentialsProvider.getCredentials(ANY_AUTH));
}
}

private ProxyConfiguration createProxyConfiguration(String host, int port, String proxyUser, String proxyPassword) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
import org.apache.commons.lang3.StringUtils;
import org.jfrog.build.api.util.Log;
import org.jfrog.build.extractor.clientConfiguration.client.ManagerBase;
import org.jfrog.build.extractor.clientConfiguration.client.access.services.CreateProject;
import org.jfrog.build.extractor.clientConfiguration.client.access.services.DeleteProject;
import org.jfrog.build.extractor.clientConfiguration.client.access.services.GetProject;
import org.jfrog.build.extractor.clientConfiguration.client.access.services.UpdateProject;
import org.jfrog.build.extractor.clientConfiguration.client.access.services.*;
import org.jfrog.build.extractor.clientConfiguration.client.response.CreateAccessTokenResponse;

import java.io.IOException;

public class AccessManager extends ManagerBase {

public AccessManager(String AccessUrl, String accessToken, Log log) {
super(AccessUrl, StringUtils.EMPTY, StringUtils.EMPTY, accessToken, log);
// The Access service should not accept the "anonymous" user
jfrogHttpClient.setNoAnonymousUser();
}

// Access has no version API.
Expand All @@ -38,4 +38,12 @@ public String getProject(String projectKey) throws IOException {
return new GetProject(projectKey, log).execute(jfrogHttpClient);
}

public void sendBrowserLoginRequest(String uuid) throws IOException {
new SendBrowserLoginRequest(uuid, log).execute(jfrogHttpClient);
}

public CreateAccessTokenResponse getBrowserLoginRequestToken(String uuid) throws IOException {
return new GetBrowserLoginRequestToken(uuid, log).execute(jfrogHttpClient);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.jfrog.build.extractor.clientConfiguration.client.access.services;

import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.jfrog.build.api.util.Log;
import org.jfrog.build.extractor.clientConfiguration.client.JFrogService;
import org.jfrog.build.extractor.clientConfiguration.client.response.CreateAccessTokenResponse;

import java.io.IOException;
import java.io.InputStream;

import static org.jfrog.build.extractor.clientConfiguration.client.access.services.Utils.BROWSER_LOGIN_ENDPOINT;

public class GetBrowserLoginRequestToken extends JFrogService<CreateAccessTokenResponse> {
private final String uuid;

public GetBrowserLoginRequestToken(String uuid, Log logger) {
super(logger);
this.uuid = uuid;
}

@Override
public HttpRequestBase createRequest() throws IOException {
return new HttpGet(String.format("%s/token/%s", BROWSER_LOGIN_ENDPOINT, uuid));
}

@Override
protected void setResponse(InputStream stream) throws IOException {
result = getMapper().readValue(stream, CreateAccessTokenResponse.class);
}

@Override
protected void handleUnsuccessfulResponse(HttpEntity entity) throws IOException {
// Until the user successfully completes the login process, Access will return status code 400.
if (getStatusCode() == HttpStatus.SC_BAD_REQUEST) {
yahavi marked this conversation as resolved.
Show resolved Hide resolved
return;
}
super.handleUnsuccessfulResponse(entity);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.jfrog.build.extractor.clientConfiguration.client.access.services;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.jfrog.build.api.util.Log;
import org.jfrog.build.extractor.clientConfiguration.client.VoidJFrogService;

import java.io.IOException;

import static org.jfrog.build.extractor.clientConfiguration.client.access.services.Utils.BROWSER_LOGIN_ENDPOINT;
import static org.jfrog.build.extractor.clientConfiguration.util.JsonUtils.toJsonString;

public class SendBrowserLoginRequest extends VoidJFrogService {
private final String uuid;

public SendBrowserLoginRequest(String uuid, Log logger) {
super(logger);
this.uuid = uuid;
}

@Override
public HttpRequestBase createRequest() throws IOException {
HttpPost request = new HttpPost(BROWSER_LOGIN_ENDPOINT + "/request");
BrowserLoginRequest browserLoginRequest = new BrowserLoginRequest(uuid);
StringEntity stringEntity = new StringEntity(toJsonString(browserLoginRequest), ContentType.APPLICATION_JSON);
request.setEntity(stringEntity);
return request;
}

private static class BrowserLoginRequest {
private final String session;

public BrowserLoginRequest(String session) {
this.session = session;
}

@SuppressWarnings("unused")
public String getSession() {
return session;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jfrog.build.extractor.clientConfiguration.client.access.services;

public class Utils {
public static final String BROWSER_LOGIN_ENDPOINT = "api/v2/authentication/jfrog_client_login";
public static final String PROJECTS_ENDPOINT = "api/v1/projects";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.jfrog.build.extractor.clientConfiguration.client.response;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
* @author yahavi
**/
@SuppressWarnings("unused")
public class CreateAccessTokenResponse {
@JsonProperty("token_id")
private String tokenId;
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("refresh_token")
private String refreshToken;
@JsonProperty("expires_in")
private int expiresIn;
@JsonProperty("scope")
private String scope;
@JsonProperty("token_type")
private String tokenType;

public String getTokenId() {
return tokenId;
}

public void setTokenId(String tokenId) {
this.tokenId = tokenId;
}

public String getAccessToken() {
return accessToken;
}

public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}

public String getRefreshToken() {
return refreshToken;
}

public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}

public int getExpiresIn() {
return expiresIn;
}

public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}

public String getScope() {
return scope;
}

public void setScope(String scope) {
this.scope = scope;
}

public String getTokenType() {
return tokenType;
}

public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
}
Loading
Loading