@@ -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> | |||
@@ -73,6 +75,11 @@ | |||
</exclusion> | |||
</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> | |||
@@ -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> |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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( ); | |||
} | |||
} |
@@ -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( ); | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} |
@@ -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"; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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> |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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"; | |||
} |
@@ -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"/> |
@@ -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; | |||
} | |||
} |
@@ -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" ) ); | |||
} | |||
} |
@@ -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> |
@@ -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"/> |
@@ -865,6 +865,47 @@ | |||
<artifactId>cxf-rt-features-logging</artifactId> | |||
<version>${cxf.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.cxf</groupId> | |||
<artifactId>cxf-rt-rs-service-description-openapi-v3</artifactId> | |||
<version>${cxf.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.swagger.core.v3</groupId> | |||
<artifactId>swagger-core</artifactId> | |||
<scope>compile</scope> | |||
<version>${io.swagger.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>javax.ws.rs</groupId> | |||
<artifactId>jsr311-api</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.swagger.core.v3</groupId> | |||
<artifactId>swagger-jaxrs2</artifactId> | |||
<version>${io.swagger.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>javax.ws.rs</groupId> | |||
<artifactId>jsr311-api</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.swagger.core.v3</groupId> | |||
<artifactId>swagger-annotations</artifactId> | |||
<version>${io.swagger.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>javax.ws.rs</groupId> | |||
<artifactId>jsr311-api</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.archiva</groupId> | |||
@@ -1135,12 +1176,16 @@ | |||
<version>${maven.resolver.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.archiva.components</groupId> | |||
<artifactId>archiva-components-rest-util</artifactId> | |||
<version>${archiva.comp.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.archiva.components</groupId> | |||
<artifactId>archiva-components-expression-evaluator</artifactId> | |||
<version>${archiva.comp.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.archiva.components</groupId> | |||
<artifactId>archiva-components-spring-taskqueue</artifactId> | |||
@@ -1494,6 +1539,7 @@ | |||
</dependency> | |||
<!-- Transitive dependency - fixing version --> | |||
<dependency> | |||
<groupId>com.google.guava</groupId> | |||
@@ -1729,6 +1775,12 @@ | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.rest-assured</groupId> | |||
<artifactId>rest-assured</artifactId> | |||
<version>${rest-assured.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- JUNIT 5 --> | |||
<dependency> | |||
@@ -1788,44 +1840,6 @@ | |||
<version>${cxf.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.swagger.core.v3</groupId> | |||
<artifactId>swagger-core</artifactId> | |||
<scope>compile</scope> | |||
<version>${io.swagger.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>javax.ws.rs</groupId> | |||
<artifactId>jsr311-api</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.swagger.core.v3</groupId> | |||
<artifactId>swagger-jaxrs2</artifactId> | |||
<version>${io.swagger.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>javax.ws.rs</groupId> | |||
<artifactId>jsr311-api</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.swagger.core.v3</groupId> | |||
<artifactId>swagger-annotations</artifactId> | |||
<version>${io.swagger.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>javax.ws.rs</groupId> | |||
<artifactId>jsr311-api</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
</dependencies> | |||
</dependencyManagement> | |||