forked from eclipse-sw360/sw360
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(client-management): added support to dynamically manage oauth cl…
…ients * added sso authentication support to standard requests * added liferay authentication support to standard requests * added REST service for basic oauth client management * added couchdb persistence for oauth client management * removed single static configurable oauth client * added refresh_token workflow to all configured clients * added some tests and javadoc For SSO users (basic auth liferay users can use other tools as well, but can also use this workflow): 1. open a browser with developer tools 2. open https://<sw360domain>/authorization/client-management 3. to add a new client, enter the following javascript in the dev tools console xmlHttpRequest = new XMLHttpRequest(); xmlHttpRequest.open('POST', '/authorization/client-management', false); xmlHttpRequest.setRequestHeader('Content-Type', 'application/json'); xmlHttpRequest.setRequestHeader('Accept', 'application/json'); xmlHttpRequest.send(JSON.stringify( { "description" : "my first test client", "authorities" : [ "BASIC" ], "scope" : [ "READ" ], "access_token_validity" : 3600, "refresh_token_validity" : 3600 } )); console.log(xmlHttpRequest.responseText); 4. to manipulate an existing client, do the same but add the clientid to the data object "client_id" : "9e358ca832ce4ce99a770c7bd0f8e066" 5. to remove an existing client, enter the following javascript in the dev tools console xmlHttpRequest = new XMLHttpRequest(); xmlHttpRequest.open('DELETE', '/authorization/client-management/9e358ca832ce4ce99a770c7bd0f8e066', false); xmlHttpRequest.setRequestHeader('Content-Type', 'application/json'); xmlHttpRequest.setRequestHeader('Accept', 'application/json'); xmlHttpRequest.send(); console.log(xmlHttpRequest.responseText); This way the session cookie of the SSO login will be used for the REST calls. This might also be possible in postman or curl or similar tools if you want to try to copy cookies (depending also on the SSO configuration). closes eclipse-sw360#543 Signed-off-by: Andreas Klett <[email protected]>
- Loading branch information
Showing
28 changed files
with
1,346 additions
and
294 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
197 changes: 197 additions & 0 deletions
197
...src/main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
/* | ||
* Copyright Siemens AG, 2019. Part of the SW360 Portal Project. | ||
* | ||
* SPDX-License-Identifier: EPL-1.0 | ||
* | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v1.0 | ||
* which accompanies this distribution, and is available at | ||
* http://www.eclipse.org/legal/epl-v10.html | ||
*/ | ||
package org.eclipse.sw360.rest.authserver.client.persistence; | ||
|
||
import com.fasterxml.jackson.annotation.JsonAnyGetter; | ||
import com.fasterxml.jackson.annotation.JsonAnySetter; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | ||
|
||
import org.ektorp.support.CouchDbDocument; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.authority.AuthorityUtils; | ||
import org.springframework.security.oauth2.provider.ClientDetails; | ||
import org.springframework.security.oauth2.provider.client.BaseClientDetails; | ||
import org.springframework.security.oauth2.provider.client.Jackson2ArrayOrStringDeserializer; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
public class OAuthClientEntity extends CouchDbDocument implements ClientDetails { | ||
|
||
private BaseClientDetails delegate; | ||
|
||
private String description; | ||
|
||
public OAuthClientEntity() { | ||
this.delegate = new BaseClientDetails(); | ||
} | ||
|
||
@Override | ||
@JsonProperty("client_id") | ||
public String getClientId() { | ||
return delegate.getClientId(); | ||
} | ||
|
||
@JsonProperty("client_id") | ||
public void setClientId(String clientId) { | ||
delegate.setClientId(clientId); | ||
} | ||
|
||
@Override | ||
@JsonProperty("client_secret") | ||
public String getClientSecret() { | ||
return delegate.getClientSecret(); | ||
} | ||
|
||
@JsonProperty("client_secret") | ||
public void setClientSecret(String clientSecret) { | ||
delegate.setClientSecret(clientSecret); | ||
} | ||
|
||
@JsonProperty("description") | ||
public String getDescription() { | ||
return description; | ||
} | ||
|
||
@JsonProperty("description") | ||
public void setDescription(String description) { | ||
this.description = description; | ||
} | ||
|
||
@Override | ||
@JsonProperty("resource_ids") | ||
@JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class) | ||
public Set<String> getResourceIds() { | ||
return delegate.getResourceIds(); | ||
} | ||
|
||
@JsonProperty("resource_ids") | ||
public void setResourceIds(Set<String> clientSecret) { | ||
delegate.setResourceIds(clientSecret); | ||
} | ||
|
||
@Override | ||
@JsonProperty("authorized_grant_types") | ||
@JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class) | ||
public Set<String> getAuthorizedGrantTypes() { | ||
return delegate.getAuthorizedGrantTypes(); | ||
} | ||
|
||
@JsonProperty("authorized_grant_types") | ||
public void setAuthorizedGrantTypes(Set<String> authorizedGrantTypes) { | ||
delegate.setAuthorizedGrantTypes(authorizedGrantTypes); | ||
} | ||
|
||
@Override | ||
public Collection<GrantedAuthority> getAuthorities() { | ||
return delegate.getAuthorities(); | ||
} | ||
|
||
public void setAuthorities(Collection<GrantedAuthority> authorities) { | ||
delegate.setAuthorities(authorities); | ||
} | ||
|
||
@JsonProperty("authorities") | ||
public Set<String> getAuthoritiesAsStrings() { | ||
return AuthorityUtils.authorityListToSet(delegate.getAuthorities()); | ||
} | ||
|
||
@JsonProperty("authorities") | ||
@JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class) | ||
public void setAuthoritiesAsStrings(Set<String> values) { | ||
delegate.setAuthorities(AuthorityUtils.createAuthorityList(values.toArray(new String[values.size()]))); | ||
} | ||
|
||
@Override | ||
@JsonProperty("scope") | ||
@JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class) | ||
public Set<String> getScope() { | ||
return delegate.getScope(); | ||
} | ||
|
||
@JsonProperty("scope") | ||
public void setScope(Set<String> scope) { | ||
delegate.setScope(scope); | ||
} | ||
|
||
@Override | ||
@JsonProperty("redirect_uri") | ||
@JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class) | ||
public Set<String> getRegisteredRedirectUri() { | ||
return delegate.getRegisteredRedirectUri(); | ||
} | ||
|
||
@JsonProperty("redirect_uri") | ||
public void setRegisteredRedirectUri(Set<String> registeredRedirectUri) { | ||
delegate.setRegisteredRedirectUri(registeredRedirectUri); | ||
} | ||
|
||
@Override | ||
@JsonProperty("access_token_validity") | ||
public Integer getAccessTokenValiditySeconds() { | ||
return delegate.getAccessTokenValiditySeconds(); | ||
} | ||
|
||
@JsonProperty("access_token_validity") | ||
public void setAccessTokenValiditySeconds(Integer accessTokenValiditySeconds) { | ||
delegate.setAccessTokenValiditySeconds(accessTokenValiditySeconds); | ||
} | ||
|
||
@Override | ||
@JsonProperty("refresh_token_validity") | ||
public Integer getRefreshTokenValiditySeconds() { | ||
return delegate.getRefreshTokenValiditySeconds(); | ||
} | ||
|
||
@JsonProperty("refresh_token_validity") | ||
public void setRefreshTokenValiditySeconds(Integer refreshTokenValiditySeconds) { | ||
delegate.setRefreshTokenValiditySeconds(refreshTokenValiditySeconds); | ||
} | ||
|
||
@JsonProperty("autoapprove") | ||
public Set<String> getAutoApproveScopes() { | ||
return delegate.getAutoApproveScopes(); | ||
} | ||
|
||
@JsonProperty("autoapprove") | ||
public void setAutoApproveScopes(Set<String> autoApproveScopes) { | ||
delegate.setAutoApproveScopes(autoApproveScopes); | ||
} | ||
|
||
@Override | ||
@JsonAnyGetter | ||
public Map<String, Object> getAdditionalInformation() { | ||
return delegate.getAdditionalInformation(); | ||
} | ||
|
||
@JsonAnySetter | ||
public void addAdditionalInformation(String key, Object value) { | ||
delegate.addAdditionalInformation(key, value); | ||
} | ||
|
||
@Override | ||
public boolean isSecretRequired() { | ||
return delegate.isSecretRequired(); | ||
} | ||
|
||
@Override | ||
public boolean isScoped() { | ||
return delegate.isScoped(); | ||
} | ||
|
||
@Override | ||
public boolean isAutoApprove(String scope) { | ||
return delegate.isAutoApprove(scope); | ||
} | ||
|
||
} |
77 changes: 77 additions & 0 deletions
77
...main/java/org/eclipse/sw360/rest/authserver/client/persistence/OAuthClientRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* Copyright Siemens AG, 2019. Part of the SW360 Portal Project. | ||
* | ||
* SPDX-License-Identifier: EPL-1.0 | ||
* | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v1.0 | ||
* which accompanies this distribution, and is available at | ||
* http://www.eclipse.org/legal/epl-v10.html | ||
*/ | ||
package org.eclipse.sw360.rest.authserver.client.persistence; | ||
|
||
import org.ektorp.ViewQuery; | ||
import org.ektorp.http.StdHttpClient; | ||
import org.ektorp.impl.StdCouchDbConnector; | ||
import org.ektorp.impl.StdCouchDbInstance; | ||
import org.ektorp.support.CouchDbRepositorySupport; | ||
import org.ektorp.support.View; | ||
import org.ektorp.support.Views; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.net.MalformedURLException; | ||
import java.util.List; | ||
|
||
/** | ||
* This repository can perform CRUD operations on a CouchDB for | ||
* {@link OAuthClientEntity}s. The necessary configuration for the CouchDB | ||
* connection has to be available to Spring's {@link Value} infrastructure. | ||
*/ | ||
@Component | ||
@Views({ | ||
@View(name = "all", map = "function(doc) { emit(null, doc._id); }"), | ||
@View(name = "byId", map = "function(doc) { emit(doc._id, doc); }"), | ||
@View(name = "byClientId", map = "function(doc) { emit(doc.client_id, doc); }") | ||
}) | ||
public class OAuthClientRepository extends CouchDbRepositorySupport<OAuthClientEntity> { | ||
|
||
protected OAuthClientRepository( | ||
@Value("${couchdb.url}") final String dbUrl, | ||
@Value("${couchdb.database}") final String dbName, | ||
@Value("${couchdb.username:#{null}}") final String dbUsername, | ||
@Value("${couchdb.password:#{null}}") final String dbPassword) throws MalformedURLException { | ||
|
||
super(OAuthClientEntity.class, new StdCouchDbConnector(dbName, | ||
new StdCouchDbInstance( | ||
new StdHttpClient.Builder() | ||
.url(dbUrl) | ||
.username(dbUsername) | ||
.password(dbPassword) | ||
.build() | ||
) | ||
) | ||
); | ||
|
||
initStandardDesignDocument(); | ||
} | ||
|
||
public OAuthClientEntity getByClientId(String clientId) { | ||
ViewQuery query = createQuery("byClientId"); | ||
query.setIgnoreNotFound(true); | ||
query.key(clientId); | ||
|
||
List<OAuthClientEntity> clients = db.queryView(query, OAuthClientEntity.class); | ||
if (clients.size() < 1) { | ||
log.warn("No clients found for clientId <{}>.", clientId); | ||
return null; | ||
} else if (clients.size() > 1) { | ||
log.warn("More than one client found ({}) for clientId <{}>.", clients.size(), clientId); | ||
return clients.get(0); | ||
} else { | ||
log.debug("Client found for clientId <{}>", clientId); | ||
return clients.get(0); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.