Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* OAuthProvider API

* fixed filter processor url

* Fixed bug with logout
  • Loading branch information
aedelmann authored Dec 10, 2019
1 parent 1bd16e6 commit 2f3a9b2
Show file tree
Hide file tree
Showing 61 changed files with 1,203 additions and 865 deletions.
2 changes: 2 additions & 0 deletions repository/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
<module>repository-server-config</module>
<module>repository-editor</module>
<module>repository-web</module>
<module>repository-oauth-github</module>
<module>repository-oauth-boschid</module>
</modules>

<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import org.eclipse.vorto.repository.core.events.AppEvent;
import org.eclipse.vorto.repository.core.events.EventType;
import org.eclipse.vorto.repository.domain.User;
import org.eclipse.vorto.repository.sso.SpringUserUtils;
import org.eclipse.vorto.repository.oauth.internal.SpringUserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.security.core.Authentication;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.vorto.repository.sso.oauth.strategy;
package org.eclipse.vorto.repository.oauth;

import java.security.PublicKey;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -23,15 +24,16 @@
import javax.servlet.http.HttpServletRequest;
import org.eclipse.vorto.repository.account.IUserAccountService;
import org.eclipse.vorto.repository.domain.Role;
import org.eclipse.vorto.repository.sso.SpringUserUtils;
import org.eclipse.vorto.repository.sso.oauth.JwtToken;
import org.eclipse.vorto.repository.sso.oauth.ITokenVerificationProvider;
import org.eclipse.vorto.repository.oauth.internal.JwtToken;
import org.eclipse.vorto.repository.oauth.internal.SpringUserUtils;
import org.eclipse.vorto.repository.oauth.internal.VerificationHelper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;

