mirror of https://github.com/apache/archiva.git
Adding V2 REST services
This commit is contained in:
parent
befb43799e
commit
d5d9c6d6d1
|
@ -31,6 +31,8 @@
|
||||||
<properties>
|
<properties>
|
||||||
<enunciate.docsDir>${project.build.outputDirectory}/rest-docs-archiva-rest-api</enunciate.docsDir>
|
<enunciate.docsDir>${project.build.outputDirectory}/rest-docs-archiva-rest-api</enunciate.docsDir>
|
||||||
<site.staging.base>${project.parent.parent.parent.basedir}</site.staging.base>
|
<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>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -73,6 +75,11 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.archiva.components</groupId>
|
||||||
|
<artifactId>archiva-components-rest-util</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jakarta.ws.rs</groupId>
|
<groupId>jakarta.ws.rs</groupId>
|
||||||
<artifactId>jakarta.ws.rs-api</artifactId>
|
<artifactId>jakarta.ws.rs-api</artifactId>
|
||||||
|
@ -82,6 +89,23 @@
|
||||||
<artifactId>jakarta.annotation-api</artifactId>
|
<artifactId>jakarta.annotation-api</artifactId>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<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.
|
* 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.
|
* 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>
|
* @author Martin Stockhammer <martin_s@apache.org>
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
|
@Path( "/security" )
|
||||||
|
@Tag(name = "v2")
|
||||||
|
@Tag(name = "v2/Security")
|
||||||
|
@SecurityRequirement(name = "BearerAuth")
|
||||||
public interface SecurityConfigurationService
|
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>
|
<groupId>org.apache.cxf</groupId>
|
||||||
<artifactId>cxf-rt-features-logging</artifactId>
|
<artifactId>cxf-rt-features-logging</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cxf</groupId>
|
||||||
|
<artifactId>cxf-rt-rs-extension-providers</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- TEST Scope -->
|
<!-- TEST Scope -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -470,6 +476,12 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.rest-assured</groupId>
|
||||||
|
<artifactId>rest-assured</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- Needed for JDK >= 9 -->
|
<!-- Needed for JDK >= 9 -->
|
||||||
<dependency>
|
<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
|
<context:component-scan
|
||||||
base-package="org.apache.archiva.rest.services,org.apache.archiva.redback.rest.services"/>
|
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">
|
<bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider">
|
||||||
<property name="mapper" ref="redbackJacksonJsonMapper"/>
|
<property name="mapper" ref="redbackJacksonJsonMapper"/>
|
||||||
</bean>
|
</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">
|
<bean id="xmlProvider" class="com.fasterxml.jackson.jaxrs.xml.JacksonJaxbXMLProvider">
|
||||||
<property name="mapper" ref="redbackJacksonXMLMapper"/>
|
<property name="mapper" ref="redbackJacksonXMLMapper"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="redbackJacksonJsonMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
|
<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" />
|
<bean id="redbackJacksonXMLMapper" class="com.fasterxml.jackson.dataformat.xml.XmlMapper" />
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,6 +103,29 @@
|
||||||
|
|
||||||
</jaxrs:server>
|
</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"
|
<bean name="browse#versionMetadata" class="org.apache.archiva.components.cache.ehcache.EhcacheCache"
|
||||||
init-method="initialize">
|
init-method="initialize">
|
||||||
<property name="diskPersistent" value="false"/>
|
<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>
|
<loggers>
|
||||||
|
|
||||||
<logger name="jaxrs" level="info" />
|
<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" level="debug" />
|
||||||
<logger name="org.apache.archiva.redback" level="debug"/>
|
<logger name="org.apache.archiva.redback" level="debug"/>
|
||||||
<logger name="com.fasterxml.jackson" level="info" />
|
<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="JPOX" level="error"/>
|
||||||
<logger name="org.apache.archiva.rest.services" level="info"/>
|
<logger name="org.apache.archiva.rest.services" level="info"/>
|
||||||
|
|
92
pom.xml
92
pom.xml
|
@ -865,6 +865,47 @@
|
||||||
<artifactId>cxf-rt-features-logging</artifactId>
|
<artifactId>cxf-rt-features-logging</artifactId>
|
||||||
<version>${cxf.version}</version>
|
<version>${cxf.version}</version>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>org.apache.archiva</groupId>
|
<groupId>org.apache.archiva</groupId>
|
||||||
|
@ -1135,12 +1176,16 @@
|
||||||
<version>${maven.resolver.version}</version>
|
<version>${maven.resolver.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.archiva.components</groupId>
|
||||||
|
<artifactId>archiva-components-rest-util</artifactId>
|
||||||
|
<version>${archiva.comp.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.archiva.components</groupId>
|
<groupId>org.apache.archiva.components</groupId>
|
||||||
<artifactId>archiva-components-expression-evaluator</artifactId>
|
<artifactId>archiva-components-expression-evaluator</artifactId>
|
||||||
<version>${archiva.comp.version}</version>
|
<version>${archiva.comp.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.archiva.components</groupId>
|
<groupId>org.apache.archiva.components</groupId>
|
||||||
<artifactId>archiva-components-spring-taskqueue</artifactId>
|
<artifactId>archiva-components-spring-taskqueue</artifactId>
|
||||||
|
@ -1494,6 +1539,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Transitive dependency - fixing version -->
|
<!-- Transitive dependency - fixing version -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
|
@ -1729,6 +1775,12 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.rest-assured</groupId>
|
||||||
|
<artifactId>rest-assured</artifactId>
|
||||||
|
<version>${rest-assured.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- JUNIT 5 -->
|
<!-- JUNIT 5 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -1788,44 +1840,6 @@
|
||||||
<version>${cxf.version}</version>
|
<version>${cxf.version}</version>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue