Skip to content

Commit

Permalink
refactor: introduce AuthUser interface
Browse files Browse the repository at this point in the history
Replace the references to `OAuth2User` by `AuthUser`. This allows
downstream extenders to more easily contribute alternative OAuth2
providers: If the expected data is stored in different attributes it
will be possible to bridge it by implementing the proper `AuthUser`.
  • Loading branch information
paul-marechal committed Dec 9, 2024
1 parent 0f5315d commit 0e07da9
Show file tree
Hide file tree
Showing 11 changed files with 334 additions and 129 deletions.
63 changes: 35 additions & 28 deletions server/src/main/java/org/eclipse/openvsx/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@
********************************************************************************/
package org.eclipse.openvsx;

import com.google.common.base.Joiner;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import static org.eclipse.openvsx.cache.CacheService.CACHE_NAMESPACE_DETAILS_JSON;
import static org.eclipse.openvsx.util.UrlUtil.createApiUrl;

import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.Tika;
Expand All @@ -27,23 +33,24 @@
import org.eclipse.openvsx.json.NamespaceDetailsJson;
import org.eclipse.openvsx.json.ResultJson;
import org.eclipse.openvsx.repositories.RepositoryService;
import org.eclipse.openvsx.security.AuthUser;
import org.eclipse.openvsx.security.IdPrincipal;
import org.eclipse.openvsx.storage.StorageUtilService;
import org.eclipse.openvsx.util.*;
import org.eclipse.openvsx.util.ErrorResultException;
import org.eclipse.openvsx.util.NamingUtil;
import org.eclipse.openvsx.util.NotFoundException;
import org.eclipse.openvsx.util.TempFile;
import org.eclipse.openvsx.util.TimeUtil;
import org.eclipse.openvsx.util.UrlUtil;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import com.google.common.base.Joiner;

import static org.eclipse.openvsx.cache.CacheService.CACHE_NAMESPACE_DETAILS_JSON;
import static org.eclipse.openvsx.util.UrlUtil.createApiUrl;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;

@Component
public class UserService {
Expand Down Expand Up @@ -80,44 +87,44 @@ public UserData findLoggedInUser() {
}

@Transactional
public UserData registerNewUser(OAuth2User oauth2User) {
public UserData registerNewUser(AuthUser authUser) {
var user = new UserData();
user.setProvider("github");
user.setAuthId(oauth2User.getName());
user.setLoginName(oauth2User.getAttribute("login"));
user.setFullName(oauth2User.getAttribute("name"));
user.setEmail(oauth2User.getAttribute("email"));
user.setProviderUrl(oauth2User.getAttribute("html_url"));
user.setAvatarUrl(oauth2User.getAttribute("avatar_url"));
user.setProvider(authUser.getProviderId());
user.setAuthId(authUser.getAuthId());
user.setLoginName(authUser.getLoginName());
user.setFullName(authUser.getFullName());
user.setEmail(authUser.getEmail());
user.setProviderUrl(authUser.getProviderUrl());
user.setAvatarUrl(authUser.getAvatarUrl());
entityManager.persist(user);
return user;
}

@Transactional
public UserData updateExistingUser(UserData user, OAuth2User oauth2User) {
if ("github".equals(user.getProvider())) {
public UserData updateExistingUser(UserData user, AuthUser authUser) {
if (authUser.getProviderId().equals(user.getProvider())) {
var updated = false;
String loginName = oauth2User.getAttribute("login");
String loginName = authUser.getLoginName();
if (loginName != null && !loginName.equals(user.getLoginName())) {
user.setLoginName(loginName);
updated = true;
}
String fullName = oauth2User.getAttribute("name");
String fullName = authUser.getFullName();
if (fullName != null && !fullName.equals(user.getFullName())) {
user.setFullName(fullName);
updated = true;
}
String email = oauth2User.getAttribute("email");
String email = authUser.getEmail();
if (email != null && !email.equals(user.getEmail())) {
user.setEmail(email);
updated = true;
}
String providerUrl = oauth2User.getAttribute("html_url");
String providerUrl = authUser.getProviderUrl();
if (providerUrl != null && !providerUrl.equals(user.getProviderUrl())) {
user.setProviderUrl(providerUrl);
updated = true;
}
String avatarUrl = oauth2User.getAttribute("avatar_url");
String avatarUrl = authUser.getAvatarUrl();
if (avatarUrl != null && !avatarUrl.equals(user.getAvatarUrl())) {
user.setAvatarUrl(avatarUrl);
updated = true;
Expand Down Expand Up @@ -315,4 +322,4 @@ public ResultJson deleteAccessToken(UserData user, long id) {
token.setActive(false);
return ResultJson.success("Deleted access token for user " + user.getLoginName() + ".");
}
}
}
48 changes: 48 additions & 0 deletions server/src/main/java/org/eclipse/openvsx/security/AuthUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/********************************************************************************
* Copyright (c) 2023 Ericsson and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
package org.eclipse.openvsx.security;

/**
* Encapsulate information about freshly authenticated users.
*
* Different OAuth2 providers may return the same information with different
* attribute keys. This interface allows bridging arbitrary providers.
*/
public interface AuthUser {
/**
* @return Non-human readable unique identifier.
*/
String getAuthId();
/**
* @return The user's avatar URL. Some services require post-processing to get the actual value for it
* (the value returned is a template and you need to remplace variables).
*/
String getAvatarUrl();
/**
* @return The user's email.
*/
String getEmail();
/**
* @return The user's full name (first and last names).
*/
String getFullName();
/**
* @return The login name for the user. Human-readable unique name. AKA username.
*/
String getLoginName();
/**
* @return The authentication provider unique name, e.g. `github`, `eclipse`, etc.
*/
String getProviderId();
/**
* @return The authentication provider URL.
*/
String getProviderUrl();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.eclipse.openvsx.security;

import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

@Service
public class AuthUserFactory {

public AuthUser createAuthUser(String providerId, OAuth2User oauth2User) {
return new DefaultAuthUser(
oauth2User.getName(),
oauth2User.getAttribute("avatar_url"),
oauth2User.getAttribute("email"),
oauth2User.getAttribute("name"),
oauth2User.getAttribute("login"),
providerId,
oauth2User.getAttribute("html_url")
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/********************************************************************************
* Copyright (c) 2023 Ericsson and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
package org.eclipse.openvsx.security;

public class DefaultAuthUser implements AuthUser {

final String authId;
final String avatarUrl;
final String email;
final String fullName;
final String loginName;
final String providerId;
final String providerUrl;

public DefaultAuthUser(
final String authId,
final String avatarUrl,
final String email,
final String fullName,
final String loginName,
final String providerId,
final String providerUrl
) {
this.authId = authId;
this.avatarUrl = avatarUrl;
this.email = email;
this.fullName = fullName;
this.loginName = loginName;
this.providerId = providerId;
this.providerUrl = providerUrl;
}

@Override
public String getAuthId() {
return authId;
}

@Override
public String getAvatarUrl() {
return avatarUrl;
}

@Override
public String getEmail() {
return email;
}

@Override
public String getFullName() {
return fullName;
}

@Override
public String getLoginName() {
return loginName;
}

@Override
public String getProviderId() {
return providerId;
}

@Override
public String getProviderUrl() {
return providerUrl;
}
}
Loading

0 comments on commit 0e07da9

Please sign in to comment.