diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapper.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapper.java index 9b44fe5f..e66306d4 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapper.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapper.java @@ -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 getAllGroupObjects( DirContext context ) throws MappingException + { + + NamingEnumeration 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 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 getGroupObjects( String username, DirContext context ) throws MappingException + { + Set userGroups = new HashSet<>( ); + + NamingEnumeration 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 getRoles( String username, DirContext context, Collection realRoles ) throws MappingException { diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapperConfiguration.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapperConfiguration.java index d49561ef..0515c503 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapperConfiguration.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/DefaultLdapRoleMapperConfiguration.java @@ -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> mappings = map.asMap(); return mappings; } + + @Override + public Collection 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" ); + } } diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapGroup.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapGroup.java new file mode 100644 index 00000000..86e48e23 --- /dev/null +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapGroup.java @@ -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 + * @since 3.0 + */ +public class LdapGroup +{ + String dn = ""; + String name; + String description; + List 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 memberList) { + this.memberList = new ArrayList<>( memberList ); + } + + public List 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( ); + } +} diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java index 75fa37e8..638f8ceb 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapper.java @@ -42,6 +42,14 @@ public interface LdapRoleMapper List getAllGroups( DirContext context ) throws MappingException; + /** + * read all groups from ldap + * + * @return all LDAP groups + */ + List 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 getGroups( String username, DirContext context ) throws MappingException; + List getGroupObjects( String username, DirContext context ) + throws MappingException; + List getRoles( String username, DirContext context, Collection realRoles ) throws MappingException; diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapperConfiguration.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapperConfiguration.java index 346db919..edcfb5db 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapperConfiguration.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/role/LdapRoleMapperConfiguration.java @@ -62,6 +62,14 @@ public interface LdapRoleMapperConfiguration Map> getLdapGroupMappings() throws MappingException; + /** + * Returns the mapping for the given group + * @param groupName the group name + * @return the list of roles + * @throws MappingException + */ + Collection getLdapGroupMapping(String groupName) throws MappingException; + void setLdapGroupMappings( Map> mappings ) throws MappingException; } diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUser.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUser.java index 941cf1fa..1965ca35 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUser.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUser.java @@ -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; + } } diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUserMapper.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUserMapper.java index 5542c36a..a2ee1490 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUserMapper.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/LdapUserMapper.java @@ -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" ) ); diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/UserMapper.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/UserMapper.java index 8108c1b1..a800970a 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/UserMapper.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/user/UserMapper.java @@ -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 ) diff --git a/redback-configuration/src/main/java/org/apache/archiva/redback/configuration/UserConfigurationKeys.java b/redback-configuration/src/main/java/org/apache/archiva/redback/configuration/UserConfigurationKeys.java index a231712c..0e01af2d 100644 --- a/redback-configuration/src/main/java/org/apache/archiva/redback/configuration/UserConfigurationKeys.java +++ b/redback-configuration/src/main/java/org/apache/archiva/redback/configuration/UserConfigurationKeys.java @@ -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"; diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/Group.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/Group.java new file mode 100644 index 00000000..9072207f --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/Group.java @@ -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 + */ +@XmlRootElement(name="group") +public class Group +{ + String name; + String uniqueName; + String description; + List 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 getMemberList( ) + { + return memberList; + } + + public void setMemberList( List memberList ) + { + this.memberList = memberList; + } + + public void addMember(String member) { + if (this.memberList==null) { + this.memberList = new ArrayList<>( ); + } + this.memberList.add( member ); + } +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/GroupMapping.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/GroupMapping.java new file mode 100644 index 00000000..4dd1fdc6 --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/GroupMapping.java @@ -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 roleNames; + + public GroupMapping() + { + // no op + } + + public GroupMapping( String group, Collection roleNames ) + { + this.group = group; + this.roleNames = roleNames; + } + + public String getGroup() + { + return group; + } + + public void setGroup( String group ) + { + this.group = group; + } + + public Collection getRoleNames() + { + return roleNames; + } + + public void setRoleNames( Collection 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(); + } +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/GroupMappingUpdateRequest.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/GroupMappingUpdateRequest.java new file mode 100644 index 00000000..33746a83 --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/model/GroupMappingUpdateRequest.java @@ -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; + + public GroupMappingUpdateRequest() + { + // no op + } + + public List getGroupMapping() + { + if ( this.groupMapping == null ) + { + this.groupMapping = new ArrayList(); + } + return groupMapping; + } + + public void setGroupMapping( List groupMapping ) + { + this.groupMapping = groupMapping; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder( "LdapGroupMappingUpdateRequest{" ); + sb.append( "ldapGroupMapping=" ).append( groupMapping ); + sb.append( '}' ); + return sb.toString(); + } +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/LdapGroupMappingService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/LdapGroupMappingService.java index fb326160..a0ea6b53 100644 --- a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/LdapGroupMappingService.java +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/LdapGroupMappingService.java @@ -43,6 +43,7 @@ import java.util.List; */ @Path("/ldapGroupMappingService/") @Tag( name = "LDAP", description = "LDAP Service" ) +@Deprecated public interface LdapGroupMappingService { @Path("ldapGroups") diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/GroupService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/GroupService.java new file mode 100644 index 00000000..77707912 --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/GroupService.java @@ -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 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 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; + +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/LoginService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/LoginService.java new file mode 100644 index 00000000..ad77713f --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/LoginService.java @@ -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; +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/PasswordService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/PasswordService.java new file mode 100644 index 00000000..57e51c99 --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/PasswordService.java @@ -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. + * need to be logged by {@link UserService#validateUserFromKey(String)} + * @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; +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/RoleManagementService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/RoleManagementService.java new file mode 100644 index 00000000..5aafa080 --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/RoleManagementService.java @@ -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 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 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 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 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 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; + +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UserService.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UserService.java new file mode 100644 index 00000000..34a0ed7c --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UserService.java @@ -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 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 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 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 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 getCurrentUserOperations() + throws RedbackServiceException; + +} diff --git a/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UtilServices.java b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UtilServices.java new file mode 100644 index 00000000..b8ffe38c --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-api/src/main/java/org/apache/archiva/redback/rest/api/services/v2/UtilServices.java @@ -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; + + /** + * not intended to be exposed as a REST service. + * 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; + + +} diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java index e4a8a0c7..ff554b3b 100644 --- a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java +++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/DefaultLdapGroupMappingService.java @@ -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 diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultGroupService.java b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultGroupService.java new file mode 100644 index 00000000..f6428df8 --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-services/src/main/java/org/apache/archiva/redback/rest/services/v2/DefaultGroupService.java @@ -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 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 getGroupMappings() + throws RedbackServiceException + { + try + { + Map> map = ldapRoleMapperConfiguration.getLdapGroupMappings(); + List ldapGroupMappings = new ArrayList<>( map.size( ) ); + for ( Map.Entry> 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() ); + } + } + } +} diff --git a/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml b/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml index 8e0441c1..8850ee3b 100644 --- a/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml +++ b/redback-integrations/redback-rest/redback-rest-services/src/main/resources/META-INF/spring-context.xml @@ -74,4 +74,26 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/GroupServiceTest.java b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/GroupServiceTest.java new file mode 100644 index 00000000..88c25de8 --- /dev/null +++ b/redback-integrations/redback-rest/redback-rest-services/src/test/java/org/apache/archiva/redback/rest/services/v2/GroupServiceTest.java @@ -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 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 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 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 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() + { + @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; + } + } +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/ctl/DefaultLdapController.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/ctl/DefaultLdapController.java index 90b0a58b..efa4cee4 100644 --- a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/ctl/DefaultLdapController.java +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/ctl/DefaultLdapController.java @@ -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 {