Creating next generation REST API

This commit is contained in:
Martin Stockhammer 2020-07-07 00:02:39 +02:00
parent 3c452c38f2
commit 023d88d7b0
24 changed files with 2092 additions and 5 deletions

View File

@ -113,6 +113,9 @@ public class DefaultLdapRoleMapper
public static String DEFAULT_GROUP_NAME_ATTRIBUTE = "cn";
private String groupNameAttribute = DEFAULT_GROUP_NAME_ATTRIBUTE;
public static String DEFAULT_DESCRIPTION_ATTRIBUTE = "description";
private String descriptionAttribute = DEFAULT_DESCRIPTION_ATTRIBUTE;
// True, if the member attribute stores the DN, otherwise the userkey is used as entry value
private boolean useDnAsMemberValue = true;
@ -150,6 +153,8 @@ public class DefaultLdapRoleMapper
this.dnAttr = userConf.getString( UserConfigurationKeys.LDAP_DN_ATTRIBUTE, this.dnAttr );
this.groupNameAttribute = userConf.getString( UserConfigurationKeys.LDAP_GROUP_NAME_ATTRIBUTE, DEFAULT_GROUP_NAME_ATTRIBUTE );
this.descriptionAttribute = userConf.getString( UserConfigurationKeys.LDAP_GROUP_DESCRIPTION_ATTRIBUTE, DEFAULT_DESCRIPTION_ATTRIBUTE );
}
@ -222,6 +227,93 @@ public class DefaultLdapRoleMapper
}
}
@Override
public List<LdapGroup> getAllGroupObjects( DirContext context ) throws MappingException
{
NamingEnumeration<SearchResult> namingEnumeration = null;
try
{
SearchControls searchControls = new SearchControls( );
searchControls.setDerefLinkFlag( true );
searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
searchControls.setReturningAttributes( new String[]{ this.getLdapDnAttribute(), "objectClass", groupNameAttribute} );
String filter = "objectClass=" + getLdapGroupClass( );
if ( !StringUtils.isEmpty( this.groupFilter ) )
{
filter = "(&(" + filter + ")(" + this.groupFilter + "))";
}
namingEnumeration = context.search( getGroupsDn( ), filter, searchControls );
List<LdapGroup> allGroups = new ArrayList<>( );
while ( namingEnumeration.hasMore( ) )
{
SearchResult searchResult = namingEnumeration.next( );
allGroups.add( getGroupFromResult( searchResult ) );
}
return allGroups;
}
catch ( LdapException e )
{
throw new MappingException( e.getMessage( ), e );
}
catch ( NamingException e )
{
throw new MappingException( e.getMessage( ), e );
}
finally
{
close( namingEnumeration );
}
}
LdapGroup getGroupFromResult(SearchResult searchResult) throws NamingException
{
LdapGroup group = new LdapGroup( searchResult.getNameInNamespace() );
Attribute attValue = searchResult.getAttributes( ).get( groupNameAttribute );
if ( attValue != null )
{
group.setName( attValue.get( ).toString( ) );
}
else
{
log.error( "Could not get group name from attribute {}. Group DN: {}", groupNameAttribute, searchResult.getNameInNamespace( ) );
}
attValue = searchResult.getAttributes( ).get( descriptionAttribute );
if (attValue!=null) {
group.setDescription( attValue.get( ).toString( ) );
}
Attribute memberValues = searchResult.getAttributes( ).get( ldapGroupMemberAttribute );
if (memberValues!=null)
{
NamingEnumeration<?> allMembersEnum = memberValues.getAll( );
try
{
while ( allMembersEnum.hasMore( ) )
{
String memberValue = allMembersEnum.next( ).toString( );
if ( !StringUtils.isEmpty( memberValue ) )
{
group.addMember( memberValue );
}
}
} finally
{
if (allMembersEnum!=null) {
closeNamingEnumeration( allMembersEnum );
}
}
}
return group;
}
protected void closeNamingEnumeration( NamingEnumeration namingEnumeration )
{
if ( namingEnumeration != null )
@ -379,6 +471,9 @@ public class DefaultLdapRoleMapper
}
}
/*
* TODO: Should use LDAP search, as this may not work for users in subtrees
*/
private String getUserDnFromId(String userKey) {
return new StringBuilder().append( this.userIdAttribute ).append( "=" ).append( userKey ).append( "," ).append(
getBaseDn( ) ).toString();
@ -407,6 +502,7 @@ public class DefaultLdapRoleMapper
User user = userManager.findUser( username );
if ( user != null && user instanceof LdapUser )
{
// TODO: This is some kind of memberOf retrieval, but will not work. Need to setup a memberOf Attribute
LdapUser ldapUser = (LdapUser) user ;
Attribute dnAttribute = ldapUser.getOriginalAttributes( ).get( getLdapDnAttribute( ) );
if ( dnAttribute != null )
@ -476,6 +572,98 @@ public class DefaultLdapRoleMapper
}
}
/*
* TODO: We should implement recursive group retrieval
* Need a configuration flag, to activate recursion
*/
@Override
public List<LdapGroup> getGroupObjects( String username, DirContext context ) throws MappingException
{
Set<LdapGroup> userGroups = new HashSet<>( );
NamingEnumeration<SearchResult> namingEnumeration = null;
try
{
SearchControls searchControls = new SearchControls( );
searchControls.setDerefLinkFlag( true );
searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
String userIdentifier = null;
String userDn = null;
try
{
//try to look the user up
User user = userManager.findUser( username );
if ( user != null && user instanceof LdapUser )
{
// TODO: This is some kind of memberOf retrieval, but will not work with DN.
// We need a configuration entry for the memberOf attribute and a flag, if this should be used
LdapUser ldapUser = (LdapUser) user ;
Attribute dnAttribute = ldapUser.getOriginalAttributes( ).get( getLdapDnAttribute( ) );
if ( dnAttribute != null )
{
userIdentifier = dnAttribute.get( ).toString();
}
userDn = ldapUser.getDn( );
}
}
catch ( UserNotFoundException e )
{
log.warn( "Failed to look up user {}. Computing distinguished name manually", username, e );
}
catch ( UserManagerException e )
{
log.warn( "Failed to look up user {}. Computing distinguished name manually", username, e );
}
if ( userIdentifier == null )
{
//failed to look up the user's groupEntry directly
if ( this.useDnAsMemberValue )
{
userIdentifier = userDn;
}
else
{
userIdentifier = username;
}
}
String filter =
new StringBuilder( ).append( "(&" ).append( "(objectClass=" + getLdapGroupClass( ) + ")" ).append(
"(" ).append( getLdapGroupMemberAttribute( ) ).append( "=" ).append( Rdn.escapeValue( userIdentifier ) ).append( ")" ).append(
")" ).toString( );
log.debug( "filter: {}", filter );
namingEnumeration = context.search( getGroupsDn( ), filter, searchControls );
while ( namingEnumeration.hasMore( ) )
{
SearchResult groupSearchResult = namingEnumeration.next( );
LdapGroup groupName = getGroupFromResult( groupSearchResult );
userGroups.add( groupName );
}
}
catch ( LdapException e )
{
throw new MappingException( e.getMessage( ), e );
}
catch ( NamingException e )
{
throw new MappingException( e.getMessage( ), e );
}
finally
{
close( namingEnumeration );
}
return new ArrayList( userGroups );
}
public List<String> getRoles( String username, DirContext context, Collection<String> realRoles )
throws MappingException
{

View File

@ -31,6 +31,7 @@ import org.springframework.stereotype.Service;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@ -109,4 +110,18 @@ public class DefaultLdapRoleMapperConfiguration
Map<String, Collection<String>> mappings = map.asMap();
return mappings;
}
@Override
public Collection<String> getLdapGroupMapping( String groupName ) throws MappingException
{
if (this.ldapMappings.containsKey( groupName )) {
return this.ldapMappings.get( groupName );
} else {
String value = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY + groupName );
if ( value != null) {
return Arrays.asList( StringUtils.split( "," ) );
}
}
throw new MappingException( "Mapping for group " + groupName + " not found" );
}
}

View File

@ -0,0 +1,124 @@
package org.apache.archiva.redback.common.ldap.role;
/*
* 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 java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Representation of a LDAP group
* @author Martin Stockhammer <martin_s@apache.org>
* @since 3.0
*/
public class LdapGroup
{
String dn = "";
String name;
String description;
List<String> memberList;
public LdapGroup( )
{
}
public LdapGroup( String dn )
{
this.dn = dn;
}
public LdapGroup( String dn, String name, String displayName, String description )
{
this.dn = dn;
this.name = name;
this.description = description;
}
public String getDn( )
{
return dn;
}
public void setDn( String dn )
{
this.dn = dn;
}
public String getName( )
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public String getDescription( )
{
return description;
}
public void setDescription( String description )
{
this.description = description;
}
public void addMember(String member) {
if (this.memberList==null) {
this.memberList = new ArrayList<>( );
}
this.memberList.add( member );
}
public void setMemberList( Collection<String> memberList) {
this.memberList = new ArrayList<>( memberList );
}
public List<String> getMemberList() {
return memberList;
}
@Override
public boolean equals( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass( ) != o.getClass( ) ) return false;
LdapGroup ldapGroup = (LdapGroup) o;
return dn.equals( ldapGroup.dn );
}
@Override
public int hashCode( )
{
return dn.hashCode( );
}
@Override
public String toString( )
{
final StringBuilder sb = new StringBuilder( "LdapGroup{" );
sb.append( "dn='" ).append( dn ).append( '\'' );
sb.append( ", name='" ).append( name ).append( '\'' );
sb.append( '}' );
return sb.toString( );
}
}

View File

@ -42,6 +42,14 @@ public interface LdapRoleMapper
List<String> getAllGroups( DirContext context )
throws MappingException;
/**
* read all groups from ldap
*
* @return all LDAP groups
*/
List<LdapGroup> getAllGroupObjects( DirContext context )
throws MappingException;
/**
* read all ldap groups then map to corresponding role (if no mapping found group is ignored)
*
@ -76,6 +84,9 @@ public interface LdapRoleMapper
List<String> getGroups( String username, DirContext context )
throws MappingException;
List<LdapGroup> getGroupObjects( String username, DirContext context )
throws MappingException;
List<String> getRoles( String username, DirContext context, Collection<String> realRoles )
throws MappingException;

View File

@ -62,6 +62,14 @@ public interface LdapRoleMapperConfiguration
Map<String, Collection<String>> getLdapGroupMappings()
throws MappingException;
/**
* Returns the mapping for the given group
* @param groupName the group name
* @return the list of roles
* @throws MappingException
*/
Collection<String> getLdapGroupMapping(String groupName) throws MappingException;
void setLdapGroupMappings( Map<String, List<String>> mappings )
throws MappingException;
}

View File

@ -31,6 +31,7 @@ import java.util.List;
public class LdapUser
implements User, Serializable
{
private String dn;
private String username;
@ -249,4 +250,13 @@ public class LdapUser
return userManagerId;
}
public String getDn( )
{
return dn;
}
public void setDn( String dn )
{
this.dn = dn;
}
}

View File

