Adding new v2 REST services
This commit is contained in:
parent
e9ea418911
commit
87b089a695
5
pom.xml
5
pom.xml
|
@ -524,6 +524,11 @@
|
|||
<artifactId>jackson-jaxrs-xml-provider</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
|
|
@ -70,7 +70,7 @@ public class LoginRequest
|
|||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( "LoginRequest" );
|
||||
sb.append( "{username='" ).append( username ).append( '\'' );
|
||||
sb.append( ", password='" ).append( password ).append( '\'' );
|
||||
sb.append( ", password='" ).append( "*********" ).append( '\'' );
|
||||
sb.append( '}' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
|
|
@ -22,37 +22,34 @@ import javax.xml.bind.annotation.XmlRootElement;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A Paged result puts the data into an envelope
|
||||
* @author Martin Stockhammer <martin_s@apache.org>
|
||||
*/
|
||||
@XmlRootElement(name="pagedResult")
|
||||
public class PagedResult<T>
|
||||
{
|
||||
PaginationInfo pagination;
|
||||
List<T> data;
|
||||
T data;
|
||||
|
||||
public PagedResult() {
|
||||
|
||||
}
|
||||
|
||||
public PagedResult( long totalCount, long offset, long limit, List<T> data ) {
|
||||
public PagedResult( long totalCount, long offset, long limit, T data ) {
|
||||
this.data = data;
|
||||
this.pagination = new PaginationInfo( totalCount, offset, limit );
|
||||
}
|
||||
|
||||
public static final <T> PagedResult<T> ofAllElements(long offset, long limit, List<T> elements) {
|
||||
return new PagedResult( elements.size( ), offset, limit, elements.subList( (int)offset, (int)offset + (int)limit ) );
|
||||
public static final <T> PagedResult<T> of(long totalSize, long offset, long limit, T element) {
|
||||
return new PagedResult( totalSize, offset, limit, element);
|
||||
}
|
||||
|
||||
public static final <T> PagedResult<T> ofSegment(long totalSize, long offset, long limit, List<T> elements) {
|
||||
return new PagedResult( totalSize, offset, limit, elements);
|
||||
}
|
||||
|
||||
public List<T> getData( )
|
||||
public T getData( )
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData( List<T> data )
|
||||
public void setData( T data )
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@ import javax.xml.bind.annotation.XmlElement;
|
|||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
*
|
||||
* Informational attributes for pagination.
|
||||
*
|
||||
* @author Martin Stockhammer <martin_s@apache.org>
|
||||
*/
|
||||
@XmlRootElement(name="pagination")
|
||||
|
@ -42,7 +45,6 @@ public class PaginationInfo
|
|||
this.limit = limit;
|
||||
}
|
||||
|
||||
@XmlElement(name="total_count")
|
||||
public long getTotalCount( )
|
||||
{
|
||||
return totalCount;
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package org.apache.archiva.redback.rest.api.model;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import org.apache.archiva.redback.keys.AuthenticationKey;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Represents a authentication token.
|
||||
* @author Martin Stockhammer <martin_s@apache.org>
|
||||
* @since 3.0
|
||||
*/
|
||||
@XmlRootElement( name = "token" )
|
||||
public class Token
|
||||
{
|
||||
String key;
|
||||
Instant created;
|
||||
Instant expires;
|
||||
String principal;
|
||||
String purpose;
|
||||
|
||||
public Token( )
|
||||
{
|
||||
}
|
||||
|
||||
public static Token of( AuthenticationKey key ) {
|
||||
Token token = new Token( );
|
||||
token.setKey( key.getKey() );
|
||||
token.setCreated( key.getDateCreated().toInstant() );
|
||||
token.setExpires( key.getDateExpires().toInstant() );
|
||||
token.setPrincipal( key.getForPrincipal() );
|
||||
token.setPurpose( key.getPurpose() );
|
||||
return token;
|
||||
}
|
||||
|
||||
public static Token of( String key, Date created, Date expires, String principal, String purpose)
|
||||
{
|
||||
Token token = new Token( );
|
||||
token.setKey( key );
|
||||
token.setCreated( created.toInstant( ) );
|
||||
token.setExpires( expires.toInstant( ) );
|
||||
token.setPrincipal( principal );
|
||||
token.setPrincipal( purpose );
|
||||
return token;
|
||||
}
|
||||
|
||||
public static Token of( String key, Instant created, Instant expires, String principal, String purpose )
|
||||
{
|
||||
Token token = new Token( );
|
||||
token.setKey( key );
|
||||
token.setCreated( created );
|
||||
token.setExpires( expires );
|
||||
token.setPrincipal( principal );
|
||||
token.setPrincipal( purpose );
|
||||
return token;
|
||||
}
|
||||
|
||||
public String getKey( )
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey( String key )
|
||||
{
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Instant getCreated( )
|
||||
{
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated( Instant created )
|
||||
{
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Instant getExpires( )
|
||||
{
|
||||
return expires;
|
||||
}
|
||||
|
||||
public void setExpires( Instant expires )
|
||||
{
|
||||
this.expires = expires;
|
||||
}
|
||||
|
||||
public String getPrincipal( )
|
||||
{
|
||||
return principal;
|
||||
}
|
||||
|
||||
public void setPrincipal( String principal )
|
||||
{
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public String getPurpose( )
|
||||
{
|
||||
return purpose;
|
||||
}
|
||||
|
||||
public void setPurpose( String purpose )
|
||||
{
|
||||
this.purpose = purpose;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package org.apache.archiva.redback.rest.api.model;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author Martin Stockhammer <martin_s@apache.org>
|
||||
*/
|
||||
@XmlRootElement(name="userLogin")
|
||||
public class UserLogin extends User
|
||||
{
|
||||
|
||||
String authToken;
|
||||
String base64AuthToken;
|
||||
|
||||
public UserLogin( )
|
||||
{
|
||||
}
|
||||
|
||||
public UserLogin( String username, String fullName, String email, boolean validated, boolean locked, String authToken )
|
||||
{
|
||||
super( username, fullName, email, validated, locked );
|
||||
this.authToken = authToken;
|
||||
this.base64AuthToken = Base64.getEncoder( ).encodeToString( authToken.getBytes( ) );
|
||||
}
|
||||
|
||||
public UserLogin( org.apache.archiva.redback.users.User user, String authToken )
|
||||
{
|
||||
super( user );
|
||||
this.authToken = authToken;
|
||||
this.base64AuthToken = Base64.getEncoder( ).encodeToString( authToken.getBytes( ) );
|
||||
}
|
||||
|
||||
public String getAuthToken( )
|
||||
{
|
||||
return authToken;
|
||||
}
|
||||
|
||||
public void setAuthToken( String authToken )
|
||||
{
|
||||
this.authToken = authToken;
|
||||
}
|
||||
|
||||
public String getBase64AuthToken( )
|
||||
{
|
||||
return base64AuthToken;
|
||||
}
|
||||
|
||||
public void setBase64AuthToken( String base64AuthToken )
|
||||
{
|
||||
this.base64AuthToken = base64AuthToken;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.apache.archiva.redback.rest.api.services;
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.apache.archiva.redback.authorization.RedbackAuthorization;
|
||||
import org.apache.archiva.redback.keys.AuthenticationKey;
|
||||
import org.apache.archiva.redback.rest.api.model.ActionStatus;
|
||||
|
@ -34,20 +35,23 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
@Deprecated
|
||||
@Path( "/loginService/" )
|
||||
public interface LoginService
|
||||
{
|
||||
|
||||
@Operation( deprecated = true )
|
||||
@Path( "addAuthenticationKey" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
@RedbackAuthorization( noRestriction = true )
|
||||
AuthenticationKeyResult addAuthenticationKey( @QueryParam( "providerKey" ) String providedKey,
|
||||
String addAuthenticationKey( @QueryParam( "providerKey" ) String providedKey,
|
||||
@QueryParam( "principal" ) String principal, @QueryParam( "purpose" ) String purpose,
|
||||
@QueryParam( "expirationMinutes" ) int expirationMinutes )
|
||||
throws RedbackServiceException;
|
||||
|
||||
|
||||
@Operation( deprecated = true )
|
||||
@Path( "ping" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
|
@ -56,6 +60,7 @@ public interface LoginService
|
|||
throws RedbackServiceException;
|
||||
|
||||
|
||||
@Operation( deprecated = true )
|
||||
@Path( "pingWithAutz" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
|
@ -67,6 +72,7 @@ public interface LoginService
|
|||
* check username/password and create a http session.
|
||||
* So no more need of reuse username/password for all ajaxRequest
|
||||
*/
|
||||
@Operation( deprecated = true )
|
||||
@Path( "logIn" )
|
||||
@POST
|
||||
@RedbackAuthorization( noRestriction = true, noPermission = true )
|
||||
|
@ -78,6 +84,7 @@ public interface LoginService
|
|||
* simply check if current user has an http session opened with authz passed and return user data
|
||||
* @since 1.4
|
||||
*/
|
||||
@Operation( deprecated = true )
|
||||
@Path( "isLogged" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
|
||||
|
@ -89,6 +96,7 @@ public interface LoginService
|
|||
* clear user http session
|
||||
* @since 1.4
|
||||
*/
|
||||
@Operation( deprecated = true )
|
||||
@Path( "logout" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
|
|
|
@ -24,7 +24,9 @@ import org.apache.archiva.redback.rest.api.model.ActionStatus;
|
|||
import org.apache.archiva.redback.rest.api.model.AuthenticationKeyResult;
|
||||
import org.apache.archiva.redback.rest.api.model.LoginRequest;
|
||||
import org.apache.archiva.redback.rest.api.model.PingResult;
|
||||
import org.apache.archiva.redback.rest.api.model.Token;
|
||||
import org.apache.archiva.redback.rest.api.model.User;
|
||||
import org.apache.archiva.redback.rest.api.model.UserLogin;
|
||||
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
|
@ -34,23 +36,27 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
* Version 2 of authentication service
|
||||
*/
|
||||
@Path( "/auth" )
|
||||
public interface LoginService
|
||||
public interface AuthenticationService
|
||||
{
|
||||
|
||||
@Path( "requestkey" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
@Produces( { MediaType.APPLICATION_JSON } )
|
||||
@RedbackAuthorization( noRestriction = true )
|
||||
AuthenticationKeyResult addAuthenticationKey( @QueryParam( "providerKey" ) String providedKey,
|
||||
@QueryParam( "principal" ) String principal, @QueryParam( "purpose" ) String purpose,
|
||||
@QueryParam( "expirationMinutes" ) int expirationMinutes )
|
||||
Token requestOnetimeToken( @QueryParam( "providerKey" ) String providedKey,
|
||||
@QueryParam( "principal" ) String principal,
|
||||
@QueryParam( "purpose" ) String purpose,
|
||||
@QueryParam( "expirationSeconds" ) int expirationSeconds )
|
||||
throws RedbackServiceException;
|
||||
|
||||
|
||||
@Path( "ping" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
@Produces( { MediaType.APPLICATION_JSON } )
|
||||
@RedbackAuthorization( noRestriction = true )
|
||||
PingResult ping()
|
||||
throws RedbackServiceException;
|
||||
|
@ -58,7 +64,7 @@ public interface LoginService
|
|||
|
||||
@Path( "ping/authenticated" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
@Produces( { MediaType.APPLICATION_JSON } )
|
||||
@RedbackAuthorization( noRestriction = false, noPermission = true )
|
||||
PingResult pingWithAutz()
|
||||
throws RedbackServiceException;
|
||||
|
@ -70,8 +76,8 @@ public interface LoginService
|
|||
@Path( "authenticate" )
|
||||
@POST
|
||||
@RedbackAuthorization( noRestriction = true, noPermission = true )
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
User logIn( LoginRequest loginRequest )
|
||||
@Produces( { MediaType.APPLICATION_JSON } )
|
||||
UserLogin logIn( LoginRequest loginRequest )
|
||||
throws RedbackServiceException;
|
||||
|
||||
/**
|
||||
|
@ -80,7 +86,7 @@ public interface LoginService
|
|||
*/
|
||||
@Path( "isAuthenticated" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
|
||||
@Produces( { MediaType.APPLICATION_JSON } )
|
||||
@RedbackAuthorization( noRestriction = true )
|
||||
User isLogged()
|
||||
throws RedbackServiceException;
|
||||
|
@ -91,7 +97,7 @@ public interface LoginService
|
|||
*/
|
||||
@Path( "logout" )
|
||||
@GET
|
||||
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
|
||||
@Produces( { MediaType.APPLICATION_JSON } )
|
||||
@RedbackAuthorization( noRestriction = true, noPermission = true )
|
||||
ActionStatus logout()
|
||||
throws RedbackServiceException;
|
|
@ -53,6 +53,7 @@ import java.util.List;
|
|||
public interface GroupService
|
||||
{
|
||||
|
||||
|
||||
@Path( "" )
|
||||
@GET
|
||||
@Produces( {MediaType.APPLICATION_JSON} )
|
||||
|
@ -62,8 +63,8 @@ public interface GroupService
|
|||
@ApiResponse( description = "List of group objects. The number of returned results depend on the pagination parameters offset and limit." )
|
||||
}
|
||||
)
|
||||
PagedResult<Group> getGroups( @QueryParam( "offset" ) @DefaultValue( "0" ) Long offset,
|
||||
@QueryParam( "limit" ) @DefaultValue( value = Long.MAX_VALUE+"" ) Long limit)
|
||||
PagedResult<List<Group>> getGroups( @QueryParam( "offset" ) @DefaultValue( "0" ) Integer offset,
|
||||
@QueryParam( "limit" ) @DefaultValue( value = Integer.MAX_VALUE+"" ) Integer limit)
|
||||
throws RedbackServiceException;
|
||||
|
||||
|
||||
|
@ -87,7 +88,7 @@ public interface GroupService
|
|||
@RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
|
||||
@Operation( summary = "Adds a group mapping",
|
||||
responses = {
|
||||
@ApiResponse( description = "The status of the add action" ),
|
||||
@ApiResponse( responseCode = "201", description = "The status of the add action" ),
|
||||
@ApiResponse( responseCode = "405", description = "Invalid input" )
|
||||
}
|
||||
)
|
||||
|
|
|
@ -145,6 +145,10 @@
|
|||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cxf</groupId>
|
||||
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
|
||||
|
|
|
@ -46,9 +46,11 @@ import java.util.Map;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @deprecated Use new API version {@link org.apache.archiva.redback.rest.services.v2.DefaultGroupService}
|
||||
* @author Olivier Lamy
|
||||
* @since 2.1
|
||||
*/
|
||||
@Deprecated
|
||||
@Service("ldapGroupMappingService#rest")
|
||||
public class DefaultLdapGroupMappingService
|
||||
implements LdapGroupMappingService
|
||||
|
|
|
@ -61,9 +61,11 @@ import java.util.List;
|
|||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @deprecated You should use new REST API version {@link org.apache.archiva.redback.rest.api.services.v2.AuthenticationService}
|
||||
* @author Olivier Lamy
|
||||
* @since 1.3
|
||||
*/
|
||||
@Deprecated
|
||||
@Service( "loginService#rest" )
|
||||
public class DefaultLoginService
|
||||
implements LoginService
|
||||
|
@ -90,7 +92,7 @@ public class DefaultLoginService
|
|||
}
|
||||
|
||||
|
||||
public AuthenticationKeyResult addAuthenticationKey( String providedKey, String principal, String purpose, int expirationMinutes )
|
||||
public String addAuthenticationKey( String providedKey, String principal, String purpose, int expirationMinutes )
|
||||
throws RedbackServiceException
|
||||
{
|
||||
KeyManager keyManager = securitySystem.getKeyManager();
|
||||
|
@ -121,7 +123,7 @@ public class DefaultLoginService
|
|||
|
||||
keyManager.addKey( key );
|
||||
|
||||
return new AuthenticationKeyResult( key.getKey( ) );
|
||||
return key.getKey( );
|
||||
}
|
||||
|
||||
public PingResult ping()
|
||||
|
|
|
@ -21,6 +21,8 @@ package org.apache.archiva.redback.rest.services.interceptors;
|
|||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -28,6 +30,7 @@ import org.springframework.stereotype.Service;
|
|||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* to setup some ObjectMapper configuration
|
||||
|
@ -46,6 +49,10 @@ public class JacksonJsonConfigurator
|
|||
{
|
||||
log.info( "configure jackson ObjectMapper" );
|
||||
objectMapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES );
|
||||
objectMapper.setAnnotationIntrospector( new JaxbAnnotationIntrospector( objectMapper.getTypeFactory() ) );
|
||||
objectMapper.registerModule( new JavaTimeModule( ) );
|
||||
objectMapper.setDateFormat( new SimpleDateFormat( "yyyyMMdd'T'HHmmss.SSSZ" ) );
|
||||
|
||||
xmlMapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES );
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
package org.apache.archiva.redback.rest.services.v2;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import org.apache.archiva.redback.authentication.AuthenticationConstants;
|
||||
import org.apache.archiva.redback.authentication.AuthenticationException;
|
||||
import org.apache.archiva.redback.authentication.AuthenticationFailureCause;
|
||||
import org.apache.archiva.redback.authentication.EncryptionFailedException;
|
||||
import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
|
||||
import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
|
||||
import org.apache.archiva.redback.keys.AuthenticationKey;
|
||||
import org.apache.archiva.redback.keys.KeyManager;
|
||||
import org.apache.archiva.redback.keys.jpa.model.JpaAuthenticationKey;
|
||||
import org.apache.archiva.redback.keys.memory.MemoryAuthenticationKey;
|
||||
import org.apache.archiva.redback.keys.memory.MemoryKeyManager;
|
||||
import org.apache.archiva.redback.policy.AccountLockedException;
|
||||
import org.apache.archiva.redback.policy.MustChangePasswordException;
|
||||
import org.apache.archiva.redback.rest.api.model.ActionStatus;
|
||||
import org.apache.archiva.redback.rest.api.model.ErrorMessage;
|
||||
import org.apache.archiva.redback.rest.api.model.LoginRequest;
|
||||
import org.apache.archiva.redback.rest.api.model.PingResult;
|
||||
import org.apache.archiva.redback.rest.api.model.Token;
|
||||
import org.apache.archiva.redback.rest.api.model.User;
|
||||
import org.apache.archiva.redback.rest.api.model.UserLogin;
|
||||
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
|
||||
import org.apache.archiva.redback.rest.api.services.v2.AuthenticationService;
|
||||
import org.apache.archiva.redback.system.SecuritySession;
|
||||
import org.apache.archiva.redback.system.SecuritySystem;
|
||||
import org.apache.archiva.redback.users.UserManagerException;
|
||||
import org.apache.archiva.redback.users.UserNotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
*
|
||||
* Authentication service provides REST methods for authentication and verification.
|
||||
*
|
||||
* @author Olivier Lamy
|
||||
* @author Martin Stockhammer
|
||||
* @since 3.0
|
||||
*/
|
||||
@Service( "v2.authenticationService#rest" )
|
||||
public class DefaultAuthenticationService
|
||||
implements AuthenticationService
|
||||
{
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger( DefaultAuthenticationService.class );
|
||||
|
||||
private SecuritySystem securitySystem;
|
||||
|
||||
private HttpAuthenticator httpAuthenticator;
|
||||
|
||||
@Context
|
||||
private HttpServletRequest httpServletRequest;
|
||||
|
||||
// validation token lifetime: 3 hours
|
||||
long tokenLifetime = 1000*3600*3;
|
||||
|
||||
@Inject
|
||||
public DefaultAuthenticationService( SecuritySystem securitySystem,
|
||||
@Named( "httpAuthenticator#basic" ) HttpAuthenticator httpAuthenticator )
|
||||
{
|
||||
this.securitySystem = securitySystem;
|
||||
this.httpAuthenticator = httpAuthenticator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Token requestOnetimeToken( String providedKey, String principal, String purpose, int expirationSeconds )
|
||||
throws RedbackServiceException
|
||||
{
|
||||
KeyManager keyManager = securitySystem.getKeyManager();
|
||||
AuthenticationKey key;
|
||||
|
||||
if ( keyManager instanceof MemoryKeyManager )
|
||||
{
|
||||
key = new MemoryAuthenticationKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
key = new JpaAuthenticationKey();
|
||||
}
|
||||
|
||||
key.setKey( providedKey );
|
||||
key.setForPrincipal( principal );
|
||||
key.setPurpose( purpose );
|
||||
|
||||
Instant now = Instant.now( );
|
||||
key.setDateCreated( Date.from( now ) );
|
||||
|
||||
if ( expirationSeconds >= 0 )
|
||||
{
|
||||
Duration expireDuration = Duration.ofSeconds( expirationSeconds );
|
||||
key.setDateExpires( Date.from( now.plus( expireDuration ) ) );
|
||||
}
|
||||
keyManager.addKey( key );
|
||||
return Token.of( key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public PingResult ping()
|
||||
throws RedbackServiceException
|
||||
{
|
||||
return new PingResult( true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PingResult pingWithAutz()
|
||||
throws RedbackServiceException
|
||||
{
|
||||
return new PingResult( true );
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserLogin logIn( LoginRequest loginRequest )
|
||||
throws RedbackServiceException
|
||||
{
|
||||
String userName = loginRequest.getUsername(), password = loginRequest.getPassword();
|
||||
PasswordBasedAuthenticationDataSource authDataSource =
|
||||
new PasswordBasedAuthenticationDataSource( userName, password );
|
||||
log.debug("Login for {}",userName);
|
||||
try
|
||||
{
|
||||
SecuritySession securitySession = securitySystem.authenticate( authDataSource );
|
||||
log.debug("Security session {}", securitySession);
|
||||
if ( securitySession.getAuthenticationResult().isAuthenticated() )
|
||||
{
|
||||
org.apache.archiva.redback.users.User user = securitySession.getUser();
|
||||
log.debug("user {} authenticated", user.getUsername());
|
||||
if ( !user.isValidated() )
|
||||
{
|
||||
log.info( "user {} not validated", user.getUsername() );
|
||||
return null;
|
||||
}
|
||||
UserLogin restUser = buildRestUser( user );
|
||||
restUser.setReadOnly( securitySystem.userManagerReadOnly() );
|
||||
// validationToken only set during login
|
||||
try {
|
||||
String validationToken = securitySystem.getTokenManager().encryptToken(user.getUsername(), tokenLifetime);
|
||||
restUser.setValidationToken(validationToken);
|
||||
log.debug("Validation Token set {}",validationToken);
|
||||
|
||||
} catch (EncryptionFailedException e) {
|
||||
log.error("Validation token could not be created "+e.getMessage());
|
||||
}
|
||||
|
||||
// here create an http session
|
||||
httpAuthenticator.authenticate( authDataSource, httpServletRequest.getSession( true ) );
|
||||
return restUser;
|
||||
}
|
||||
if ( securitySession.getAuthenticationResult() != null
|
||||
&& securitySession.getAuthenticationResult().getAuthenticationFailureCauses() != null )
|
||||
{
|
||||
List<ErrorMessage> errorMessages = new ArrayList<ErrorMessage>();
|
||||
for ( AuthenticationFailureCause authenticationFailureCause : securitySession.getAuthenticationResult().getAuthenticationFailureCauses() )
|
||||
{
|
||||
if ( authenticationFailureCause.getCause() == AuthenticationConstants.AUTHN_NO_SUCH_USER )
|
||||
{
|
||||
errorMessages.add( new ErrorMessage( "incorrect.username.password" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessages.add( new ErrorMessage().message( authenticationFailureCause.getMessage() ) );
|
||||
}
|
||||
}
|
||||
|
||||
throw new RedbackServiceException( errorMessages );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch ( AuthenticationException e )
|
||||
{
|
||||
throw new RedbackServiceException( e.getMessage(), Response.Status.FORBIDDEN.getStatusCode() );
|
||||
}
|
||||
catch ( UserNotFoundException | AccountLockedException e )
|
||||
{
|
||||
throw new RedbackServiceException( e.getMessage() );
|
||||
}
|
||||
catch ( MustChangePasswordException e )
|
||||
{
|
||||
return buildRestUser( e.getUser() );
|
||||
}
|
||||
catch ( UserManagerException e )
|
||||
{
|
||||
log.info( "UserManagerException: {}", e.getMessage() );
|
||||
List<ErrorMessage> errorMessages =
|
||||
Arrays.asList( new ErrorMessage().message( "UserManagerException: " + e.getMessage() ) );
|
||||
throw new RedbackServiceException( errorMessages );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public User isLogged()
|
||||
throws RedbackServiceException
|
||||
{
|
||||
SecuritySession securitySession = httpAuthenticator.getSecuritySession( httpServletRequest.getSession( true ) );
|
||||
Boolean isLogged = securitySession != null;
|
||||
log.debug( "isLogged {}", isLogged );
|
||||
return isLogged && securitySession.getUser() != null ? buildRestUser( securitySession.getUser() ) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionStatus logout()
|
||||
throws RedbackServiceException
|
||||
{
|
||||
HttpSession httpSession = httpServletRequest.getSession();
|
||||
if ( httpSession != null )
|
||||
{
|
||||
httpSession.invalidate();
|
||||
}
|
||||
return ActionStatus.SUCCESS;
|
||||
}
|
||||
|
||||
private Calendar getNowGMT()
|
||||
{
|
||||
return Calendar.getInstance( TimeZone.getTimeZone( "GMT" ) );
|
||||
}
|
||||
|
||||
private UserLogin buildRestUser( org.apache.archiva.redback.users.User user )
|
||||
{
|
||||
UserLogin restUser = new UserLogin();
|
||||
restUser.setEmail( user.getEmail() );
|
||||
restUser.setUsername( user.getUsername() );
|
||||
restUser.setPasswordChangeRequired( user.isPasswordChangeRequired() );
|
||||
restUser.setLocked( user.isLocked() );
|
||||
restUser.setValidated( user.isValidated() );
|
||||
restUser.setFullName( user.getFullName() );
|
||||
return restUser;
|
||||
}
|
||||
}
|
|
@ -40,6 +40,9 @@ import javax.inject.Inject;
|
|||
import javax.inject.Named;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -60,6 +63,9 @@ public class DefaultGroupService
|
|||
{
|
||||
private final Logger log = LoggerFactory.getLogger( getClass() );
|
||||
|
||||
@Context //injected response proxy supporting multiple threads
|
||||
private HttpServletResponse response;
|
||||
|
||||
@Inject
|
||||
@Named(value = "ldapRoleMapper#default")
|
||||
private LdapRoleMapper ldapRoleMapper;
|
||||
|
@ -82,7 +88,7 @@ public class DefaultGroupService
|
|||
}
|
||||
|
||||
@Override
|
||||
public PagedResult<Group> getGroups( Long offset, Long limit ) throws RedbackServiceException
|
||||
public PagedResult<List<Group>> getGroups( Integer offset, Integer limit ) throws RedbackServiceException
|
||||
{
|
||||
LdapConnection ldapConnection = null;
|
||||
|
||||
|
@ -93,7 +99,7 @@ public class DefaultGroupService
|
|||
ldapConnection = ldapConnectionFactory.getConnection();
|
||||
context = ldapConnection.getDirContext();
|
||||
List<LdapGroup> groups = ldapRoleMapper.getAllGroupObjects( context );
|
||||
return PagedResult.ofSegment( groups.size( ), offset, limit, groups.stream( ).skip( offset ).limit( limit ).map( DefaultGroupService::getGroupFromLdap ).collect( Collectors.toList( ) ) );
|
||||
return PagedResult.of( groups.size( ), offset, limit, groups.stream( ).skip( offset ).limit( limit ).map( DefaultGroupService::getGroupFromLdap ).collect( Collectors.toList( ) ) );
|
||||
}
|
||||
catch ( LdapException | MappingException e )
|
||||
{
|
||||
|
@ -138,6 +144,7 @@ public class DefaultGroupService
|
|||
{
|
||||
ldapRoleMapperConfiguration.addLdapMapping( ldapGroupMapping.getGroup(),
|
||||
new ArrayList<>( ldapGroupMapping.getRoleNames() ) );
|
||||
response.setStatus( Response.Status.CREATED.getStatusCode() );
|
||||
}
|
||||
catch ( MappingException e )
|
||||
{
|
||||
|
|
|
@ -49,6 +49,11 @@
|
|||
|
||||
<bean id="redbackJacksonJsonMapper" class="com.fasterxml.jackson.databind.ObjectMapper" >
|
||||
<property name="annotationIntrospector" ref="jacksonAnnotationIntrospector"/>
|
||||
<property name="dateFormat">
|
||||
<bean class="java.text.SimpleDateFormat">
|
||||
<constructor-arg type="java.lang.String" value="yyyyMMdd'T'HHmmss.SSSZ"/>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
<bean id="redbackJacksonXMLMapper" class="com.fasterxml.jackson.dataformat.xml.XmlMapper" >
|
||||
<property name="annotationIntrospector" ref="jacksonAnnotationIntrospector"/>
|
||||
|
@ -58,7 +63,7 @@
|
|||
|
||||
|
||||
|
||||
<jaxrs:server id="redbackServices" address="/redbackServices">
|
||||
<jaxrs:server name="redbackServices" address="/redbackServices">
|
||||
|
||||
<jaxrs:serviceBeans>
|
||||
<ref bean="userService#rest"/>
|
||||
|
@ -81,10 +86,10 @@
|
|||
</jaxrs:providers>
|
||||
</jaxrs:server>
|
||||
|
||||
<jaxrs:server address="/redback/v2">
|
||||
<jaxrs:server address="/v2/redback">
|
||||
<jaxrs:serviceBeans>
|
||||
<ref bean="userService#rest"/>
|
||||
<ref bean="loginService#rest"/>
|
||||
<ref bean="v2.authenticationService#rest"/>
|
||||
<ref bean="roleManagementService#rest"/>
|
||||
<ref bean="utilServices#rest"/>
|
||||
<ref bean="passwordService#rest"/>
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.archiva.redback.rest.api.services.LdapGroupMappingService;
|
|||
import org.apache.archiva.redback.rest.api.services.LoginService;
|
||||
import org.apache.archiva.redback.rest.api.services.RoleManagementService;
|
||||
import org.apache.archiva.redback.rest.api.services.UserService;
|
||||
import org.apache.archiva.redback.rest.api.services.v2.AuthenticationService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.apache.cxf.common.util.Base64Utility;
|
||||
|
@ -257,6 +258,27 @@ public abstract class AbstractRestServicesTest
|
|||
return service;
|
||||
}
|
||||
|
||||
protected AuthenticationService getLoginServiceV2( String authzHeader )
|
||||
{
|
||||
AuthenticationService service =
|
||||
JAXRSClientFactory.create( "http://localhost:" + getServerPort() + "/" + getRestServicesPath() + "/v2/redback/",
|
||||
AuthenticationService.class, Collections.singletonList( new JacksonJaxbJsonProvider() ) );
|
||||
|
||||
// for debuging purpose
|
||||
WebClient.getConfig( service ).getHttpConduit().getClient().setReceiveTimeout( getTimeout() );
|
||||
|
||||
if ( authzHeader != null )
|
||||
{
|
||||
WebClient.client( service ).header( "Authorization", authzHeader );
|
||||
}
|
||||
WebClient.client(service).header("Referer","http://localhost:"+getServerPort());
|
||||
|
||||
WebClient.client( service ).accept( MediaType.APPLICATION_JSON_TYPE );
|
||||
WebClient.client( service ).type( MediaType.APPLICATION_JSON_TYPE );
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
protected LdapGroupMappingService getLdapGroupMappingService( String authzHeader )
|
||||
{
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
package org.apache.archiva.redback.rest.services.v2;
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
|
||||
import org.apache.archiva.redback.rest.api.model.LoginRequest;
|
||||
import org.apache.archiva.redback.rest.api.model.User;
|
||||
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
|
||||
import org.apache.archiva.redback.rest.api.services.UserService;
|
||||
import org.apache.archiva.redback.rest.services.AbstractRestServicesTest;
|
||||
import org.apache.archiva.redback.rest.services.FakeCreateAdminService;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Olivier Lamy
|
||||
*/
|
||||
@RunWith( SpringJUnit4ClassRunner.class )
|
||||
@ContextConfiguration(
|
||||
locations = { "classpath:/spring-context.xml" } )
|
||||
public class AuthenticationServiceTest
|
||||
extends AbstractRestServicesTest
|
||||
{
|
||||
@Test
|
||||
public void loginAdmin()
|
||||
throws Exception
|
||||
{
|
||||
assertNotNull( getLoginService( null ).logIn( new LoginRequest( RedbackRoleConstants.ADMINISTRATOR_ACCOUNT_NAME,
|
||||
FakeCreateAdminService.ADMIN_TEST_PWD ) ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUserThenLog()
|
||||
throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
// START SNIPPET: create-user
|
||||
User user = new User( "toto", "toto the king", "toto@toto.fr", false, false );
|
||||
user.setPassword( "foo123" );
|
||||
user.setPermanent( false );
|
||||
user.setPasswordChangeRequired( false );
|
||||
user.setLocked( false );
|
||||
user.setValidated( true );
|
||||
UserService userService = getUserService( authorizationHeader );
|
||||
userService.createUser( user );
|
||||
// END SNIPPET: create-user
|
||||
user = userService.getUser( "toto" );
|
||||
assertNotNull( user );
|
||||
assertEquals( "toto the king", user.getFullName() );
|
||||
assertEquals( "toto@toto.fr", user.getEmail() );
|
||||
getLoginServiceV2( encode( "toto", "foo123" ) ).pingWithAutz();
|
||||
}
|
||||
finally
|
||||
{
|
||||
getUserService( authorizationHeader ).deleteUser( "toto" );
|
||||
getUserService( authorizationHeader ).removeFromCache( "toto" );
|
||||
assertNull( getUserService( authorizationHeader ).getUser( "toto" ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleLogin() throws RedbackServiceException
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
// START SNIPPET: create-user
|
||||
User user = new User( "toto", "toto the king", "toto@toto.fr", false, false );
|
||||
user.setPassword( "foo123" );
|
||||
user.setPermanent( false );
|
||||
user.setPasswordChangeRequired( false );
|
||||
user.setLocked( false );
|
||||
user.setValidated( true );
|
||||
UserService userService = getUserService( authorizationHeader );
|
||||
userService.createUser( user );
|
||||
// END SNIPPET: create-user
|
||||
LoginRequest request = new LoginRequest( "toto", "foo123" );
|
||||
User result = getLoginServiceV2( "" ).logIn( request );
|
||||
assertNotNull( result );
|
||||
assertEquals( "toto", result.getUsername( ) );
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
getUserService( authorizationHeader ).deleteUser( "toto" );
|
||||
getUserService( authorizationHeader ).removeFromCache( "toto" );
|
||||
assertNull( getUserService( authorizationHeader ).getUser( "toto" ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -73,7 +73,7 @@ public class GroupServiceTest
|
|||
protected GroupService getGroupService( String authzHeader )
|
||||
{
|
||||
GroupService service =
|
||||
JAXRSClientFactory.create( "http://localhost:" + getServerPort() + "/" + getRestServicesPath() + "/redback/v2/",
|
||||
JAXRSClientFactory.create( "http://localhost:" + getServerPort() + "/" + getRestServicesPath() + "/v2/redback/",
|
||||
GroupService.class,
|
||||
Collections.singletonList( new JacksonJaxbJsonProvider() ) );
|
||||
|
||||
|
@ -193,7 +193,7 @@ public class GroupServiceTest
|
|||
{
|
||||
GroupService service = getGroupService( authorizationHeader );
|
||||
|
||||
List<String> allGroups = service.getGroups( Long.valueOf( 0 ), Long.valueOf( Long.MAX_VALUE ) ).getData().stream( ).map( group -> group.getName( ) ).collect( Collectors.toList( ) );
|
||||
List<String> allGroups = service.getGroups( Integer.valueOf( 0 ), Integer.valueOf( Integer.MAX_VALUE ) ).getData().stream( ).map( group -> group.getName( ) ).collect( Collectors.toList( ) );
|
||||
|
||||
assertThat( allGroups ).isNotNull().isNotEmpty().hasSize( 3 ).containsAll( groups );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue