diff options
author | Martin Stockhammer <martin_s@apache.org> | 2021-01-02 11:26:29 +0100 |
---|---|---|
committer | Martin Stockhammer <martin_s@apache.org> | 2021-01-02 11:26:29 +0100 |
commit | d5d9c6d6d11831feb1434d1c9272c86703779879 (patch) | |
tree | 0e733eeadbb00106fa4d2f2c137c607eafef4d82 /archiva-modules | |
parent | befb43799ebf958241e69b75b00835994f40fa7d (diff) | |
download | archiva-d5d9c6d6d11831feb1434d1c9272c86703779879.tar.gz archiva-d5d9c6d6d11831feb1434d1c9272c86703779879.zip |
Adding V2 REST services
Diffstat (limited to 'archiva-modules')
19 files changed, 2141 insertions, 2 deletions
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/pom.xml b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/pom.xml index 8071c9123..67efa7acd 100644 --- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/pom.xml +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/pom.xml @@ -31,6 +31,8 @@ <properties> <enunciate.docsDir>${project.build.outputDirectory}/rest-docs-archiva-rest-api</enunciate.docsDir> <site.staging.base>${project.parent.parent.parent.basedir}</site.staging.base> + <openapi.config.file>${project.basedir}/src/main/resources/archiva/openapi-configuration.yaml</openapi.config.file> + <openapi.prefix>archiva</openapi.prefix> </properties> <dependencies> @@ -74,6 +76,11 @@ </exclusions> </dependency> <dependency> + <groupId>org.apache.archiva.components</groupId> + <artifactId>archiva-components-rest-util</artifactId> + </dependency> + + <dependency> <groupId>jakarta.ws.rs</groupId> <artifactId>jakarta.ws.rs-api</artifactId> </dependency> @@ -82,6 +89,23 @@ <artifactId>jakarta.annotation-api</artifactId> </dependency> + + <dependency> + <groupId>io.swagger.core.v3</groupId> + <artifactId>swagger-core</artifactId> + </dependency> + <dependency> + <groupId>io.swagger.core.v3</groupId> + <artifactId>swagger-jaxrs2</artifactId> + </dependency> + <dependency> + <groupId>io.swagger.core.v3</groupId> + <artifactId>swagger-annotations</artifactId> + </dependency> + <dependency> + <groupId>jakarta.xml.bind</groupId> + <artifactId>jakarta.xml.bind-api</artifactId> + </dependency> </dependencies> <build> diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/BeanInformation.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/BeanInformation.java new file mode 100644 index 000000000..2185a6f28 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/BeanInformation.java @@ -0,0 +1,91 @@ +package org.apache.archiva.rest.api.model.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.media.Schema; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +@XmlRootElement(name="beanInformation") +public class BeanInformation implements Serializable +{ + private static final long serialVersionUID = -432385743277355987L; + String id; + String displayName; + String descriptionKey; + String defaultDescription; + boolean readonly; + + @Schema(description = "The identifier") + public String getId( ) + { + return id; + } + + public void setId( String id ) + { + this.id = id; + } + + @Schema(description = "The display name") + public String getDisplayName( ) + { + return displayName; + } + + public void setDisplayName( String displayName ) + { + this.displayName = displayName; + } + + @Schema(description = "The translation key for the description") + public String getDescriptionKey( ) + { + return descriptionKey; + } + + public void setDescriptionKey( String descriptionKey ) + { + this.descriptionKey = descriptionKey; + } + + @Schema(description = "The description translated in the default language") + public String getDefaultDescription( ) + { + return defaultDescription; + } + + public void setDefaultDescription( String defaultDescription ) + { + this.defaultDescription = defaultDescription; + } + + @Schema(description = "True, if this bean cannot be removed") + public boolean isReadonly( ) + { + return readonly; + } + + public void setReadonly( boolean readonly ) + { + this.readonly = readonly; + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/CacheConfiguration.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/CacheConfiguration.java new file mode 100644 index 000000000..5f062f089 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/CacheConfiguration.java @@ -0,0 +1,152 @@ +package org.apache.archiva.rest.api.model.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.media.Schema; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; + +/** + * @author Olivier Lamy + * @author Martin Stockhammer + * @since 3.0 + */ +@XmlRootElement( name = "cacheConfiguration" ) +public class CacheConfiguration + implements Serializable +{ + private static final long serialVersionUID = 5479989049980673894L; + /** + * TimeToIdleSeconds. + */ + private int timeToIdleSeconds = -1; + + /** + * TimeToLiveSeconds. + */ + private int timeToLiveSeconds = -1; + + /** + * max elements in memory. + */ + private int maxEntriesInMemory = -1; + + /** + * max elements on disk. + */ + private int maxEntriesOnDisk = -1; + + public CacheConfiguration() + { + // no op + } + + public CacheConfiguration of( org.apache.archiva.admin.model.beans.CacheConfiguration beanConfiguration ) { + CacheConfiguration newConfig = new CacheConfiguration( ); + newConfig.setMaxEntriesInMemory( beanConfiguration.getMaxElementsInMemory() ); + newConfig.setMaxEntriesOnDisk( beanConfiguration.getMaxElementsOnDisk() ); + newConfig.setTimeToIdleSeconds( beanConfiguration.getTimeToIdleSeconds( ) ); + newConfig.setTimeToLiveSeconds( beanConfiguration.getTimeToLiveSeconds( ) ); + return newConfig; + } + + @Schema(description = "The maximum number of seconds an element can exist in the cache without being accessed. "+ + "The element expires at this limit and will no longer be returned from the cache.") + public int getTimeToIdleSeconds() + { + return timeToIdleSeconds; + } + + public void setTimeToIdleSeconds( int timeToIdleSeconds ) + { + this.timeToIdleSeconds = timeToIdleSeconds; + } + + @Schema(description = "The maximum number of seconds an element can exist in the cache regardless of use. "+ + "The element expires at this limit and will no longer be returned from the cache.") + public int getTimeToLiveSeconds() + { + return timeToLiveSeconds; + } + + public void setTimeToLiveSeconds( int timeToLiveSeconds ) + { + this.timeToLiveSeconds = timeToLiveSeconds; + } + + @Schema(description = "The maximum cache entries to keep in memory. If the limit is reached, older entries will be evicted, or persisted on disk.") + public int getMaxEntriesInMemory() + { + return maxEntriesInMemory; + } + + public void setMaxEntriesInMemory( int maxEntriesInMemory ) + { + this.maxEntriesInMemory = maxEntriesInMemory; + } + + @Schema(description = "The maximum cache entries to keep on disk. If the limit is reached, older entries will be evicted.") + public int getMaxEntriesOnDisk() + { + return maxEntriesOnDisk; + } + + public void setMaxEntriesOnDisk( int maxEntriesOnDisk ) + { + this.maxEntriesOnDisk = maxEntriesOnDisk; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) return true; + if ( o == null || getClass( ) != o.getClass( ) ) return false; + + CacheConfiguration that = (CacheConfiguration) o; + + if ( timeToIdleSeconds != that.timeToIdleSeconds ) return false; + if ( timeToLiveSeconds != that.timeToLiveSeconds ) return false; + if ( maxEntriesInMemory != that.maxEntriesInMemory ) return false; + return maxEntriesOnDisk == that.maxEntriesOnDisk; + } + + @Override + public int hashCode( ) + { + int result = timeToIdleSeconds; + result = 31 * result + timeToLiveSeconds; + result = 31 * result + maxEntriesInMemory; + result = 31 * result + maxEntriesOnDisk; + return result; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(); + sb.append( "CacheConfiguration" ); + sb.append( "{timeToIdleSeconds=" ).append( timeToIdleSeconds ); + sb.append( ", timeToLiveSeconds=" ).append( timeToLiveSeconds ); + sb.append( ", maxElementsInMemory=" ).append( maxEntriesInMemory ); + sb.append( ", maxElementsOnDisk=" ).append( maxEntriesOnDisk ); + sb.append( '}' ); + return sb.toString(); + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/LdapConfiguration.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/LdapConfiguration.java new file mode 100644 index 000000000..20026bafb --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/LdapConfiguration.java @@ -0,0 +1,263 @@ +package org.apache.archiva.rest.api.model.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.media.Schema; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +@XmlRootElement(name="ldapConfiguration") +public class LdapConfiguration implements Serializable +{ + private static final long serialVersionUID = -4736767846016398583L; + + private String hostName = ""; + private int port = 389; + private boolean sslEnabled = false; + private String baseDn = ""; + private String groupsBaseDn = ""; + private String bindDn = ""; + private String bindPassword = ""; + private String authenticationMethod = ""; + private boolean bindAuthenticatorEnabled = true; + private boolean useRoleNameAsGroup = false; + private final Map<String, String> properties = new TreeMap<>(); + private boolean writable = false; + + public LdapConfiguration( ) + { + } + + public static LdapConfiguration of( org.apache.archiva.admin.model.beans.LdapConfiguration ldapConfiguration ) { + LdapConfiguration newCfg = new LdapConfiguration( ); + newCfg.setAuthenticationMethod( ldapConfiguration.getAuthenticationMethod( ) ); + newCfg.setBaseDn( ldapConfiguration.getBaseDn( ) ); + newCfg.setGroupsBaseDn( ldapConfiguration.getBaseGroupsDn() ); + newCfg.setBindDn( ldapConfiguration.getBindDn() ); + newCfg.setBindPassword( ldapConfiguration.getPassword() ); + newCfg.setBindAuthenticatorEnabled( ldapConfiguration.isBindAuthenticatorEnabled() ); + newCfg.setHostName( ldapConfiguration.getHostName( ) ); + newCfg.setPort( ldapConfiguration.getPort( ) ); + newCfg.setProperties( ldapConfiguration.getExtraProperties( ) ); + newCfg.setSslEnabled( ldapConfiguration.isSsl() ); + newCfg.setWritable( ldapConfiguration.isWritable() ); + return newCfg; + } + + @Schema(description = "The hostname to use to connect to the LDAP server") + public String getHostName( ) + { + return hostName; + } + + public void setHostName( String hostName ) + { + this.hostName = hostName; + } + + @Schema(description = "The port to use to connect to the LDAP server") + public int getPort( ) + { + return port; + } + + public void setPort( int port ) + { + this.port = port; + } + + @Schema(description = "If SSL should be used for connecting the LDAP server") + public boolean isSslEnabled( ) + { + return sslEnabled; + } + + public void setSslEnabled( boolean sslEnabled ) + { + this.sslEnabled = sslEnabled; + } + + @Schema(description = "The BASE DN used for the LDAP server") + public String getBaseDn( ) + { + return baseDn; + } + + public void setBaseDn( String baseDn ) + { + this.baseDn = baseDn; + } + + @Schema(description = "The distinguished name of the bind user which is used to bind to the LDAP server") + public String getBindDn( ) + { + return bindDn; + } + + public void setBindDn( String bindDn ) + { + this.bindDn = bindDn; + } + + @Schema(description = "The password used to bind to the ldap server") + public String getBindPassword( ) + { + return bindPassword; + } + + public void setBindPassword( String bindPassword ) + { + this.bindPassword = bindPassword; + } + + @Schema(description = "The distinguished name of the base to use for searching group.") + public String getGroupsBaseDn( ) + { + return groupsBaseDn; + } + + public void setGroupsBaseDn( String groupsBaseDn ) + { + this.groupsBaseDn = groupsBaseDn; + } + + @Schema(description = "The authentication method used to bind to the LDAP server (PLAINTEXT, SASL, ...)") + public String getAuthenticationMethod( ) + { + return authenticationMethod; + } + + public void setAuthenticationMethod( String authenticationMethod ) + { + this.authenticationMethod = authenticationMethod; + } + + @Schema(description = "True, if the LDAP bind authentication is used for logging in to Archiva") + public boolean isBindAuthenticatorEnabled( ) + { + return bindAuthenticatorEnabled; + } + + public void setBindAuthenticatorEnabled( boolean bindAuthenticatorEnabled ) + { + this.bindAuthenticatorEnabled = bindAuthenticatorEnabled; + } + + @Schema(description = "True, if the archiva role name is also the LDAP group name") + public boolean isUseRoleNameAsGroup( ) + { + return useRoleNameAsGroup; + } + + public void setUseRoleNameAsGroup( boolean useRoleNameAsGroup ) + { + this.useRoleNameAsGroup = useRoleNameAsGroup; + } + + @Schema(description = "Map of additional properties") + public Map<String, String> getProperties( ) + { + return properties; + } + + public void setProperties( Map<String, String> properties ) + { + this.properties.clear(); + this.properties.putAll( properties ); + } + + @Schema(description = "True, if attributes in the the LDAP server can be edited by Archiva") + public boolean isWritable( ) + { + return writable; + } + + public void setWritable( boolean writable ) + { + this.writable = writable; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) return true; + if ( o == null || getClass( ) != o.getClass( ) ) return false; + + LdapConfiguration that = (LdapConfiguration) o; + + if ( port != that.port ) return false; + if ( sslEnabled != that.sslEnabled ) return false; + if ( bindAuthenticatorEnabled != that.bindAuthenticatorEnabled ) return false; + if ( useRoleNameAsGroup != that.useRoleNameAsGroup ) return false; + if ( writable != that.writable ) return false; + if ( !Objects.equals( hostName, that.hostName ) ) return false; + if ( !Objects.equals( baseDn, that.baseDn ) ) return false; + if ( !Objects.equals( bindDn, that.bindDn ) ) return false; + if ( !Objects.equals( groupsBaseDn, that.groupsBaseDn ) ) + return false; + if ( !Objects.equals( bindPassword, that.bindPassword ) ) return false; + if ( !Objects.equals( authenticationMethod, that.authenticationMethod ) ) + return false; + return properties.equals( that.properties ); + } + + @Override + public int hashCode( ) + { + int result = hostName != null ? hostName.hashCode( ) : 0; + result = 31 * result + port; + result = 31 * result + ( sslEnabled ? 1 : 0 ); + result = 31 * result + ( baseDn != null ? baseDn.hashCode( ) : 0 ); + result = 31 * result + ( bindDn != null ? bindDn.hashCode( ) : 0 ); + result = 31 * result + ( groupsBaseDn != null ? groupsBaseDn.hashCode( ) : 0 ); + result = 31 * result + ( bindPassword != null ? bindPassword.hashCode( ) : 0 ); + result = 31 * result + ( authenticationMethod != null ? authenticationMethod.hashCode( ) : 0 ); + result = 31 * result + ( bindAuthenticatorEnabled ? 1 : 0 ); + result = 31 * result + ( useRoleNameAsGroup ? 1 : 0 ); + result = 31 * result + properties.hashCode( ); + result = 31 * result + ( writable ? 1 : 0 ); + return result; + } + + @SuppressWarnings( "StringBufferReplaceableByString" ) + @Override + public String toString( ) + { + final StringBuilder sb = new StringBuilder( "LdapConfiguration{" ); + sb.append( "hostName='" ).append( hostName ).append( '\'' ); + sb.append( ", port=" ).append( port ); + sb.append( ", sslEnabled=" ).append( sslEnabled ); + sb.append( ", baseDn='" ).append( baseDn ).append( '\'' ); + sb.append( ", groupsBaseDn='" ).append( groupsBaseDn ).append( '\'' ); + sb.append( ", bindDn='" ).append( bindDn ).append( '\'' ); + sb.append( ", bindPassword='" ).append( bindPassword ).append( '\'' ); + sb.append( ", authenticationMethod='" ).append( authenticationMethod ).append( '\'' ); + sb.append( ", bindAuthenticatorEnabled=" ).append( bindAuthenticatorEnabled ); + sb.append( ", useRoleNameAsGroup=" ).append( useRoleNameAsGroup ); + sb.append( ", properties=" ).append( properties ); + sb.append( ", writable=" ).append( writable ); + sb.append( '}' ); + return sb.toString( ); + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/SecurityConfiguration.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/SecurityConfiguration.java new file mode 100644 index 000000000..bd72f7684 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/SecurityConfiguration.java @@ -0,0 +1,163 @@ +package org.apache.archiva.rest.api.model.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.media.Schema; +import org.apache.archiva.admin.model.beans.RedbackRuntimeConfiguration; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +@XmlRootElement(name = "securityConfiguration") +public class SecurityConfiguration implements Serializable +{ + private static final long serialVersionUID = -4186866365979053029L; + + private final List<String> activeUserManagers = new ArrayList<>( ); + private final List<String> activeRbacManagers = new ArrayList<>( ); + private final Map<String,String> properties = new TreeMap<>( ); + private boolean userCacheEnabled=false; + private boolean ldapActive=false; + + public SecurityConfiguration() { + + } + + public static SecurityConfiguration ofRedbackConfiguration( RedbackRuntimeConfiguration configuration ) { + SecurityConfiguration secConfig = new SecurityConfiguration( ); + secConfig.setActiveRbacManagers( configuration.getRbacManagerImpls() ); + secConfig.setActiveUserManagers( configuration.getUserManagerImpls() ); + secConfig.setProperties( configuration.getConfigurationProperties() ); + boolean rbLdapActive = configuration.getUserManagerImpls( ).stream( ).anyMatch( um -> um.contains( "ldap" ) ); + secConfig.setLdapActive( rbLdapActive ); + secConfig.setUserCacheEnabled( configuration.isUseUsersCache() ); + return secConfig; + } + + @Schema(description = "List of ids of the active user managers") + public List<String> getActiveUserManagers( ) + { + return activeUserManagers; + } + + public void setActiveUserManagers( List<String> activeUserManagers ) + { + this.activeUserManagers.clear(); + this.activeUserManagers.addAll( activeUserManagers ); + } + + public void addSelectedUserManager(String userManager) { + this.activeUserManagers.add( userManager ); + } + + @Schema(description = "List of ids of the active rbac managers") + public List<String> getActiveRbacManagers( ) + { + return activeRbacManagers; + } + + public void setActiveRbacManagers( List<String> activeRbacManagers ) + { + this.activeRbacManagers.clear(); + this.activeRbacManagers.addAll( activeRbacManagers ); + } + + public void addSelectedRbacManager(String rbacManager) { + this.activeRbacManagers.add( rbacManager ); + } + + @Schema(description = "Map of all security properties") + public Map<String, String> getProperties( ) + { + return properties; + } + + public void setProperties( Map<String, String> properties ) + { + this.properties.clear(); + this.properties.putAll( properties ); + } + + @Schema(description = "True, if the user cache is active. It caches data from user backend.") + public boolean isUserCacheEnabled( ) + { + return userCacheEnabled; + } + + public void setUserCacheEnabled( boolean userCacheEnabled ) + { + this.userCacheEnabled = userCacheEnabled; + } + + @Schema(description = "True, if LDAP is used as user manager") + public boolean isLdapActive( ) + { + return ldapActive; + } + + public void setLdapActive( boolean ldapActive ) + { + this.ldapActive = ldapActive; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) return true; + if ( o == null || getClass( ) != o.getClass( ) ) return false; + + SecurityConfiguration that = (SecurityConfiguration) o; + + if ( userCacheEnabled != that.userCacheEnabled ) return false; + if ( ldapActive != that.ldapActive ) return false; + if ( !activeUserManagers.equals( that.activeUserManagers ) ) return false; + if ( !activeRbacManagers.equals( that.activeRbacManagers ) ) return false; + return properties.equals( that.properties ); + } + + @Override + public int hashCode( ) + { + int result = activeUserManagers.hashCode( ); + result = 31 * result + activeRbacManagers.hashCode( ); + result = 31 * result + properties.hashCode( ); + result = 31 * result + ( userCacheEnabled ? 1 : 0 ); + result = 31 * result + ( ldapActive ? 1 : 0 ); + return result; + } + + @SuppressWarnings( "StringBufferReplaceableByString" ) + @Override + public String toString( ) + { + final StringBuilder sb = new StringBuilder( "SecurityConfiguration{" ); + sb.append( "selectedUserManagers=" ).append( activeUserManagers ); + sb.append( ", selectedRbacManagers=" ).append( activeRbacManagers ); + sb.append( ", properties=" ).append( properties ); + sb.append( ", userCacheEnabled=" ).append( userCacheEnabled ); + sb.append( ", ldapActive=" ).append( ldapActive ); + sb.append( '}' ); + return sb.toString( ); + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ArchivaRestError.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ArchivaRestError.java new file mode 100644 index 000000000..93a2b4fea --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ArchivaRestError.java @@ -0,0 +1,71 @@ +package org.apache.archiva.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.media.Schema; +import org.apache.commons.lang3.StringUtils; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Martin Stockhammer + * @since 3.0 + */ +@XmlRootElement( name = "archivaRestError" ) +@Schema(name="ArchivaRestError", description = "Contains a list of error messages that resulted from the current REST call") +public class ArchivaRestError + implements Serializable +{ + + private static final long serialVersionUID = -8892617571273167067L; + private List<ErrorMessage> errorMessages = new ArrayList<ErrorMessage>( 1 ); + + public ArchivaRestError() + { + // no op + } + + public ArchivaRestError( ArchivaRestServiceException e ) + { + errorMessages.addAll( e.getErrorMessages() ); + if ( e.getErrorMessages().isEmpty() && StringUtils.isNotEmpty( e.getMessage() ) ) + { + errorMessages.add( new ErrorMessage( e.getMessage(), null ) ); + } + } + + @Schema(name="errorMessages", description = "The list of errors that occurred while processing the REST request") + public List<ErrorMessage> getErrorMessages() + { + return errorMessages; + } + + public void setErrorMessages( List<ErrorMessage> errorMessages ) + { + this.errorMessages = errorMessages; + } + + public void addErrorMessage( ErrorMessage errorMessage ) + { + this.errorMessages.add( errorMessage ); + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ArchivaRestServiceException.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ArchivaRestServiceException.java new file mode 100644 index 000000000..c2ac99059 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ArchivaRestServiceException.java @@ -0,0 +1,96 @@ +package org.apache.archiva.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 java.util.ArrayList; +import java.util.List; + +/** + * Generic REST Service Exception that contains error information. + * + * @author Martin Stockhammer <martin_s@apache.org> + * @since 3.0 + */ +public class ArchivaRestServiceException extends Exception +{ + private int httpErrorCode = 500; + + private List<ErrorMessage> errorMessages = new ArrayList<ErrorMessage>(0); + + public ArchivaRestServiceException( String s ) + { + super( s ); + } + + public ArchivaRestServiceException( String s, int httpErrorCode ) + { + super( s ); + this.httpErrorCode = httpErrorCode; + } + + public ArchivaRestServiceException( ErrorMessage errorMessage ) + { + errorMessages.add( errorMessage ); + } + + public ArchivaRestServiceException( ErrorMessage errorMessage, int httpResponseCode ) + { + this.httpErrorCode = httpResponseCode; + errorMessages.add( errorMessage ); + } + + public ArchivaRestServiceException( List<ErrorMessage> errorMessage ) + { + errorMessages.addAll( errorMessage ); + } + + public ArchivaRestServiceException( List<ErrorMessage> errorMessage, int httpResponseCode ) + { + this.httpErrorCode = httpResponseCode; + errorMessages.addAll( errorMessage ); + } + + public int getHttpErrorCode() + { + return httpErrorCode; + } + + public void setHttpErrorCode( int httpErrorCode ) + { + this.httpErrorCode = httpErrorCode; + } + + public List<ErrorMessage> getErrorMessages() + { + if ( errorMessages == null ) + { + this.errorMessages = new ArrayList<ErrorMessage>(); + } + return errorMessages; + } + + public void setErrorMessages( List<ErrorMessage> errorMessages ) + { + this.errorMessages = errorMessages; + } + + public void addErrorMessage( ErrorMessage errorMessage ) + { + this.errorMessages.add( errorMessage ); + } + +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/Configuration.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/Configuration.java new file mode 100644 index 000000000..e0b63577a --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/Configuration.java @@ -0,0 +1,25 @@ +package org.apache.archiva.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. + */ + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +public interface Configuration +{ + String DEFAULT_PAGE_LIMIT = "10"; +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ErrorMessage.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ErrorMessage.java new file mode 100644 index 000000000..7c673cd46 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/ErrorMessage.java @@ -0,0 +1,103 @@ +package org.apache.archiva.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.media.Schema; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; + +/** + * @author Olivier Lamy + * @author Martin Stockhammer + * @since 3.0 + */ +@XmlRootElement( name = "errorMessage" ) +@Schema(name="ErrorMessage",description = "Information about the error, that occurred while processing the REST request.") +public class ErrorMessage + implements Serializable +{ + private String errorKey = ""; + + private String[] args = EMPTY; + + private String message = ""; + + private static final String[] EMPTY = new String[0]; + + public ErrorMessage() + { + // no op + } + + public ErrorMessage( String errorKey ) + { + this.errorKey = errorKey; + this.args = EMPTY; + } + + public ErrorMessage( String errorKey, String[] args ) + { + this.errorKey = errorKey; + this.args = args; + } + + public static ErrorMessage of(String errorKey, String... args) { + return new ErrorMessage( errorKey, args ); + } + + @Schema(description = "The key of the error message. If this is empty, the message message must be set.") + public String getErrorKey() + { + return errorKey; + } + + public void setErrorKey( String errorKey ) + { + this.errorKey = errorKey; + } + + @Schema(description = "Parameters that can be filled to the translated error message") + public String[] getArgs() + { + return args; + } + + public void setArgs( String[] args ) + { + this.args = args; + } + + @Schema(description = "Full error message. Either additional to the key in the default language, or if the message is without key.") + public String getMessage() + { + return message; + } + + public void setMessage( String message ) + { + this.message = message; + } + + public ErrorMessage message( String message ) + { + this.message = message; + return this; + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/SecurityConfigurationService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/SecurityConfigurationService.java index b494c3f73..59c57c53e 100644 --- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/SecurityConfigurationService.java +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/SecurityConfigurationService.java @@ -16,6 +16,33 @@ package org.apache.archiva.rest.api.services.v2;/* * under the License. */ +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.archiva.components.rest.model.PagedResult; +import org.apache.archiva.components.rest.model.PropertyEntry; +import org.apache.archiva.redback.authorization.RedbackAuthorization; +import org.apache.archiva.rest.api.model.v2.BeanInformation; +import org.apache.archiva.rest.api.model.v2.CacheConfiguration; +import org.apache.archiva.rest.api.model.v2.LdapConfiguration; +import org.apache.archiva.rest.api.model.v2.SecurityConfiguration; +import org.apache.archiva.security.common.ArchivaRoleConstants; + +import javax.ws.rs.DefaultValue; +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.List; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.apache.archiva.rest.api.services.v2.Configuration.DEFAULT_PAGE_LIMIT; + /** * * Service for configuration of redback and security related settings. @@ -23,6 +50,145 @@ package org.apache.archiva.rest.api.services.v2;/* * @author Martin Stockhammer <martin_s@apache.org> * @since 3.0 */ +@Path( "/security" ) +@Tag(name = "v2") +@Tag(name = "v2/Security") +@SecurityRequirement(name = "BearerAuth") public interface SecurityConfigurationService { + @Path("config") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + @RedbackAuthorization(permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION) + @Operation( summary = "Returns the security configuration that is currently active.", + security = { + @SecurityRequirement( + name = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION + ) + }, + responses = { + @ApiResponse( responseCode = "200", + description = "If the configuration could be retrieved" + ), + @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestServiceException.class )) ) + } + ) + SecurityConfiguration getConfiguration() + throws ArchivaRestServiceException; + + @GET + @Produces( { APPLICATION_JSON } ) + @RedbackAuthorization( permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION ) + @Operation( summary = "Returns all configuration properties. The result is paged.", + parameters = { + @Parameter(name = "q", description = "Search term"), + @Parameter(name = "offset", description = "The offset of the first element returned"), + @Parameter(name = "limit", description = "Maximum number of items to return in the response"), + @Parameter(name = "orderBy", description = "List of attribute used for sorting (user_id, fullName, email, created"), + @Parameter(name = "order", description = "The sort order. Either ascending (asc) or descending (desc)") + }, + security = { + @SecurityRequirement( + name = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION + ) + }, + responses = { + @ApiResponse( responseCode = "200", + description = "If the list could be returned", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = PagedResult.class)) + ), + @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestServiceException.class )) ) + } + ) + PagedResult<PropertyEntry> getConfigurationProperties( @QueryParam("q") @DefaultValue( "" ) String searchTerm, + @QueryParam( "offset" ) @DefaultValue( "0" ) Integer offset, + @QueryParam( "limit" ) @DefaultValue( value = DEFAULT_PAGE_LIMIT ) Integer limit, + @QueryParam( "orderBy") @DefaultValue( "id" ) List<String> orderBy, + @QueryParam("order") @DefaultValue( "asc" ) String order ) throws ArchivaRestServiceException; + + @Path("ldap") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + @RedbackAuthorization(permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION) + @Operation( summary = "Returns the LDAP configuration that is currently active.", + security = { + @SecurityRequirement( + name = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION + ) + }, + responses = { + @ApiResponse( responseCode = "200", + description = "If the configuration could be retrieved" + ), + @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestServiceException.class )) ) + } + ) + LdapConfiguration getLdapConfiguration( ) throws ArchivaRestServiceException; + + + @Path("user/cache") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + @RedbackAuthorization(permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION) + @Operation( summary = "Returns the cache configuration that is currently active.", + security = { + @SecurityRequirement( + name = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION + ) + }, + responses = { + @ApiResponse( responseCode = "200", + description = "If the configuration could be retrieved" + ), + @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestServiceException.class )) ) + } + ) + CacheConfiguration getCacheConfiguration( ) throws ArchivaRestServiceException; + + @Path("user/managers") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + @RedbackAuthorization(permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION) + @Operation( summary = "Returns the available user manager implementations.", + security = { + @SecurityRequirement( + name = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION + ) + }, + responses = { + @ApiResponse( responseCode = "200", + description = "If the list could be retrieved" + ), + @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestServiceException.class )) ) + } + ) + List<BeanInformation> getAvailableUserManagers() + throws ArchivaRestServiceException; + + @Path("rbac/managers") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + @RedbackAuthorization(permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION) + @Operation( summary = "Returns the available RBAC manager implementations.", + security = { + @SecurityRequirement( + name = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION + ) + }, + responses = { + @ApiResponse( responseCode = "200", + description = "If the list could be retrieved" + ), + @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", + content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestServiceException.class )) ) + } + ) + List<BeanInformation> getAvailableRbacManagers() + throws ArchivaRestServiceException; + } diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/pom.xml b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/pom.xml index dad1cd887..2183737cd 100644 --- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/pom.xml +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/pom.xml @@ -350,6 +350,12 @@ <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-features-logging</artifactId> </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-extension-providers</artifactId> + <scope>runtime</scope> + </dependency> + <!-- TEST Scope --> <dependency> @@ -470,6 +476,12 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>io.rest-assured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <!-- Needed for JDK >= 9 --> <dependency> diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/interceptors/v2/ArchivaRestServiceExceptionMapper.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/interceptors/v2/ArchivaRestServiceExceptionMapper.java new file mode 100644 index 000000000..17237cb85 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/interceptors/v2/ArchivaRestServiceExceptionMapper.java @@ -0,0 +1,68 @@ +package org.apache.archiva.rest.services.interceptors.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.rest.api.services.v2.ArchivaRestError; +import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException; +import org.springframework.stereotype.Service; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +/** + * Maps exceptions to REST responses. + * + * @author Martin Stockhammer + * @since 3.0 + */ +@Provider +@Service( "v2.archivaRestServiceExceptionMapper" ) +public class ArchivaRestServiceExceptionMapper + implements ExceptionMapper<ArchivaRestServiceException> +{ + @Override + public Response toResponse( final ArchivaRestServiceException e ) + { + ArchivaRestError restError = new ArchivaRestError( e ); + + Response.ResponseBuilder responseBuilder = Response.status( e.getHttpErrorCode() ).entity( restError ); + if ( e.getMessage() != null ) + { + responseBuilder = responseBuilder.status( new Response.StatusType() + { + public int getStatusCode() + { + return e.getHttpErrorCode(); + } + + public Response.Status.Family getFamily() + { + return Response.Status.Family.SERVER_ERROR; + } + + public String getReasonPhrase() + { + return e.getMessage(); + } + } ); + } + return responseBuilder.build(); + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java new file mode 100644 index 000000000..db580338d --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java @@ -0,0 +1,98 @@ +package org.apache.archiva.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.admin.model.RepositoryAdminException; +import org.apache.archiva.admin.model.beans.RedbackRuntimeConfiguration; +import org.apache.archiva.admin.model.runtime.RedbackRuntimeConfigurationAdmin; +import org.apache.archiva.components.rest.model.PagedResult; +import org.apache.archiva.components.rest.model.PropertyEntry; +import org.apache.archiva.rest.api.model.v2.BeanInformation; +import org.apache.archiva.rest.api.model.v2.CacheConfiguration; +import org.apache.archiva.rest.api.model.v2.LdapConfiguration; +import org.apache.archiva.rest.api.model.v2.SecurityConfiguration; +import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException; +import org.apache.archiva.rest.api.services.v2.ErrorMessage; +import org.apache.archiva.rest.api.services.v2.SecurityConfigurationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.inject.Inject; +import java.util.List; + +import static org.apache.archiva.rest.services.v2.ErrorKeys.REPOSITORY_ADMIN_ERROR; + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +@Service("v2.defaultSecurityConfigurationService") +public class DefaultSecurityConfigurationService implements SecurityConfigurationService +{ + private static final Logger log = LoggerFactory.getLogger( DefaultSecurityConfigurationService.class ); + + @Inject + private RedbackRuntimeConfigurationAdmin redbackRuntimeConfigurationAdmin; + + @Override + public SecurityConfiguration getConfiguration( ) throws ArchivaRestServiceException + { + try + { + RedbackRuntimeConfiguration redbackRuntimeConfiguration = + redbackRuntimeConfigurationAdmin.getRedbackRuntimeConfiguration(); + + log.debug( "getRedbackRuntimeConfiguration -> {}", redbackRuntimeConfiguration ); + + return SecurityConfiguration.ofRedbackConfiguration( redbackRuntimeConfiguration ); + } + catch ( RepositoryAdminException e ) + { + throw new ArchivaRestServiceException( ErrorMessage.of( REPOSITORY_ADMIN_ERROR ) ); + } + } + + @Override + public PagedResult<PropertyEntry> getConfigurationProperties( String searchTerm, Integer offset, Integer limit, List<String> orderBy, String order ) throws ArchivaRestServiceException + { + return null; + } + + @Override + public LdapConfiguration getLdapConfiguration( ) throws ArchivaRestServiceException + { + return null; + } + + @Override + public CacheConfiguration getCacheConfiguration( ) throws ArchivaRestServiceException + { + return null; + } + + @Override + public List<BeanInformation> getAvailableUserManagers( ) throws ArchivaRestServiceException + { + return null; + } + + @Override + public List<BeanInformation> getAvailableRbacManagers( ) throws ArchivaRestServiceException + { + return null; + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/ErrorKeys.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/ErrorKeys.java new file mode 100644 index 000000000..85e9c5c3d --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/ErrorKeys.java @@ -0,0 +1,26 @@ +package org.apache.archiva.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. + */ + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +public interface ErrorKeys +{ + public static final String REPOSITORY_ADMIN_ERROR = "a.repositoryadmin.error"; + +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml index 7d1f847b3..910fb7e9a 100644 --- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/resources/META-INF/spring-context.xml @@ -39,15 +39,31 @@ <context:component-scan base-package="org.apache.archiva.rest.services,org.apache.archiva.redback.rest.services"/> + <!-- CXF OpenApiFeature --> + <bean id="archivaOpenApiFeature" class="org.apache.cxf.jaxrs.openapi.OpenApiFeature"> + <property name="scanKnownConfigLocations" value="false"/> + <property name="configLocation" value="archiva/openapi-configuration.yaml"/> + <property name="scan" value="false"/> + <property name="useContextBasedConfig" value="true"/> + <!-- <property name="scannerClass" value="io.swagger.v3.jaxrs2.integration.JaxrsApplicationScanner"/> --> + </bean> + + <bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"> <property name="mapper" ref="redbackJacksonJsonMapper"/> </bean> + <bean id="v2.jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"> + <property name="mapper" ref="v2.redbackJacksonJsonMapper"/> + </bean> + <bean id="xmlProvider" class="com.fasterxml.jackson.jaxrs.xml.JacksonJaxbXMLProvider"> <property name="mapper" ref="redbackJacksonXMLMapper"/> </bean> <bean id="redbackJacksonJsonMapper" class="com.fasterxml.jackson.databind.ObjectMapper" /> + <bean id="v2.redbackJacksonJsonMapper" class="com.fasterxml.jackson.databind.ObjectMapper" > + </bean> <bean id="redbackJacksonXMLMapper" class="com.fasterxml.jackson.dataformat.xml.XmlMapper" /> @@ -87,6 +103,29 @@ </jaxrs:server> + + <jaxrs:server name="v2.archiva" address="/v2/archiva" > + + <jaxrs:providers> + <ref bean="v2.jsonProvider" /> + <ref bean="bearerAuthInterceptor#rest"/> + <ref bean="permissionInterceptor#rest"/> + <ref bean="requestValidationInterceptor#rest" /> + <ref bean="v2.archivaRestServiceExceptionMapper"/> + <ref bean="threadLocalUserCleaner#rest" /> + </jaxrs:providers> + + <jaxrs:serviceBeans> + <ref bean="v2.defaultSecurityConfigurationService" /> + </jaxrs:serviceBeans> + + <jaxrs:features> + <ref bean="archivaOpenApiFeature" /> + </jaxrs:features> + + </jaxrs:server> + + <bean name="browse#versionMetadata" class="org.apache.archiva.components.cache.ehcache.EhcacheCache" init-method="initialize"> <property name="diskPersistent" value="false"/> diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/AbstractNativeRestServices.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/AbstractNativeRestServices.java new file mode 100644 index 000000000..06d6f7429 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/AbstractNativeRestServices.java @@ -0,0 +1,517 @@ +package org.apache.archiva.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.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import io.restassured.RestAssured; +import io.restassured.builder.RequestSpecBuilder; +import io.restassured.config.ObjectMapperConfig; +import io.restassured.config.RestAssuredConfig; +import io.restassured.path.json.mapper.factory.Jackson2ObjectMapperFactory; +import io.restassured.response.Response; +import io.restassured.specification.RequestSpecification; +import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants; +import org.apache.archiva.redback.rest.services.BaseSetup; +import org.apache.archiva.redback.role.RoleManager; +import org.apache.archiva.redback.role.RoleManagerException; +import org.apache.archiva.redback.users.User; +import org.apache.archiva.redback.users.UserManager; +import org.apache.archiva.redback.users.UserManagerException; +import org.apache.archiva.redback.users.UserNotFoundException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.cxf.transport.servlet.CXFServlet; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.jupiter.api.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.context.ContextLoaderListener; + +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalTime; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static io.restassured.RestAssured.*; +import static io.restassured.http.ContentType.JSON; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Native REST tests do not use the JAX-RS client and can be used with a remote + * REST API service. The tests + * + * @author Martin Stockhammer <martin_s@apache.org> + */ +@Tag( "rest-native" ) +@Tag( "rest-v2" ) +public abstract class AbstractNativeRestServices +{ + private AtomicReference<Path> projectDir = new AtomicReference<>(); + private AtomicReference<Path> appServerBase = new AtomicReference<>( ); + private AtomicReference<Path> basePath = new AtomicReference<>( ); + + public static final int STOPPED = 0; + public static final int STOPPING = 1; + public static final int STARTING = 2; + public static final int STARTED = 3; + public static final int ERROR = 4; + private final boolean startServer; + private final String serverPort; + private final String baseUri; + + private RequestSpecification requestSpec; + protected Logger log = LoggerFactory.getLogger( getClass( ) ); + + private static AtomicReference<Server> server = new AtomicReference<>( ); + private static AtomicReference<ServerConnector> serverConnector = new AtomicReference<>( ); + private static AtomicInteger serverStarted = new AtomicInteger( STOPPED ); + private UserManager userManager; + private RoleManager roleManager; + + private final boolean remoteService; + + private String adminToken; + private String adminRefreshToken; + + + public AbstractNativeRestServices( ) + { + this.startServer = BaseSetup.startServer( ); + this.serverPort = BaseSetup.getServerPort( ); + this.baseUri = BaseSetup.getBaseUri( ); + + if ( startServer ) + { + this.remoteService = false; + } else { + this.remoteService = true; + } + } + + protected abstract String getServicePath( ); + + protected String getSpringConfigLocation( ) + { + return "classpath*:META-INF/spring-context.xml,classpath:META-INF/spring-context-native-test.xml"; + } + + protected RequestSpecification getRequestSpec( ) + { + return this.requestSpec; + } + + protected String getContextRoot( ) + { + return "/api"; + } + + + protected String getServiceBasePath( ) + { + return "/v2/archiva"; + } + + protected String getRedbackServiceBasePath( ) + { + return "/v2/redback"; + } + + + protected String getBasePath( ) + { + return new StringBuilder( ) + .append( getContextRoot( ) ) + .append( getServiceBasePath( ) ) + .append( getServicePath( ) ).toString( ); + } + + /** + * Returns the server that was started, or null if not initialized before. + * + * @return + */ + public Server getServer( ) + { + return this.server.get( ); + } + + public int getServerPort( ) + { + ServerConnector connector = serverConnector.get( ); + if ( connector != null ) + { + return connector.getLocalPort( ); + } + else + { + return 0; + } + } + + /** + * Returns true, if the server does exist and is running. + * + * @return true, if server does exist and is running. + */ + public boolean isServerRunning( ) + { + return serverStarted.get( ) == STARTED && this.server.get( ) != null && this.server.get( ).isRunning( ); + } + + private UserManager getUserManager( ) + { + if ( this.userManager == null ) + { + UserManager userManager = ContextLoaderListener.getCurrentWebApplicationContext( ) + .getBean( "userManager#default", UserManager.class ); + assertNotNull( userManager ); + this.userManager = userManager; + } + return this.userManager; + } + + private RoleManager getRoleManager( ) + { + if ( this.roleManager == null ) + { + RoleManager roleManager = ContextLoaderListener.getCurrentWebApplicationContext( ) + .getBean( "roleManager", RoleManager.class ); + assertNotNull( roleManager ); + this.roleManager = roleManager; + } + return this.roleManager; + } + + protected String getAdminPwd( ) + { + return BaseSetup.getAdminPwd( ); + } + + protected String getAdminUser( ) + { + return RedbackRoleConstants.ADMINISTRATOR_ACCOUNT_NAME; + } + + private void setupAdminUser( ) throws UserManagerException, RoleManagerException + { + + UserManager um = getUserManager( ); + + User adminUser = null; + try + { + adminUser = um.findUser( getAdminUser( ) ); + } + catch ( UserNotFoundException e ) + { + // ignore + } + adminUser = um.createUser( getAdminUser( ), "Administrator", "admin@local.home" ); + adminUser.setUsername( getAdminUser( ) ); + adminUser.setPassword( getAdminPwd( ) ); + adminUser.setFullName( "the admin user" ); + adminUser.setEmail( "toto@toto.fr" ); + adminUser.setPermanent( true ); + adminUser.setValidated( true ); + adminUser.setLocked( false ); + adminUser.setPasswordChangeRequired( false ); + if ( adminUser == null ) + { + um.addUser( adminUser ); + } + else + { + um.updateUser( adminUser, false ); + } + getRoleManager( ).assignRole( "system-administrator", adminUser.getUsername( ) ); + } + + protected Path getProjectDirectory() { + if ( projectDir.get()==null) { + String propVal = System.getProperty("mvn.project.base.dir"); + Path newVal; + if (StringUtils.isEmpty(propVal)) { + newVal = Paths.get("").toAbsolutePath(); + } else { + newVal = Paths.get(propVal).toAbsolutePath(); + } + projectDir.compareAndSet(null, newVal); + } + return projectDir.get(); + } + + public Path getBasedir() + { + if (basePath.get()==null) { + String baseDir = System.getProperty( "basedir" ); + final Path baseDirPath; + if (StringUtils.isNotEmpty( baseDir )) { + baseDirPath = Paths.get( baseDir ); + } else { + baseDirPath = getProjectDirectory( ); + } + basePath.compareAndSet( null, baseDirPath ); + } + return basePath.get( ); + } + + Path getAppserverBase() { + if (appServerBase.get()==null) { + String basePath = System.getProperty( "appserver.base" ); + final Path appserverPath; + if (StringUtils.isNotEmpty( basePath )) { + appserverPath = Paths.get( basePath ).toAbsolutePath( ); + } else { + appserverPath = getBasedir( ).resolve( "target" ).resolve( "appserver-base-" + LocalTime.now( ).toSecondOfDay( ) ); + } + appServerBase.compareAndSet( null, appserverPath ); + } + return appServerBase.get(); + } + + private void removeAppsubFolder( Path appServerBase, String folder ) + throws Exception + { + Path directory = appServerBase.resolve( folder ); + if ( Files.exists(directory) ) + { + org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory ); + } + } + + public void startServer( ) + throws Exception + { + if ( serverStarted.compareAndSet( STOPPED, STARTING ) ) + { + try + { + log.info( "Starting server" ); + Path appServerBase = getAppserverBase( ); + + removeAppsubFolder(appServerBase, "jcr"); + removeAppsubFolder(appServerBase, "conf"); + removeAppsubFolder(appServerBase, "data"); + + + Server myServer = new Server( ); + this.server.set( myServer ); + this.serverConnector.set( new ServerConnector( myServer, new HttpConnectionFactory( ) ) ); + myServer.addConnector( serverConnector.get( ) ); + + ServletHolder servletHolder = new ServletHolder( new CXFServlet( ) ); + ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS ); + context.setResourceBase( SystemUtils.JAVA_IO_TMPDIR ); + context.setSessionHandler( new SessionHandler( ) ); + context.addServlet( servletHolder, getContextRoot( ) + "/*" ); + context.setInitParameter( "contextConfigLocation", getSpringConfigLocation( ) ); + context.addEventListener( new ContextLoaderListener( ) ); + + getServer( ).setHandler( context ); + getServer( ).start( ); + + if ( log.isDebugEnabled( ) ) + { + log.debug( "Jetty dump: {}", getServer( ).dump( ) ); + } + + setupAdminUser( ); + log.info( "Started server on port {}", getServerPort( ) ); + serverStarted.set( STARTED ); + } + finally + { + // In case, if the last statement was not reached + serverStarted.compareAndSet( STARTING, ERROR ); + } + } + + } + + public void stopServer( ) + throws Exception + { + if ( this.serverStarted.compareAndSet( STARTED, STOPPING ) ) + { + try + { + final Server myServer = getServer( ); + if ( myServer != null ) + { + log.info( "Stopping server" ); + myServer.stop( ); + } + serverStarted.set( STOPPED ); + } + finally + { + serverStarted.compareAndSet( STOPPING, ERROR ); + } + } + else + { + log.error( "Serer is not in STARTED state!" ); + } + } + + + protected void setupNative( ) throws Exception + { + if ( this.startServer ) + { + startServer( ); + } + + if ( StringUtils.isNotEmpty( serverPort ) ) + { + RestAssured.port = Integer.parseInt( serverPort ); + } + else + { + RestAssured.port = getServerPort( ); + } + if ( StringUtils.isNotEmpty( baseUri ) ) + { + RestAssured.baseURI = baseUri; + } + else + { + RestAssured.baseURI = "http://localhost"; + } + String basePath = getBasePath( ); + this.requestSpec = getRequestSpecBuilder( ).build( ); + RestAssured.basePath = basePath; + RestAssured.config = RestAssuredConfig.config().objectMapperConfig(new ObjectMapperConfig().jackson2ObjectMapperFactory( + new Jackson2ObjectMapperFactory() { + @Override + public ObjectMapper create( Type cls, String charset) { + ObjectMapper om = new ObjectMapper().findAndRegisterModules(); + om.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + om.setPropertyNamingStrategy( PropertyNamingStrategy.SNAKE_CASE ); + return om; + } + + } + )); + } + + protected RequestSpecBuilder getRequestSpecBuilder( ) { + return getRequestSpecBuilder( null ); + } + + protected RequestSpecBuilder getRequestSpecBuilder( String basePath ) + { + String myBasePath = basePath == null ? getBasePath( ) : basePath; + return new RequestSpecBuilder( ).setBaseUri( baseURI ) + .setPort( port ) + .setBasePath( myBasePath ) + .addHeader( "Origin", RestAssured.baseURI + ":" + RestAssured.port ); + } + + protected RequestSpecBuilder getAuthRequestSpecBuilder( ) + { + return new RequestSpecBuilder( ).setBaseUri( baseURI ) + .setPort( port ) + .setBasePath( new StringBuilder( ) + .append( getContextRoot( ) ) + .append( getRedbackServiceBasePath() ).append("/auth").toString() ) + .addHeader( "Origin", RestAssured.baseURI + ":" + RestAssured.port ); + } + + protected RequestSpecification getRequestSpec( String bearerToken ) + { + return getRequestSpecBuilder( ).addHeader( "Authorization", "Bearer " + bearerToken ).build( ); + } + + protected RequestSpecification getRequestSpec( String bearerToken, String path) + { + return getRequestSpecBuilder( path ).addHeader( "Authorization", "Bearer " + bearerToken ).build( ); + } + + protected void shutdownNative( ) throws Exception + { + if (startServer) + { + stopServer( ); + } + } + + protected org.apache.archiva.redback.rest.api.model.User addRemoteUser(String userid, String password, String fullName, String mail) { + + return null; + } + + protected void initAdminToken() { + Map<String, Object> jsonAsMap = new HashMap<>(); + jsonAsMap.put( "grant_type", "authorization_code" ); + jsonAsMap.put("user_id", getAdminUser()); + jsonAsMap.put("password", getAdminPwd() ); + Response result = given( ).spec( getAuthRequestSpecBuilder().build() ) + .contentType( JSON ) + .body( jsonAsMap ) + .when( ).post( "/authenticate").then( ).statusCode( 200 ) + .extract( ).response( ); + this.adminToken = result.body( ).jsonPath( ).getString( "access_token" ); + this.adminRefreshToken = result.body( ).jsonPath( ).getString( "refresh_token" ); + } + + protected String getUserToken(String userId, String password) { + Map<String, Object> jsonAsMap = new HashMap<>(); + jsonAsMap.put( "grant_type", "authorization_code" ); + jsonAsMap.put("user_id", userId); + jsonAsMap.put("password", password ); + Response result = given( ).spec( getAuthRequestSpecBuilder().build() ) + .contentType( JSON ) + .body( jsonAsMap ) + .when( ).post( "/authenticate").prettyPeek().then( ).statusCode( 200 ) + .extract( ).response( ); + result.getBody( ).prettyPrint( ); + return result.body( ).jsonPath( ).getString( "access_token" ); + } + protected String getAdminToken() { + if (this.adminToken == null) { + initAdminToken(); + } + return this.adminToken; + } + + + protected String getAdminRefreshToken() { + if (this.adminRefreshToken == null) { + initAdminToken(); + } + return this.adminRefreshToken; + } + + public boolean isRemoteService() { + return this.remoteService; + } +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/NativeSecurityConfigurationServiceTest.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/NativeSecurityConfigurationServiceTest.java new file mode 100644 index 000000000..eff49c12c --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/NativeSecurityConfigurationServiceTest.java @@ -0,0 +1,82 @@ +package org.apache.archiva.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 io.restassured.response.Response; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static io.restassured.RestAssured.given; +import static io.restassured.http.ContentType.JSON; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Martin Stockhammer <martin_s@apache.org> + */ +@TestInstance( TestInstance.Lifecycle.PER_CLASS ) +@Tag( "rest-native" ) +@TestMethodOrder( MethodOrderer.Random.class ) +@DisplayName( "Native REST tests for V2 SecurityConfigurationService" ) +public class NativeSecurityConfigurationServiceTest extends AbstractNativeRestServices +{ + @Override + protected String getServicePath( ) + { + return "/security"; + } + + @BeforeAll + void setup( ) throws Exception + { + super.setupNative( ); + } + + @AfterAll + void destroy( ) throws Exception + { + super.shutdownNative( ); + } + + @Test + void testGetConfiguration() { + String token = getAdminToken( ); + Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON ) + .when( ) + .get( "config" ) + .prettyPeek() + .then( ).statusCode( 200 ).extract( ).response( ); + assertNotNull( response ); + assertEquals( "jpa", response.getBody( ).jsonPath( ).getString( "active_user_managers[0]" ) ); + assertEquals( "jpa", response.getBody( ).jsonPath( ).getString( "active_rbac_managers[0]" ) ); + assertEquals( "memory", response.getBody( ).jsonPath( ).getString( "properties.\"authentication.jwt.keystoreType\"" ) ); + assertEquals("10",response.getBody( ).jsonPath( ).getString( "properties.\"security.policy.allowed.login.attempt\"")); + assertTrue( response.getBody( ).jsonPath( ).getBoolean( "user_cache_enabled" ) ); + assertFalse( response.getBody( ).jsonPath( ).getBoolean( "ldap_active" ) ); + } + +} diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/resources/META-INF/spring-context-native-test.xml b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/resources/META-INF/spring-context-native-test.xml new file mode 100644 index 000000000..79379ad65 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/resources/META-INF/spring-context-native-test.xml @@ -0,0 +1,143 @@ +<?xml version="1.0"?> + +<!-- + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xmlns:tx="http://www.springframework.org/schema/tx" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd + + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" + default-lazy-init="true"> + + <context:annotation-config/> + <context:component-scan + base-package="org.apache.archiva.redback.configuration,org.apache.archiva.redback.keys,org.apache.archiva.rest.services.utils,org.apache.archiva.repository.content"/> + + <bean name="scheduler" class="org.apache.archiva.components.scheduler.DefaultScheduler"> + <property name="properties"> + <props> + <prop key="org.quartz.scheduler.instanceName">scheduler1</prop> + <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop> + <prop key="org.quartz.threadPool.threadCount">2</prop> + <prop key="org.quartz.threadPool.threadPriority">4</prop> + <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop> + </props> + </property> + </bean> + + <!-- wire up more basic configuration so it doesn't overwrite any config files --> + <bean name="archivaConfiguration#default" class="org.apache.archiva.configuration.DefaultArchivaConfiguration"> + <property name="registry" ref="registry#default"/> + </bean> + + <alias name="archivaConfiguration#default" alias="archivaConfiguration"/> + + <bean name="registry#default" class="org.apache.archiva.components.registry.commons.CommonsConfigurationRegistry"> + <property name="initialConfiguration"> + <value> + <![CDATA[ + <configuration> + <system/> + <xml fileName="${appserver.base}/conf/archiva.xml" config-forceCreate="true" + config-optional="true" + config-name="org.apache.archiva.base" config-at="org.apache.archiva"/> + <properties fileName="${basedir}/src/test/resources/security.properties" config-optional="true" + config-at="org.apache.archiva.redback"/> + + </configuration> + ]]> + </value> + </property> + </bean> + + <bean name="taskQueueExecutor#repository-scanning" + class="org.apache.archiva.components.taskqueue.execution.ThreadedTaskQueueExecutor" lazy-init="false"> + <property name="name" value="repository-scanning"/> + <property name="executor" ref="taskExecutor#repository-scanning"/> + <property name="queue" ref="taskQueue#repository-scanning"/> + </bean> + + <!-- + <bean id="repository" class="org.apache.jackrabbit.core.RepositoryImpl" destroy-method="shutdown"> + <constructor-arg ref="config"/> + </bean> + + <bean id="config" class="org.apache.jackrabbit.core.config.RepositoryConfig" factory-method="create"> + <constructor-arg value="${basedir}/src/test/repository.xml"/> + <constructor-arg value="${appserver.base}/jcr"/> + </bean> + --> + + <bean name="commons-configuration" class="org.apache.archiva.components.registry.commons.CommonsConfigurationRegistry" + init-method="initialize"> + <property name="initialConfiguration"> + <value> + <![CDATA[ + <configuration> + <system/> + <properties fileName="${basedir}/src/test/resources/security.properties" config-optional="true" + config-at="org.apache.archiva.redback"/> + </configuration> + ]]> + </value> + </property> + </bean> + + + <alias name="redbackRuntimeConfigurationAdmin#default" alias="userConfiguration#default"/> + + <alias name="authorizer#rbac" alias="authorizer#default"/> + + <alias name="userManager#configurable" alias="userManager#default"/> + + <!-- *** + JPA settings + *** --> + <bean name="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> + + <property name="persistenceXmlLocation" value="classpath:META-INF/persistence-hsqldb.xml" /> + <property name="jpaPropertyMap"> + <map> + <entry key="openjpa.ConnectionURL" value="jdbc:hsqldb:mem:redback_database" /> + <entry key="openjpa.ConnectionDriverName" value="org.hsqldb.jdbcDriver" /> + <entry key="openjpa.ConnectionUserName" value="sa" /> + <entry key="openjpa.ConnectionPassword" value="" /> + <entry key="openjpa.Log" value="${openjpa.Log:DefaultLevel=INFO,Runtime=ERROR,Tool=ERROR,SQL=ERROR,Schema=ERROR,MetaData=ERROR}" /> + <entry key="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" /> + <entry key="openjpa.jdbc.MappingDefaults" + value="ForeignKeyDeleteAction=restrict,JoinForeignKeyDeleteAction=restrict"/> + </map> + </property> + + </bean> + + <bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" > + <property name="entityManagerFactory" ref="entityManagerFactory" /> + </bean> + + <tx:annotation-driven /> + <!-- *** + End of JPA settings + *** --> +</beans>
\ No newline at end of file diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/resources/log4j2-test.xml b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/resources/log4j2-test.xml index 8e0fce6e0..c0414be38 100644 --- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/resources/log4j2-test.xml +++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/resources/log4j2-test.xml @@ -39,11 +39,11 @@ <loggers> <logger name="jaxrs" level="info" /> - <logger name="org.apache.cxf" level="info" /> + <logger name="org.apache.cxf" level="debug" /> <logger name="org.apache.archiva" level="debug" /> <logger name="org.apache.archiva.redback" level="debug"/> <logger name="com.fasterxml.jackson" level="info" /> - <logger name="org.apache.archiva.components.registry.commons" level="error" /> + <logger name="org.apache.archiva.components" level="error" /> <logger name="JPOX" level="error"/> <logger name="org.apache.archiva.rest.services" level="info"/> |