Skip to content

Commit

Permalink
commit
Browse files Browse the repository at this point in the history
  • Loading branch information
vinokurig committed Jul 14, 2023
1 parent c5963e0 commit b048a7a
Show file tree
Hide file tree
Showing 17 changed files with 118 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,14 @@ public void createOrReplace(PersonalAccessToken personalAccessToken)
'/'))
&& personalAccessToken
.getCheUserId()
.equals(s.getMetadata().getAnnotations().get(ANNOTATION_CHE_USERID))
&& personalAccessToken
.getScmUserName()
.equals(
s.getMetadata().getAnnotations().get(ANNOTATION_SCM_USERNAME)))
.equals(s.getMetadata().getAnnotations().get(ANNOTATION_CHE_USERID)))
.findFirst();

Secret secret =
existing.orElseGet(
() -> {
Map<String, String> annotations = new HashMap<>(DEFAULT_SECRET_ANNOTATIONS);
annotations.put(ANNOTATION_SCM_URL, personalAccessToken.getScmProviderUrl());
annotations.put(ANNOTATION_SCM_USERNAME, personalAccessToken.getScmUserName());
annotations.put(ANNOTATION_CHE_USERID, personalAccessToken.getCheUserId());
ObjectMeta meta =
new ObjectMetaBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
public static final String NAME_PATTERN = "personal-access-token-";

public static final String ANNOTATION_CHE_USERID = "che.eclipse.org/che-userid";
public static final String ANNOTATION_SCM_USERNAME = "che.eclipse.org/scm-username";
public static final String ANNOTATION_SCM_ORGANIZATION = "che.eclipse.org/scm-organization";
public static final String ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID =
"che.eclipse.org/scm-personal-access-token-id";
Expand Down Expand Up @@ -96,7 +95,6 @@ void save(PersonalAccessToken personalAccessToken)
.withAnnotations(
new ImmutableMap.Builder<String, String>()
.put(ANNOTATION_CHE_USERID, personalAccessToken.getCheUserId())
.put(ANNOTATION_SCM_USERNAME, personalAccessToken.getScmUserName())
.put(ANNOTATION_SCM_URL, personalAccessToken.getScmProviderUrl())
.put(
ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID,
Expand Down Expand Up @@ -196,7 +194,6 @@ private Optional<PersonalAccessToken> doGetPersonalAccessToken(
trimmedUrl,
annotations.get(ANNOTATION_CHE_USERID),
annotations.get(ANNOTATION_SCM_ORGANIZATION),
annotations.get(ANNOTATION_SCM_USERNAME),
annotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_NAME),
annotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID),
token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import static java.util.Collections.singletonList;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.ANNOTATION_CHE_USERID;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.ANNOTATION_SCM_URL;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.ANNOTATION_SCM_USERNAME;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.DEFAULT_SECRET_ANNOTATIONS;
import static org.eclipse.che.api.factory.server.scm.kubernetes.KubernetesGitCredentialManager.NAME_PATTERN;
import static org.mockito.ArgumentMatchers.anyMap;
Expand Down Expand Up @@ -125,7 +124,6 @@ public void shouldUseHardcodedUsernameIfScmOrganizationIsDefined() throws Except
Map<String, String> annotations = new HashMap<>(DEFAULT_SECRET_ANNOTATIONS);

annotations.put(ANNOTATION_SCM_URL, token.getScmProviderUrl() + "/");
annotations.put(ANNOTATION_SCM_USERNAME, token.getScmUserName());
annotations.put(ANNOTATION_CHE_USERID, token.getCheUserId());
ObjectMeta objectMeta =
new ObjectMetaBuilder()
Expand Down Expand Up @@ -210,7 +208,6 @@ public void testUpdateTokenInExistingCredential() throws Exception {
Map<String, String> annotations = new HashMap<>(DEFAULT_SECRET_ANNOTATIONS);

annotations.put(ANNOTATION_SCM_URL, token.getScmProviderUrl() + "/");
annotations.put(ANNOTATION_SCM_USERNAME, token.getScmUserName());
annotations.put(ANNOTATION_CHE_USERID, token.getCheUserId());
ObjectMeta objectMeta =
new ObjectMetaBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ public Optional<Boolean> isValid(PersonalAccessToken personalAccessToken) {
azureDevOpsApiClient.getUserWithPAT(
personalAccessToken.getToken(), personalAccessToken.getScmOrganization());
}
return Optional.of(personalAccessToken.getScmUserName().equals(user.getEmailAddress()));
personalAccessToken.setScmUserName(user.getEmailAddress());
return Optional.of(Boolean.TRUE);
} catch (ScmItemNotFoundException | ScmCommunicationException | ScmBadRequestException e) {
return Optional.of(Boolean.FALSE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,25 @@ public PersonalAccessToken fetchPersonalAccessToken(Subject cheUser, String scmS
final String tokenName =
format(TOKEN_NAME_TEMPLATE, cheUser.getUserId(), apiEndpoint.getHost());
try {
BitbucketUser user =
bitbucketServerApiClient.getUser(EnvironmentContext.getCurrent().getSubject());
BitbucketUser user = bitbucketServerApiClient.getUser(null);
LOG.debug("Current bitbucket user {} ", user);
// cleanup existed
List<BitbucketPersonalAccessToken> existingTokens =
bitbucketServerApiClient.getPersonalAccessTokens(user.getSlug()).stream()
bitbucketServerApiClient.getPersonalAccessTokens().stream()
.filter(p -> p.getName().equals(tokenName))
.collect(Collectors.toList());
for (BitbucketPersonalAccessToken existedToken : existingTokens) {
LOG.debug("Deleting existed che token {} {}", existedToken.getId(), existedToken.getName());
bitbucketServerApiClient.deletePersonalAccessTokens(user.getSlug(), existedToken.getId());
bitbucketServerApiClient.deletePersonalAccessTokens(existedToken.getId());
}

BitbucketPersonalAccessToken token =
bitbucketServerApiClient.createPersonalAccessTokens(
user.getSlug(), tokenName, DEFAULT_TOKEN_SCOPE);
bitbucketServerApiClient.createPersonalAccessTokens(tokenName, DEFAULT_TOKEN_SCOPE);
LOG.debug("Token created = {} for {}", token.getId(), token.getUser());
return new PersonalAccessToken(
scmServerUrl,
EnvironmentContext.getCurrent().getSubject().getUserId(),
user.getName(),
user.getSlug(),
token.getName(),
valueOf(token.getId()),
token.getToken());
Expand All @@ -118,7 +115,8 @@ public Optional<Boolean> isValid(PersonalAccessToken personalAccessToken)
oAuthAPI,
apiEndpoint.toString());
try {
apiClient.getUser(personalAccessToken.getScmUserName(), personalAccessToken.getToken());
BitbucketUser user = apiClient.getUser(personalAccessToken.getToken());
personalAccessToken.setScmUserName(user.getName());
return Optional.of(Boolean.TRUE);
} catch (ScmItemNotFoundException
| ScmUnauthorizedException
Expand All @@ -131,8 +129,8 @@ public Optional<Boolean> isValid(PersonalAccessToken personalAccessToken)
try {
BitbucketPersonalAccessToken bitbucketPersonalAccessToken =
bitbucketServerApiClient.getPersonalAccessToken(
personalAccessToken.getScmUserName(),
Long.valueOf(personalAccessToken.getScmTokenId()));
personalAccessToken.setScmUserName(bitbucketPersonalAccessToken.getUser().getName());
return Optional.of(DEFAULT_TOKEN_SCOPE.equals(bitbucketPersonalAccessToken.getPermissions()));
} catch (ScmItemNotFoundException e) {
return Optional.of(Boolean.FALSE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private boolean isApiRequestRelevant(String repositoryUrl) {
"");
// If the token request catches the unauthorised error, it means that the provided url
// belongs to Bitbucket.
bitbucketServerApiClient.getPersonalAccessToken("", 0L);
bitbucketServerApiClient.getPersonalAccessToken(0L);
} catch (ScmItemNotFoundException | ScmCommunicationException e) {
return false;
} catch (ScmUnauthorizedException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public GitUserData fetchGitUserData()
for (String bitbucketServerEndpoint : this.registeredBitbucketEndpoints) {
if (bitbucketServerApiClient.isConnected(bitbucketServerEndpoint)) {
try {
BitbucketUser user = bitbucketServerApiClient.getUser(cheSubject);
BitbucketUser user = bitbucketServerApiClient.getUser(null);
return new GitUserData(user.getDisplayName(), user.getEmailAddress());
} catch (ScmItemNotFoundException e) {
throw new ScmCommunicationException(e.getMessage(), e);
Expand All @@ -100,8 +100,7 @@ public GitUserData fetchGitUserData()
oAuthAPI,
this.apiEndpoint);

BitbucketUser user =
httpBitbucketServerApiClient.getUser(token.getScmUserName(), token.getToken());
BitbucketUser user = httpBitbucketServerApiClient.getUser(token.getToken());
return new GitUserData(user.getDisplayName(), user.getEmailAddress());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,9 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ConflictException;
Expand Down Expand Up @@ -112,44 +110,49 @@ public boolean isConnected(String bitbucketServerUrl) {
}

@Override
public BitbucketUser getUser(Subject cheUser)
throws ScmUnauthorizedException, ScmCommunicationException, ScmItemNotFoundException {
public BitbucketUser getUser(@Nullable String token)
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException {
return getUser(token, getUserSlug(token));
}

private String getUserSlug(@Nullable String token)
throws ScmCommunicationException, ScmUnauthorizedException, ScmItemNotFoundException {
URI uri;
try {
// Since Bitbucket server API doesn't provide a way to get an account profile currently
// authenticated user we will try to find it and by iterating over the list available to the
// current user Bitbucket users and attempting to get their personal access tokens. To speed
// up this process first of all we will search among users that contain(somewhere in Bitbucket
// user
// entity) Che's user username. At the second step, we will search against all visible(to the
// current Che's user) bitbucket users that are not included in the first list.
Set<String> usersByName =
getUsers(cheUser.getUserName()).stream()
.map(BitbucketUser::getSlug)
.collect(Collectors.toSet());
uri = serverUri.resolve("./plugins/servlet/applinks/whoami");
} catch (IllegalArgumentException e) {
// if the slug contains invalid characters (space for example) then the URI will be invalid
throw new ScmCommunicationException(e.getMessage(), e);
}

Optional<BitbucketUser> currentUser = findCurrentUser(usersByName);
if (currentUser.isPresent()) {
return currentUser.get();
}
Set<String> usersAllExceptByName =
getUsers().stream()
.map(BitbucketUser::getSlug)
.filter(s -> !usersByName.contains(s))
.collect(Collectors.toSet());
currentUser = findCurrentUser(usersAllExceptByName);
if (currentUser.isPresent()) {
return currentUser.get();
}
} catch (ScmBadRequestException | ScmItemNotFoundException scmException) {
throw new ScmCommunicationException(scmException.getMessage(), scmException);
HttpRequest request =
HttpRequest.newBuilder(uri)
.headers(
"Authorization",
token != null
? "Bearer " + token
: computeAuthorizationHeader("GET", uri.toString()))
.timeout(DEFAULT_HTTP_TIMEOUT)
.build();

try {
LOG.trace("executeRequest={}", request);
return executeRequest(
httpClient,
request,
inputStream -> {
try {
return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
} catch (ScmBadRequestException e) {
throw new ScmCommunicationException(e.getMessage(), e);
}
throw new ScmItemNotFoundException(
"Current user not found. That is possible only if user are not authorized against "
+ serverUri);
}

@Override
public BitbucketUser getUser(String slug, @Nullable String token)
private BitbucketUser getUser(String slug, @Nullable String token)
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException {
URI uri;
try {
Expand Down Expand Up @@ -209,9 +212,10 @@ public List<BitbucketUser> getUsers(String filter)
}

@Override
public void deletePersonalAccessTokens(String userSlug, Long tokenId)
public void deletePersonalAccessTokens(Long tokenId)
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException {
URI uri = serverUri.resolve("./rest/access-tokens/1.0/users/" + userSlug + "/" + tokenId);
URI uri =
serverUri.resolve("./rest/access-tokens/1.0/users/" + getUserSlug(null) + "/" + tokenId);
HttpRequest request =
HttpRequest.newBuilder(uri)
.DELETE()
Expand Down Expand Up @@ -246,11 +250,12 @@ public void deletePersonalAccessTokens(String userSlug, Long tokenId)

@Override
public BitbucketPersonalAccessToken createPersonalAccessTokens(
String userSlug, String tokenName, Set<String> permissions)
throws ScmBadRequestException, ScmUnauthorizedException, ScmCommunicationException {
String tokenName, Set<String> permissions)
throws ScmBadRequestException, ScmUnauthorizedException, ScmCommunicationException,
ScmItemNotFoundException {
BitbucketPersonalAccessToken token =
new BitbucketPersonalAccessToken(tokenName, permissions, 90);
URI uri = serverUri.resolve("./rest/access-tokens/1.0/users/" + userSlug);
URI uri = serverUri.resolve("./rest/access-tokens/1.0/users/" + getUserSlug(null));

try {
HttpRequest request =
Expand Down Expand Up @@ -288,20 +293,23 @@ public BitbucketPersonalAccessToken createPersonalAccessTokens(
}

@Override
public List<BitbucketPersonalAccessToken> getPersonalAccessTokens(String userSlug)
public List<BitbucketPersonalAccessToken> getPersonalAccessTokens()
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException {
try {
return doGetItems(
BitbucketPersonalAccessToken.class, "./rest/access-tokens/1.0/users/" + userSlug, null);
BitbucketPersonalAccessToken.class,
"./rest/access-tokens/1.0/users/" + getUserSlug(null),
null);
} catch (ScmBadRequestException e) {
throw new ScmCommunicationException(e.getMessage(), e);
}
}

@Override
public BitbucketPersonalAccessToken getPersonalAccessToken(String userSlug, Long tokenId)
public BitbucketPersonalAccessToken getPersonalAccessToken(Long tokenId)
throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException {
URI uri = serverUri.resolve("./rest/access-tokens/1.0/users/" + userSlug + "/" + tokenId);
URI uri =
serverUri.resolve("./rest/access-tokens/1.0/users/" + getUserSlug(null) + "/" + tokenId);
HttpRequest request =
HttpRequest.newBuilder(uri)
.headers(
Expand Down Expand Up @@ -331,37 +339,6 @@ public BitbucketPersonalAccessToken getPersonalAccessToken(String userSlug, Long
}
}

/**
* This method is testing provided collection of user's `slug`s if contains the `slug` of the
* currently authenticated user and return it. The major method to test that condition is to get
* the list of personal access tokens. Current Che user that is associated with Bitbucket user
* should not be able to get someone else list of personal access tokens except his own.
*
* @param userSlugs set of user's `slug`s to test if it contains currently authenticated user.
* @return Bitbucket user from the given set that is associated with the current user. Or
* Optional.empty if the given set doesn't contain that user.
* @throws ScmCommunicationException can happen if communication between che server and bitbucket
* server is failed.
* @throws ScmUnauthorizedException can happen if currently authenticated che user is not
* associated with bitbucket server.
* @throws ScmItemNotFoundException can happen if provided `slug` to test is not associated with
* any user on Bitbucket server
*/
private Optional<BitbucketUser> findCurrentUser(Set<String> userSlugs)
throws ScmCommunicationException, ScmUnauthorizedException, ScmItemNotFoundException {

for (String userSlug : userSlugs) {
BitbucketUser user = getUser(userSlug, null);
try {
getPersonalAccessTokens(userSlug);
return Optional.of(user);
} catch (ScmItemNotFoundException | ScmUnauthorizedException e) {
// ok
}
}
return Optional.empty();
}

private <T> List<T> doGetItems(Class<T> tClass, String api, String filter)
throws ScmUnauthorizedException, ScmCommunicationException, ScmBadRequestException,
ScmItemNotFoundException {
Expand Down
Loading

0 comments on commit b048a7a

Please sign in to comment.