@ -180,7 +180,7 @@ public class LdapUserMapper
return null;
}
public LdapUser getUser( Attributes attributes )
public LdapUser getUser( String dn, Attributes attributes )
throws MappingException
{
String userIdAttribute = getUserIdAttribute();
@ -191,6 +191,7 @@ public class LdapUserMapper
String userId = LdapUtils.getAttributeValue( attributes, userIdAttribute, "username" );
LdapUser user = new LdapUser( userId );
user.setDn( dn );
user.setOriginalAttributes( attributes );
user.setEmail( LdapUtils.getAttributeValue( attributes, emailAddressAttribute, "email address" ) );

View File

@ -29,7 +29,7 @@ import javax.naming.directory.Attributes;
*/
public interface UserMapper
{
LdapUser getUser( Attributes attributes )
LdapUser getUser( String dn, Attributes attributes )
throws MappingException;
Attributes getCreationAttributes( User user, boolean encodePasswordIfChanged )

View File

@ -100,6 +100,8 @@ public interface UserConfigurationKeys
String LDAP_GROUP_NAME_ATTRIBUTE = "ldap.config.groups.name.attribute";
String LDAP_GROUP_DESCRIPTION_ATTRIBUTE = "ldap.config.groups.description.attribute";
String APPLICATION_URL = "application.url";
String EMAIL_URL_PATH = "email.url.path";

View File

@ -0,0 +1,91 @@
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.ArrayList;
import java.util.List;
/**
* @author Martin Stockhammer <martin_s@apache.org>
*/
@XmlRootElement(name="group")
public class Group
{
String name;
String uniqueName;
String description;
List<String> memberList;
public Group() {
}
public Group( String name )
{
this.name = name;
}
public String getName( )
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public String getUniqueName( )
{
return uniqueName;
}
public void setUniqueName( String uniqueName )
{
this.uniqueName = uniqueName;
}
public String getDescription( )
{
return description;
}
public void setDescription( String description )
{
this.description = description;
}
public List<String> getMemberList( )
{
return memberList;
}
public void setMemberList( List<String> memberList )
{
this.memberList = memberList;
}
public void addMember(String member) {
if (this.memberList==null) {
this.memberList = new ArrayList<>( );
}
this.memberList.add( member );
}
}

View File

@ -0,0 +1,106 @@
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.io.Serializable;
import java.util.Collection;
/**
* @author Olivier Lamy
* @since 2.1
*/
@XmlRootElement(name = "groupMapping")
public class GroupMapping
implements Serializable
{
private String group;
private Collection<String> roleNames;
public GroupMapping()
{
// no op
}
public GroupMapping( String group, Collection<String> roleNames )
{
this.group = group;
this.roleNames = roleNames;
}
public String getGroup()
{
return group;
}
public void setGroup( String group )
{
this.group = group;
}
public Collection<String> getRoleNames()
{
return roleNames;
}
public void setRoleNames( Collection<String> roleNames )
{
this.roleNames = roleNames;
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
sb.append( "LdapGroupMapping" );
sb.append( "{group='" ).append( group ).append( '\'' );
sb.append( ", roleNames=" ).append( roleNames );
sb.append( '}' );
return sb.toString();
}
@Override
public boolean equals( Object o )
{
if ( this == o )
{
return true;
}
if ( o == null || getClass() != o.getClass() )
{
return false;
}
GroupMapping that = (GroupMapping) o;
if ( !group.equals( that.group ) )
{
return false;
}
return true;
}
@Override
public int hashCode()
{
return group.hashCode();
}
}

View File

@ -0,0 +1,64 @@
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.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Olivier Lamy
*/
@XmlRootElement( name = "groupMappingUpdateRequest" )
public class GroupMappingUpdateRequest
implements Serializable
{
private List<GroupMapping> groupMapping;
public GroupMappingUpdateRequest()
{
// no op
}
public List<GroupMapping> getGroupMapping()
{
if ( this.groupMapping == null )
{
this.groupMapping = new ArrayList<GroupMapping>();
}
return groupMapping;
}
public void setGroupMapping( List<GroupMapping> groupMapping )
{
this.groupMapping = groupMapping;
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder( "LdapGroupMappingUpdateRequest{" );
sb.append( "ldapGroupMapping=" ).append( groupMapping );
sb.append( '}' );
return sb.toString();
}
}

View File

@ -43,6 +43,7 @@ import java.util.List;
*/
@Path("/ldapGroupMappingService/")
@Tag( name = "LDAP", description = "LDAP Service" )
@Deprecated
public interface LdapGroupMappingService
{
@Path("ldapGroups")

View File

@ -0,0 +1,145 @@
package org.apache.archiva.redback.rest.api.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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.archiva.redback.authorization.RedbackAuthorization;
import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
import org.apache.archiva.redback.rest.api.model.ActionStatus;
import org.apache.archiva.redback.rest.api.model.Group;
import org.apache.archiva.redback.rest.api.model.GroupMapping;
import org.apache.archiva.redback.rest.api.model.GroupMappingUpdateRequest;
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.List;
/**
* @author Olivier Lamy
* @since 2.1
*/
@Path( "/groups" )
@Tag( name = "Groups", description = "Groups and Group to Role Mappings" )
public interface GroupService
{
@Path( "" )
@GET
@Produces( {MediaType.APPLICATION_JSON} )
@RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
@Operation( summary = "Get list of group objects",
responses = {
@ApiResponse( description = "List of group objects. The number of returned results depend on the pagination parameters offset and limit." )
}
)
List<Group> getGroups( @QueryParam( "offset" ) @DefaultValue( "0" ) Long offset,
@QueryParam( "limit" ) @DefaultValue( value = Long.MAX_VALUE+"" ) Long limit)
throws RedbackServiceException;
@Path( "mappings" )
@GET
@Produces( {MediaType.APPLICATION_JSON} )
@RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
@Operation( summary = "Get list of group mappings",
responses = {
@ApiResponse( description = "List of group mappings" )
}
)
List<GroupMapping> getGroupMappings( )
throws RedbackServiceException;
@Path( "mappings" )
@POST
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces( {MediaType.APPLICATION_JSON} )
@RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
@Operation( summary = "Adds a group mapping",
responses = {
@ApiResponse( description = "The status of the add action" ),
@ApiResponse( responseCode = "405", description = "Invalid input" )
}
)
ActionStatus addGroupMapping( @Parameter( description = "The data of the group mapping", required = true )
GroupMapping groupMapping )
throws RedbackServiceException;
@Path( "mappings/{group}" )
@DELETE
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces( {MediaType.APPLICATION_JSON} )
@RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
@Operation( summary = "Deletes a group mapping",
responses = {
@ApiResponse( description = "The status of the delete action" ),
@ApiResponse( responseCode = "404", description = "Group mapping not found" )
}
)
ActionStatus removeGroupMapping( @Parameter( description = "The group name", required = true )
@PathParam( "group" ) String group )
throws RedbackServiceException;
@Path( "mappings/{group}" )
@PUT
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces( {MediaType.APPLICATION_JSON} )
@RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
@Operation( summary = "Updates a group mapping",
responses = {
@ApiResponse( description = "The status of the update action" ),
@ApiResponse( responseCode = "404", description = "Group mapping not found" )
}
)
ActionStatus updateGroupMapping( @Parameter( description = "The group name", required = true )
@PathParam( "group" ) String groupName,
@Parameter( description = "The updated data of the group mapping", required = true )
GroupMapping groupMapping )
throws RedbackServiceException;
@Path( "mappings" )
@PUT
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces( {MediaType.APPLICATION_JSON} )
@RedbackAuthorization( permissions = RedbackRoleConstants.CONFIGURATION_EDIT_OPERATION )
@Operation( summary = "Updates a multiple group mappings",
responses = {
@ApiResponse( description = "The status of the update action" ),
@ApiResponse( responseCode = "405", description = "Invalid input" )
}
)
ActionStatus updateGroupMapping( @Parameter( description = "The list of group mapping updates", required = true )
GroupMappingUpdateRequest groupMappingUpdateRequest )
throws RedbackServiceException;
}

View File

@ -0,0 +1,98 @@
package org.apache.archiva.redback.rest.api.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.authorization.RedbackAuthorization;
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.User;
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
@Path( "/auth" )
public interface LoginService
{
@Path( "requestkey" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true )
AuthenticationKeyResult addAuthenticationKey( @QueryParam( "providerKey" ) String providedKey,
@QueryParam( "principal" ) String principal, @QueryParam( "purpose" ) String purpose,
@QueryParam( "expirationMinutes" ) int expirationMinutes )
throws RedbackServiceException;
@Path( "ping" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true )
PingResult ping()
throws RedbackServiceException;
@Path( "ping/authenticated" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = false, noPermission = true )
PingResult pingWithAutz()
throws RedbackServiceException;
/**
* check username/password and create a http session.
* So no more need of reuse username/password for all ajaxRequest
*/
@Path( "authenticate" )
@POST
@RedbackAuthorization( noRestriction = true, noPermission = true )
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
User logIn( LoginRequest loginRequest )
throws RedbackServiceException;
/**
* simply check if current user has an http session opened with authz passed and return user data
* @since 1.4
*/
@Path( "isAuthenticated" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( noRestriction = true )
User isLogged()
throws RedbackServiceException;
/**
* clear user http session
* @since 1.4
*/
@Path( "logout" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
ActionStatus logout()
throws RedbackServiceException;
}

View File

@ -0,0 +1,66 @@
package org.apache.archiva.redback.rest.api.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.authorization.RedbackAuthorization;
import org.apache.archiva.redback.rest.api.model.User;
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
/**
* @author Olivier Lamy
* @since 1.4
*/
@Path( "/passwordService/" )
public interface PasswordService
{
/**
* used to change the password on first user connection after registration use.
* the key is mandatory and a control will be done on the username provided.
* <b>need to be logged by {@link UserService#validateUserFromKey(String)}</b>
* @return username
*/
@GET
@Path( "changePasswordWithKey" )
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
User changePasswordWithKey( @QueryParam( "password" ) String password,
@QueryParam( "passwordConfirmation" ) String passwordConfirmation,
@QueryParam( "key" ) String key )
throws RedbackServiceException;
/**
* used to change the password on passwordChangeRequired state.
*/
@GET
@Path( "changePassword" )
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
User changePassword( @QueryParam( "userName" ) String userName,
@QueryParam( "previousPassword" ) String previousPassword,
@QueryParam( "password" ) String password,
@QueryParam( "passwordConfirmation" ) String passwordConfirmation )
throws RedbackServiceException;
}

View File

@ -0,0 +1,310 @@
package org.apache.archiva.redback.rest.api.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.authorization.RedbackAuthorization;
import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
import org.apache.archiva.redback.rest.api.model.ActionStatus;
import org.apache.archiva.redback.rest.api.model.Application;
import org.apache.archiva.redback.rest.api.model.ApplicationRoles;
import org.apache.archiva.redback.rest.api.model.AvailabilityStatus;
import org.apache.archiva.redback.rest.api.model.Role;
import org.apache.archiva.redback.rest.api.model.User;
import org.apache.archiva.redback.rest.api.model.VerificationStatus;
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.List;
/**
* @author Olivier Lamy
*/
@Path( "/roleManagementService/" )
public interface RoleManagementService
{
@Path( "createTemplatedRole" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus createTemplatedRole( @QueryParam( "templateId" ) String templateId,
@QueryParam( "resource" ) String resource )
throws RedbackServiceException;
/**
* removes a role corresponding to the role Id that was manufactured with the given resource
*
* it also removes any user assignments for that role
*
* @param templateId
* @param resource
*/
@Path( "removeTemplatedRole" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus removeTemplatedRole( @QueryParam( "templateId" ) String templateId,
@QueryParam( "resource" ) String resource )
throws RedbackServiceException;
/**
* allows for a role coming from a template to be renamed effectively swapping out the bits of it that
* were labeled with the oldResource with the newResource
*
* it also manages any user assignments for that role
*
* @param templateId
* @param oldResource
* @param newResource
*/
@Path( "updateRole" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus updateRole( @QueryParam( "templateId" ) String templateId, @QueryParam( "oldResource" ) String oldResource,
@QueryParam( "newResource" ) String newResource )
throws RedbackServiceException;
/**
* Assigns the role indicated by the roleId to the given principal
*
* @param roleId
* @param principal
*/
@Path( "assignRole" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus assignRole( @QueryParam( "roleId" ) String roleId, @QueryParam( "principal" ) String principal )
throws RedbackServiceException;
/**
* Assigns the role indicated by the roleName to the given principal
*
* @param roleName
* @param principal
* @throws RedbackServiceException
*/
@Path( "assignRoleByName" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus assignRoleByName( @QueryParam( "roleName" ) String roleName, @QueryParam( "principal" ) String principal )
throws RedbackServiceException;
/**
* Assigns the templated role indicated by the templateId
*
* fails if the templated role has not been created
*
* @param templateId
* @param resource
* @param principal
*/
@Path( "assignTemplatedRole" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus assignTemplatedRole( @QueryParam( "templateId" ) String templateId,
@QueryParam( "resource" ) String resource,
@QueryParam( "principal" ) String principal )
throws RedbackServiceException;
/**
* Unassigns the role indicated by the role id from the given principal
*
* @param roleId
* @param principal
* @throws RedbackServiceException
*/
@Path( "unassignRole" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus unassignRole( @QueryParam( "roleId" ) String roleId, @QueryParam( "principal" ) String principal )
throws RedbackServiceException;
/**
* Unassigns the role indicated by the role name from the given principal
*
* @param roleName
* @param principal
* @throws RedbackServiceException
*/
@Path( "unassignRoleByName" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus unassignRoleByName( @QueryParam( "roleName" ) String roleName, @QueryParam( "principal" ) String principal )
throws RedbackServiceException;
/**
* true of a role exists with the given roleId
*
* @param roleId
* @return
* @throws RedbackServiceException
*/
@Path( "roleExists" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
AvailabilityStatus roleExists( @QueryParam( "roleId" ) String roleId )
throws RedbackServiceException;
/**
* true of a role exists with the given roleId
*
* @param templateId
* @param resource
* @return
* @throws RedbackServiceException
*/
@Path( "templatedRoleExists" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
AvailabilityStatus templatedRoleExists( @QueryParam( "templateId" ) String templateId,
@QueryParam( "resource" ) String resource )
throws RedbackServiceException;
/**
* Check a role template is complete in the RBAC store.
*
* @param templateId the templated role
* @param resource the resource to verify
* @throws RedbackServiceException
*/
@Path( "verifyTemplatedRole" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
VerificationStatus verifyTemplatedRole( @QueryParam( "templateId" ) String templateId,
@QueryParam( "resource" ) String resource )
throws RedbackServiceException;
/**
* @since 1.4
*/
@Path( "getEffectivelyAssignedRoles/{username}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
List<Role> getEffectivelyAssignedRoles( @PathParam( "username" ) String username )
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "allRoles" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
List<Role> getAllRoles()
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "detailledAllRoles" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
List<Role> getDetailedAllRoles()
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "getApplications/{username}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
List<Application> getApplications( @PathParam( "username" ) String username )
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "getRole/{roleName}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
Role getRole( @PathParam( "roleName" ) String roleName )
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "updateRoleDescription" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus updateRoleDescription( @QueryParam( "roleName" ) String roleName,
@QueryParam( "roleDescription" ) String description )
throws RedbackServiceException;
/**
* update users assigned to a role
* @since 2.0
*/
@Path( "updateRoleUsers" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus updateRoleUsers( Role role )
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "getApplicationRoles/{username}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
List<ApplicationRoles> getApplicationRoles( @PathParam( "username" ) String username )
throws RedbackServiceException;
/**
* update roles assigned to a user
* @since 2.0
*/
@Path( "updateUserRoles" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_RBAC_ADMIN_OPERATION )
ActionStatus updateUserRoles( User user )
throws RedbackServiceException;
}

View File

@ -0,0 +1,258 @@
package org.apache.archiva.redback.rest.api.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.authorization.RedbackAuthorization;
import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
import org.apache.archiva.redback.rest.api.model.ActionStatus;
import org.apache.archiva.redback.rest.api.model.AvailabilityStatus;
import org.apache.archiva.redback.rest.api.model.Operation;
import org.apache.archiva.redback.rest.api.model.PasswordStatus;
import org.apache.archiva.redback.rest.api.model.Permission;
import org.apache.archiva.redback.rest.api.model.PingResult;
import org.apache.archiva.redback.rest.api.model.RegistrationKey;
import org.apache.archiva.redback.rest.api.model.ResetPasswordRequest;
import org.apache.archiva.redback.rest.api.model.User;
import org.apache.archiva.redback.rest.api.model.UserRegistrationRequest;
import org.apache.archiva.redback.rest.api.model.VerificationStatus;
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Collection;
import java.util.List;
@Path( "/userService/" )
public interface UserService
{
@Path( "getUser/{userName}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
User getUser( @PathParam( "userName" ) String username )
throws RedbackServiceException;
@Path( "getUsers" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
List<User> getUsers()
throws RedbackServiceException;
@Path( "createUser" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_CREATE_OPERATION )
ActionStatus createUser( User user )
throws RedbackServiceException;
/**
* will create admin user only if not exists !! if exists will return false
*/
@Path( "createAdminUser" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( noRestriction = true )
ActionStatus createAdminUser( User user )
throws RedbackServiceException;
@Path( "isAdminUserExists" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true )
AvailabilityStatus isAdminUserExists()
throws RedbackServiceException;
@Path( "deleteUser/{userName}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_DELETE_OPERATION )
ActionStatus deleteUser( @PathParam( "userName" ) String username )
throws RedbackServiceException;
@Path( "updateUser" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
ActionStatus updateUser( User user )
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "lockUser/{username}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
ActionStatus lockUser( @PathParam( "username" ) String username )
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "unlockUser/{username}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
ActionStatus unlockUser( @PathParam( "username" ) String username )
throws RedbackServiceException;
/**
* @since 2.0
*/
@Path( "passwordChangeRequired/{username}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
PasswordStatus passwordChangeRequired( @PathParam( "username" ) String username )
throws RedbackServiceException;
/**
* update only the current user and this fields: fullname, email, password.
* the service verify the curent logged user with the one passed in the method
* @since 1.4
*/
@Path( "updateMe" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = false, noPermission = true )
ActionStatus updateMe( User user )
throws RedbackServiceException;
@Path( "ping" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true )
PingResult ping()
throws RedbackServiceException;
@Path( "removeFromCache/{userName}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
ActionStatus removeFromCache( @PathParam( "userName" ) String username )
throws RedbackServiceException;
@Path( "getGuestUser" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
User getGuestUser()
throws RedbackServiceException;
@Path( "createGuestUser" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_EDIT_OPERATION )
User createGuestUser()
throws RedbackServiceException;
/**
* if redback is not configured for email validation is required, -1 is returned as key
* @since 1.4
*/
@Path( "registerUser" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
RegistrationKey registerUser( UserRegistrationRequest userRegistrationRequest )
throws RedbackServiceException;
/**
* validate the key and the user with forcing a password change for next login.
* http session is created.
* @param key authentication key
* @since 1.4
*/
@Path( "validateKey/{key}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
VerificationStatus validateUserFromKey( @PathParam( "key" ) String key )
throws RedbackServiceException;
/**
*
* @param resetPasswordRequest contains username for send a password reset email
* @since 1.4
*/
@Path( "resetPassword" )
@POST
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
ActionStatus resetPassword( ResetPasswordRequest resetPasswordRequest )
throws RedbackServiceException;
/**
* @since 1.4
*/
@Path( "getUserPermissions/{userName}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
Collection<Permission> getUserPermissions( @PathParam( "userName" ) String userName )
throws RedbackServiceException;
/**
* @since 1.4
*/
@Path( "getUserOperations/{userName}" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( permissions = RedbackRoleConstants.USER_MANAGEMENT_USER_LIST_OPERATION )
Collection<Operation> getUserOperations( @PathParam( "userName" ) String userName )
throws RedbackServiceException;
/**
* @return the current logged user permissions, if no logged user guest permissions are returned
* @since 1.4
*/
@Path( "getCurrentUserPermissions" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
Collection<Permission> getCurrentUserPermissions()
throws RedbackServiceException;
/**
* @return the current logged user operations, if no logged user guest operations are returned
* @since 1.4
*/
@Path( "getCurrentUserOperations" )
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true, noPermission = true )
Collection<Operation> getCurrentUserOperations()
throws RedbackServiceException;
}

View File

@ -0,0 +1,57 @@
package org.apache.archiva.redback.rest.api.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.authorization.RedbackAuthorization;
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.Properties;
/**
* @author Olivier Lamy
* @since 1.4
*/
@Path( "/utilServices/" )
public interface UtilServices
{
@Path( "getBundleResources" )
@GET
@Produces( { MediaType.TEXT_PLAIN } )
@RedbackAuthorization( noRestriction = true )
String getI18nResources( @QueryParam( "locale" ) String locale )
throws RedbackServiceException;
/**
* <b>not intended to be exposed as a REST service.</b>
* will load i18N resource org/apache/archiva/redback/users/messages in default en then in the asked locale.
* @param locale
* @return
* @throws RedbackServiceException
*/
Properties getI18nProperties( String locale )
throws RedbackServiceException;
}

View File

@ -25,6 +25,7 @@ import org.apache.archiva.redback.common.ldap.connection.LdapException;
import org.apache.archiva.redback.common.ldap.role.LdapRoleMapper;
import org.apache.archiva.redback.common.ldap.role.LdapRoleMapperConfiguration;
import org.apache.archiva.redback.rest.api.model.ActionStatus;
import org.apache.archiva.redback.rest.api.model.Group;
import org.apache.archiva.redback.rest.api.model.LdapGroupMapping;
import org.apache.archiva.redback.rest.api.model.LdapGroupMappingUpdateRequest;
import org.apache.archiva.redback.rest.api.model.StringList;
@ -42,6 +43,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Olivier Lamy

View File

@ -0,0 +1,235 @@
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.common.ldap.MappingException;
import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
import org.apache.archiva.redback.common.ldap.connection.LdapException;
import org.apache.archiva.redback.common.ldap.role.LdapGroup;
import org.apache.archiva.redback.common.ldap.role.LdapRoleMapper;
import org.apache.archiva.redback.common.ldap.role.LdapRoleMapperConfiguration;
import org.apache.archiva.redback.rest.api.model.ActionStatus;
import org.apache.archiva.redback.rest.api.model.Group;
import org.apache.archiva.redback.rest.api.model.GroupMapping;
import org.apache.archiva.redback.rest.api.model.GroupMappingUpdateRequest;
import org.apache.archiva.redback.rest.api.model.StringList;
import org.apache.archiva.redback.rest.api.services.RedbackServiceException;
import org.apache.archiva.redback.rest.api.services.v2.GroupService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
import javax.inject.Named;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
* LDAP implementation of the group service
*
* @author Olivier Lamy
* @author Martin Stockhammer
* @since 3.0
*/
@Service("v2.groupService#rest")
public class DefaultGroupService
implements GroupService
{
private final Logger log = LoggerFactory.getLogger( getClass() );
@Inject
@Named(value = "ldapRoleMapper#default")
private LdapRoleMapper ldapRoleMapper;
@Inject
@Named(value = "ldapRoleMapperConfiguration#default")
private LdapRoleMapperConfiguration ldapRoleMapperConfiguration;
@Inject
@Named(value = "ldapConnectionFactory#configurable")
private LdapConnectionFactory ldapConnectionFactory;
private static final Group getGroupFromLdap( LdapGroup ldapGroup ) {
Group group = new Group( );
group.setName( ldapGroup.getName() );
group.setUniqueName( ldapGroup.getDn() );
group.setDescription( ldapGroup.getDescription() );
group.setMemberList( ldapGroup.getMemberList() );
return group;
}
@Override
public List<Group> getGroups( Long offset, Long limit ) throws RedbackServiceException
{
LdapConnection ldapConnection = null;
DirContext context = null;
try
{
ldapConnection = ldapConnectionFactory.getConnection();
context = ldapConnection.getDirContext();
return ldapRoleMapper.getAllGroupObjects( context ).stream( ).skip( offset ).limit( limit ).map( DefaultGroupService::getGroupFromLdap ).collect( Collectors.toList( ) );
}
catch ( LdapException | MappingException e )
{
log.error( e.getMessage(), e );
throw new RedbackServiceException( e.getMessage() );
}
finally
{
closeContext( context );
closeLdapConnection( ldapConnection );
}
}
@Override
public List<GroupMapping> getGroupMappings()
throws RedbackServiceException
{
try
{
Map<String, Collection<String>> map = ldapRoleMapperConfiguration.getLdapGroupMappings();
List<GroupMapping> ldapGroupMappings = new ArrayList<>( map.size( ) );
for ( Map.Entry<String, Collection<String>> entry : map.entrySet() )
{
GroupMapping ldapGroupMapping = new GroupMapping( entry.getKey(), entry.getValue() );
ldapGroupMappings.add( ldapGroupMapping );
}
return ldapGroupMappings;
}
catch ( MappingException e )
{
log.error( e.getMessage(), e );
throw new RedbackServiceException( e.getMessage() );
}
}
@Override
public ActionStatus addGroupMapping( GroupMapping ldapGroupMapping )
throws RedbackServiceException
{
try
{
ldapRoleMapperConfiguration.addLdapMapping( ldapGroupMapping.getGroup(),
new ArrayList<>( ldapGroupMapping.getRoleNames() ) );
}
catch ( MappingException e )
{
log.error( e.getMessage(), e );
throw new RedbackServiceException( e.getMessage() );
}
return ActionStatus.SUCCESS;
}
@Override
public ActionStatus removeGroupMapping( String group )
throws RedbackServiceException
{
try
{
ldapRoleMapperConfiguration.removeLdapMapping( group );
}
catch ( MappingException e )
{
log.error( e.getMessage(), e );
throw new RedbackServiceException( e.getMessage() );
}
return ActionStatus.SUCCESS;
}
@Override
public ActionStatus updateGroupMapping( String groupName, GroupMapping groupMapping ) throws RedbackServiceException
{
try
{
ldapRoleMapperConfiguration.getLdapGroupMapping( groupName );
}
catch ( MappingException e )
{
throw new RedbackServiceException( "Group mapping not found ", 404 );
}
try
{
ldapRoleMapperConfiguration.updateLdapMapping( groupName,
new ArrayList<>( groupMapping.getRoleNames() ) );
return ActionStatus.SUCCESS;
}
catch ( MappingException e )
{
log.error( "Could not update mapping {}", e.getMessage( ) );
throw new RedbackServiceException( e.getMessage( ) );
}
}
@Override
public ActionStatus updateGroupMapping( GroupMappingUpdateRequest groupMappingUpdateRequest )
throws RedbackServiceException
{
try
{
for ( GroupMapping ldapGroupMapping : groupMappingUpdateRequest.getGroupMapping() )
{
ldapRoleMapperConfiguration.updateLdapMapping( ldapGroupMapping.getGroup(),
new ArrayList<>( ldapGroupMapping.getRoleNames() ) );
}
}
catch ( MappingException e )
{
log.error( e.getMessage(), e );
throw new RedbackServiceException( e.getMessage() );
}
return ActionStatus.SUCCESS;
}
//------------------
// utils
//------------------
protected void closeLdapConnection( LdapConnection ldapConnection )
{
if ( ldapConnection != null )
{
ldapConnection.close();
}
}
protected void closeContext( DirContext context )
{
if ( context != null )
{
try
{
context.close();
}
catch ( NamingException e )
{
log.warn( "skip issue closing context: {}", e.getMessage() );
}
}
}
}

View File

@ -74,4 +74,26 @@
</jaxrs:providers>
</jaxrs:server>
<jaxrs:server address="/redback/v2">
<jaxrs:serviceBeans>
<ref bean="userService#rest"/>
<ref bean="loginService#rest"/>
<ref bean="roleManagementService#rest"/>
<ref bean="utilServices#rest"/>
<ref bean="passwordService#rest"/>
<ref bean="v2.groupService#rest"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="jsonProvider"/>
<ref bean="xmlProvider"/>
<ref bean="authenticationInterceptor#rest"/>
<ref bean="permissionInterceptor#rest"/>
<ref bean="redbackServiceExceptionMapper"/>
<ref bean="passwordRuleViolationExceptionMapper"/>
<ref bean="requestValidationInterceptor#rest" />
<ref bean="threadLocalUserCleaner#rest"/>
</jaxrs:providers>
</jaxrs:server>
</beans>

View File

@ -0,0 +1,273 @@
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 com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.apache.archiva.components.apacheds.ApacheDs;
import org.apache.archiva.redback.rest.api.model.GroupMapping;
import org.apache.archiva.redback.rest.api.services.v2.GroupService;
import org.apache.archiva.redback.rest.services.AbstractRestServicesTest;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
import org.apache.cxf.jaxrs.client.WebClient;
import org.assertj.core.api.Condition;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.inject.Inject;
import javax.inject.Named;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.ws.rs.core.MediaType;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Olivier Lamy
*/
@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration(
locations = { "classpath:/ldap-spring-test.xml" } )
@DirtiesContext( classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD )
public class GroupServiceTest
extends AbstractRestServicesTest
{
@Inject
@Named( value = "apacheDS#test" )
private ApacheDs apacheDs;
List<String> groups =
Arrays.asList( "Archiva System Administrator", "Internal Repo Manager", "Internal Repo Observer" );
private String suffix;
private String groupSuffix;
protected GroupService getGroupService( String authzHeader )
{
GroupService service =
JAXRSClientFactory.create( "http://localhost:" + getServerPort() + "/" + getRestServicesPath() + "/redback/v2/",
GroupService.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;
}
@Override
protected String getSpringConfigLocation()
{
return "classpath*:spring-context.xml,classpath*:META-INF/spring-context.xml,classpath:/ldap-spring-test.xml";
}
@Override
public void startServer()
throws Exception
{
super.startServer();
groupSuffix = apacheDs.addSimplePartition( "test", new String[]{ "archiva", "apache", "org" } ).getSuffix();
log.info( "groupSuffix: {}", groupSuffix );
suffix = "ou=People,dc=archiva,dc=apache,dc=org";
log.info( "DN Suffix: {}", suffix );
apacheDs.startServer();
BasicAttribute objectClass = new BasicAttribute( "objectClass" );
objectClass.add( "top" );
objectClass.add( "organizationalUnit" );
Attributes attributes = new BasicAttributes( true );
attributes.put( objectClass );
attributes.put( "organizationalUnitName", "foo" );
apacheDs.getAdminContext().createSubcontext( suffix, attributes );
createGroups();
}
@Override
public void stopServer()
throws Exception
{
// cleanup ldap entries
InitialDirContext context = apacheDs.getAdminContext();
for ( String group : this.groups )
{
context.unbind( createGroupDn( group ) );
}
context.unbind( suffix );
context.close();
apacheDs.stopServer();
super.stopServer();
}
private void createGroups()
throws Exception
{
InitialDirContext context = apacheDs.getAdminContext();
for ( String group : groups )
{
createGroup( context, group, createGroupDn( group ) );
}
}
private void createGroup( DirContext context, String groupName, String dn )
throws Exception
{
Attributes attributes = new BasicAttributes( true );
BasicAttribute objectClass = new BasicAttribute( "objectClass" );
objectClass.add( "top" );
objectClass.add( "groupOfUniqueNames" );
attributes.put( objectClass );
attributes.put( "cn", groupName );
BasicAttribute basicAttribute = new BasicAttribute( "uniquemember" );
basicAttribute.add( "uid=admin," + suffix );
attributes.put( basicAttribute );
context.createSubcontext( dn, attributes );
}
private String createGroupDn( String cn )
{
return "cn=" + cn + "," + groupSuffix;
}
@Test
public void getAllGroups()
throws Exception
{
try
{
GroupService service = getGroupService( authorizationHeader );
List<String> allGroups = service.getGroups( Long.valueOf( 0 ), Long.valueOf( Long.MAX_VALUE ) ).stream( ).map( group -> group.getName( ) ).collect( Collectors.toList( ) );
assertThat( allGroups ).isNotNull().isNotEmpty().hasSize( 3 ).containsAll( groups );
}
catch ( Exception e )
{
log.error( e.getMessage(), e );
throw e;
}
}
@Test
public void getGroupMappings()
throws Exception
{
try
{
GroupService service = getGroupService( authorizationHeader );
List<GroupMapping> mappings = service.getGroupMappings();
assertThat( mappings ).isNotNull().isNotEmpty().hasSize( 3 );
}
catch ( Exception e )
{
log.error( e.getMessage(), e );
throw e;
}
}
@Test
public void addThenRemove()
throws Exception
{
try
{
GroupService service = getGroupService( authorizationHeader );
List<GroupMapping> mappings = service.getGroupMappings();
assertThat( mappings ).isNotNull().isNotEmpty().hasSize( 3 );
GroupMapping groupMapping = new GroupMapping( "ldap group", Arrays.asList( "redback role" ) );
service.addGroupMapping( groupMapping );
mappings = service.getGroupMappings();
assertThat( mappings ).isNotNull().isNotEmpty().hasSize( 4 ).are(
new Condition<GroupMapping>()
{
@Override
public boolean matches( GroupMapping mapping )
{
if ( StringUtils.equals( "ldap group", mapping.getGroup() ) )
{
assertThat( mapping.getRoleNames() ).isNotNull().isNotEmpty().containsOnly(
"redback role" );
return true;
}
return true;
}
} );
service.removeGroupMapping( "ldap group" );
mappings = service.getGroupMappings();
assertThat( mappings ).isNotNull().isNotEmpty().hasSize( 3 );
}
catch ( Exception e )
{
log.error( e.getMessage(), e );
throw e;
}
}
}

View File

@ -204,7 +204,7 @@ public class DefaultLdapController
{
SearchResult result = results.nextElement();
users.add( mapper.getUser( result.getAttributes() ) );
users.add( mapper.getUser( result.getNameInNamespace(), result.getAttributes() ) );
}
return users;
@ -247,7 +247,7 @@ public class DefaultLdapController
{
SearchResult result = results.nextElement();
users.add( mapper.getUser( result.getAttributes() ) );
users.add( mapper.getUser( result.getNameInNamespace(), result.getAttributes() ) );
}
return users;
@ -360,7 +360,7 @@ public class DefaultLdapController
log.info( "Found user: {}", username );
return mapper.getUser( next.getAttributes() );
return mapper.getUser( next.getNameInNamespace(), next.getAttributes() );
}
else
{