Adding new v2 REST services

This commit is contained in:
Martin Stockhammer 2020-07-07 22:34:31 +02:00
parent e9ea418911
commit 87b089a695
19 changed files with 678 additions and 36 deletions

View File

@ -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>

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 } )

View File

@ -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;

View File

@ -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" )
}
)

View File

@ -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>

View File

@ -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

View File

@ -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()

View File

@ -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 );
}

View File

@ -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;
}
}

View File

@ -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 )
{

View File

@ -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"/>

View File

@ -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 )
{

View File

@ -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" ) );
}
}
}

View File

@ -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 );
}