public abstract class AbstractTokenVerificationProvider implements ITokenVerificationProvider {
public abstract class AbstractOAuthProvider implements IOAuthProvider {

protected static final String JWT_EMAIL = "email";
private static final String JWT_EXPIRY = "exp";
Expand All @@ -44,7 +46,7 @@ public abstract class AbstractTokenVerificationProvider implements ITokenVerific
private Map<String, PublicKey> publicKeys = null;
protected IUserAccountService userAccountService;

public AbstractTokenVerificationProvider(Supplier<Map<String, PublicKey>> publicKeySupplier,
public AbstractOAuthProvider(Supplier<Map<String, PublicKey>> publicKeySupplier,
IUserAccountService userAccountService) {
this.userAccountService = Objects.requireNonNull(userAccountService);
this.publicKeySupplier = Objects.requireNonNull(publicKeySupplier);
Expand All @@ -64,18 +66,17 @@ public boolean canHandle(String accessToken) {
return false;
}

@Override
public OAuth2Authentication createAuthentication(HttpServletRequest request, String accessToken) {
private Authentication createAuthentication(HttpServletRequest request, String accessToken) {
Optional<JwtToken> jwtToken = JwtToken.instance(accessToken);
if (jwtToken.isPresent()) {
return createAuthentication(request, jwtToken.get());
}
return null;
}

protected abstract OAuth2Authentication createAuthentication(HttpServletRequest httpRequest, JwtToken accessToken);
protected abstract Authentication createAuthentication(HttpServletRequest httpRequest, JwtToken accessToken);

protected OAuth2Authentication createAuthentication(String ciamClientId, String userId, String name,
protected OAuth2Authentication createAuthentication(String clientId, String userId, String name,
String email, Set<Role> roles) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
name, "N/A", SpringUserUtils.toAuthorityList(roles));
Expand All @@ -87,7 +88,7 @@ protected OAuth2Authentication createAuthentication(String ciamClientId, String
authToken.setDetails(detailsMap);

OAuth2Request request =
new OAuth2Request(null, ciamClientId, null, true, null, null, null, null, null);
new OAuth2Request(null, clientId, null, true, null, null, null, null, null);

return new OAuth2Authentication(request, authToken);
}
Expand Down Expand Up @@ -133,13 +134,48 @@ protected boolean verifyUserExist(JwtToken jwtToken) {
}

@Override
public boolean verify(HttpServletRequest httpRequest, String accessToken) {
public Authentication authenticate(HttpServletRequest httpRequest, String accessToken) throws OAuthAuthenticationException {
Optional<JwtToken> maybeJwtToken = JwtToken.instance(accessToken);
if (maybeJwtToken.isPresent()) {
return verify(httpRequest, maybeJwtToken.get());
boolean isValid = verify(httpRequest, maybeJwtToken.get());
if (!isValid) {
return createAuthentication(httpRequest, accessToken);
} else {
throw new IOAuthProvider.OAuthAuthenticationException("Authentication failed");

}
} else {
throw new IOAuthProvider.OAuthAuthenticationException(new InvalidTokenException("Specified token is invalid. Cannot authenticate"));
}
}


private static final String KEY_EMAIL = "email";

/**
* Creates an OAuth user for the given authentication object
*
* @param authentication
* @return
*/
@SuppressWarnings("rawtypes")
public OAuthUser createUser(Authentication authentication) {
OAuthUser user = new OAuthUser();
user.setUserId(authentication.getName());
user.setDisplayName(authentication.getName());

if (authentication instanceof OAuth2Authentication) {
Authentication userAuthentication = ((OAuth2Authentication)authentication).getUserAuthentication();
if (userAuthentication.getDetails() != null && ((Map)userAuthentication.getDetails()).containsKey(KEY_EMAIL)) {
user.setEmail((String)((Map)userAuthentication.getDetails()).get(KEY_EMAIL));
}
}

return false;
Set<String> roles = new HashSet<>();
authentication.getAuthorities().stream().forEach(e -> roles.add(e.getAuthority()));
user.setRoles(roles);

return user;
}

public boolean verify(HttpServletRequest httpRequest, JwtToken jwtToken) {
Expand All @@ -153,4 +189,14 @@ public boolean verify(HttpServletRequest httpRequest, JwtToken jwtToken) {

return verifyUserExist(jwtToken);
}

@Override
public boolean supportsWebflow() {
return false;
}

@Override
public Optional<IOAuthFlowConfiguration> getWebflowConfiguration() {
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.eclipse.vorto.repository.oauth;

import javax.servlet.Filter;
import org.eclipse.vorto.repository.web.listeners.AuthenticationSuccessHandler;
import org.eclipse.vorto.repository.web.security.UserDBAuthoritiesExtractor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.token.AccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;

public abstract class AbstractOAuthProviderConfiguration implements IOAuthFlowConfiguration {

@Autowired
protected AuthenticationSuccessHandler successHandler;

@Autowired
protected AccessTokenProvider accessTokenProvider;

@Autowired
protected OAuth2ClientContext oauth2ClientContext;

protected UserInfoTokenServices tokenService;

public AbstractOAuthProviderConfiguration(UserInfoTokenServices tokenService) {
this.tokenService = tokenService;
this.tokenService.setAuthoritiesExtractor(new UserDBAuthoritiesExtractor(getUserAttributeId()));
}


public UserInfoTokenServices getUserInfoTokenService() {
return this.tokenService;
}

public Filter createFilter() {
OAuth2RestTemplate restTemplate = createOAuthTemplate();

restTemplate.setAccessTokenProvider(accessTokenProvider);

OAuth2ClientAuthenticationProcessingFilter filter =
new OAuth2ClientAuthenticationProcessingFilter("/"+getFilterProcessingUrl());
filter.setAuthenticationSuccessHandler(successHandler);
tokenService.setRestTemplate(restTemplate);
tokenService.setAuthoritiesExtractor(authoritiesExtractor(getUserAttributeId()));
filter.setRestTemplate(restTemplate);
filter.setTokenServices(tokenService);

return filter;
}

protected OAuth2RestTemplate createOAuthTemplate() {
return new OAuth2RestTemplate(createDetails(), oauth2ClientContext);
}

protected abstract AuthorizationCodeResourceDetails createDetails();

public abstract String getFilterProcessingUrl();

protected abstract String getUserAttributeId();

@Bean
@Scope("prototype")
public AuthoritiesExtractor authoritiesExtractor(String userAttributeId) {
return new UserDBAuthoritiesExtractor(userAttributeId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.vorto.repository.oauth;

import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;

/**
* OAuth2 Webflow Configuration
*
* @author Alexander Edelmann (Robert Bosch (SEA) Pte. Ltd)
*
*/
public interface IOAuthFlowConfiguration {

/**
* Creates the filter that activates this oauth provider
* @return
*/
Filter createFilter();

/**
* Returns the user token information service for this provider
* @return
*/
UserInfoTokenServices getUserInfoTokenService();

/**
* Gets the logout Url for this oauth provider
* @param request
* @return
*/
String getLogoutUrl(HttpServletRequest request);

/**
* Gets the oauth provider specific logo url
* @return
*/
String getLogoHref();

/**
* Gets the url to initiate the oauth web flow
* @return
*/
String getFilterProcessingUrl();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* Copyright (c) 2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.vorto.repository.oauth;

import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;

/**
*
* @author Alexander Edelmann (Robert Bosch (SEA) Pte. Ltd)
*
*/
public interface IOAuthProvider {

/**
* A unique ID of this provider
* @return
*/
String getId();

/**
* A descriptive label for the OAuth Provider
* @return
*/
String getLabel();

/**
* Checks if the provider can handle the given authentication object
* @param auth
* @return
*/
boolean canHandle(Authentication authentication);

/**
*
* Checks if this provider can handle the given token
* @param jwtToken
* @return
*/
boolean canHandle(String jwtToken);

/**
* Performs the actual authentication for the given token
* @param request http servlet request
* @param jwtToken JSON Webtoken to authenticate
* @return Authentication object if the provided token is valid
*
* @throws OAuthAuthenticationException if the specific token request cannot be verified
*/
Authentication authenticate(HttpServletRequest request, String jwtToken) throws OAuthAuthenticationException;


/**
* Creates an OAuth user for the given authentication object
*
* @param authentication
* @return OAuthUser containing information about the authenticated user
*/
OAuthUser createUser(Authentication authentication);

/**
* Indicates if the provider supports OAuth Webflow. If yes, a {@link IOAuthProvider#getWebflowConfiguration()}} can be read
* @return true if the provider supports web flow , false otherwise
*/
boolean supportsWebflow();

/**
* Returns a webflow configuration for the oauth provider
* @return
*/
Optional<IOAuthFlowConfiguration> getWebflowConfiguration();


public class OAuthAuthenticationException extends Exception {

/**
*
*/
private static final long serialVersionUID = 1L;
public OAuthAuthenticationException(Throwable t) {
super(t);
}

public OAuthAuthenticationException(String msg) {
super(msg);
}
public OAuthAuthenticationException(String msg, Throwable t) {
super(msg,t);
}
}
}
Loading

0 comments on commit 2f3a9b2

Please sign in to comment.