merge with branch 1.x

This commit is contained in:
Sebastian Sdorra
2014-03-17 10:49:52 +01:00
51 changed files with 2711 additions and 210 deletions

16
pom.xml
View File

@@ -169,6 +169,14 @@
<version>1.0</version> <version>1.0</version>
</signature> </signature>
</configuration> </configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>
@@ -438,7 +446,7 @@
<logback.version>1.1.1</logback.version> <logback.version>1.1.1</logback.version>
<servlet.version>2.5</servlet.version> <servlet.version>2.5</servlet.version>
<guice.version>3.0</guice.version> <guice.version>3.0</guice.version>
<jersey.version>1.18</jersey.version> <jersey.version>1.18.1</jersey.version>
<freemarker.version>2.3.20</freemarker.version> <freemarker.version>2.3.20</freemarker.version>
<jetty.version>7.6.14.v20131031</jetty.version> <jetty.version>7.6.14.v20131031</jetty.version>
@@ -446,11 +454,11 @@
<legman.version>1.2.0</legman.version> <legman.version>1.2.0</legman.version>
<!-- security libraries --> <!-- security libraries -->
<shiro.version>1.2.2</shiro.version> <shiro.version>1.2.3</shiro.version>
<!-- repostitory libraries --> <!-- repostitory libraries -->
<jgit.version>3.2.0.201312181205-r</jgit.version> <jgit.version>3.3.0.201403021825-r</jgit.version>
<svnkit.version>1.8.3-scm1</svnkit.version> <svnkit.version>1.8.4-scm2</svnkit.version>
<!-- util libraries --> <!-- util libraries -->
<guava.version>16.0</guava.version> <guava.version>16.0</guava.version>

View File

@@ -41,7 +41,7 @@
<dependency> <dependency>
<groupId>args4j</groupId> <groupId>args4j</groupId>
<artifactId>args4j</artifactId> <artifactId>args4j</artifactId>
<version>2.0.25</version> <version>2.0.26</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -98,7 +98,22 @@ public class GetRepositorySubCommand extends TemplateSubCommand
protected void run() protected void run()
{ {
ScmClientSession session = createSession(); ScmClientSession session = createSession();
Repository repository = session.getRepositoryHandler().get(id);
Repository repository;
int index = id.indexOf("/");
if (index > 0)
{
String type = id.substring(0, index);
String name = id.substring(index + 1);
repository = session.getRepositoryHandler().get(type, name);
}
else
{
repository = session.getRepositoryHandler().get(id);
}
if (repository != null) if (repository != null)
{ {
@@ -117,7 +132,7 @@ public class GetRepositorySubCommand extends TemplateSubCommand
/** Field description */ /** Field description */
@Argument( @Argument(
usage = "optionRepositoryId", usage = "optionRepositoryIdOrTypeAndName",
metaVar = "repositoryid", metaVar = "repositoryid",
required = true required = true
) )

View File

@@ -44,6 +44,7 @@ optionLoggingLevel = Logging level (DEBUG, INFO, WARN, ERROR)
optionTemplate = Template optionTemplate = Template
optionTemplateFile = Template file optionTemplateFile = Template file
optionRepositoryId = Repository Id optionRepositoryId = Repository Id
optionRepositoryIdOrTypeAndName = Repository Id or type/name
optionRepositoryName = Repository name optionRepositoryName = Repository name
optionRepositoryType = Repository name optionRepositoryType = Repository name
optionRepositoryContact = Repository contact optionRepositoryContact = Repository contact
@@ -56,7 +57,7 @@ optionPermissionName = Group or user name
optionPermissionType = Permission type (READ,WRITE or OWNER) optionPermissionType = Permission type (READ,WRITE or OWNER)
optionUserName = Username optionUserName = Username
optionUserDisplayName = "Diesplay name optionUserDisplayName = Display name
optionUserMail = E-Mail address optionUserMail = E-Mail address
optionUserPassword = Password optionUserPassword = Password
optionUserType = Type optionUserType = Type

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import sonia.scm.i18n.I18nMessages;
//~--- JDK imports ------------------------------------------------------------
/**
* I18n messages which are send back to client.
*
* @author Sebastian Sdorra
* @since 1.37
*/
public final class ClientMessages
{
/**
* Constructs a new instance of ClientMessages. This constructor should not be
* used. Use the {@link #get(javax.servlet.http.HttpServletRequest)} method
* instead.
*/
public ClientMessages() {}
//~--- methods --------------------------------------------------------------
/**
* Returns the localized string for a failed authentication.
*
*
* @return localized string
*/
public String failedAuthentication()
{
return failedAuthentication;
}
/**
* Returns the localized string for "not enough privileges.
*
*
* @return localized string
*/
public String notEnoughPrivileges()
{
return notEnoughPrivileges;
}
//~--- get methods ----------------------------------------------------------
/**
* Returns an instance {@link ClientMessages}.
*
* @param request servlet request
*
* @return instance of client messages
*/
public static ClientMessages get(HttpServletRequest request)
{
return I18nMessages.get(ClientMessages.class, request);
}
//~--- fields ---------------------------------------------------------------
/** failed authentication */
private String failedAuthentication;
/** not enough privileges */
private String notEnoughPrivileges;
}

View File

@@ -295,6 +295,7 @@ public class ScmConfiguration
* *
* *
* @return realm description * @return realm description
* @since 1.36
*/ */
public String getRealmDescription() public String getRealmDescription()
{ {
@@ -577,6 +578,7 @@ public class ScmConfiguration
* *
* *
* @param realmDescription * @param realmDescription
* @since 1.36
*/ */
public void setRealmDescription(String realmDescription) public void setRealmDescription(String realmDescription)
{ {

View File

@@ -30,10 +30,12 @@
*/ */
package sonia.scm.i18n; package sonia.scm.i18n;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import sonia.scm.util.ClassLoaders;
import sonia.scm.util.Util; import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
@@ -41,6 +43,7 @@ import sonia.scm.util.Util;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Locale; import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle; import java.util.ResourceBundle;
/** /**
@@ -81,7 +84,7 @@ public class Bundle
*/ */
public static Bundle getBundle(String path) public static Bundle getBundle(String path)
{ {
return new Bundle(ResourceBundle.getBundle(path)); return getBundle(path, null, null);
} }
/** /**
@@ -95,7 +98,35 @@ public class Bundle
*/ */
public static Bundle getBundle(String path, Locale locale) public static Bundle getBundle(String path, Locale locale)
{ {
return new Bundle(ResourceBundle.getBundle(path, locale)); return getBundle(path, locale, null);
}
/**
* Creates a new bundle instance
*
*
* @param path path to the properties file
* @param locale locale for the properties file
* @param classLoader classLoader to load
*
* @return new bundle instance
*
* @since 1.37
*/
public static Bundle getBundle(String path, Locale locale,
ClassLoader classLoader)
{
if (locale == null)
{
locale = Locale.ENGLISH;
}
if (classLoader == null)
{
classLoader = ClassLoaders.getContextClassLoader(Bundle.class);
}
return new Bundle(ResourceBundle.getBundle(path, locale, classLoader));
} }
/** /**
@@ -134,8 +165,33 @@ public class Bundle
return msg; return msg;
} }
/**
* Returns the value of the key, formatted with {@link MessageFormat} or null
* if the key is not present in the bundle.
*
*
* @param key key in the properties file
* @param args format arguments
*
* @return formated message or null
*
* @since 1.37
*/
public String getStringIfPresent(String key, Object... args)
{
String msg = null;
try
{
msg = getString(key, args);
}
catch (MissingResourceException ex) {}
return msg;
}
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */ /** resource bundle */
private ResourceBundle bundle; private final ResourceBundle bundle;
} }

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.i18n;
//~--- JDK imports ------------------------------------------------------------
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The I18n annotation is used by the {@link I18nMessages} class to define the
* resource bundle key.
*
* @author Sebastian Sdorra
* @since 1.37
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface I18n
{
/**
* Returns the key for the resource bundle.
*
* @return resource bundle key
*/
String value();
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.i18n;
/**
*
* @author Sebastian Sdorra
*/
public class I18nException extends RuntimeException
{
/** Field description */
private static final long serialVersionUID = 1845326427312983227L;
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*/
public I18nException() {}
/**
* Constructs ...
*
*
* @param message
*/
public I18nException(String message)
{
super(message);
}
/**
* Constructs ...
*
*
* @param cause
*/
public I18nException(Throwable cause)
{
super(cause);
}
/**
* Constructs ...
*
*
* @param message
* @param cause
*/
public I18nException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@@ -0,0 +1,288 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.i18n;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Objects;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import sonia.scm.util.ClassLoaders;
//~--- JDK imports ------------------------------------------------------------
import java.lang.reflect.Field;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
/**
* The I18nMessages class instantiates a class and initializes all {@link String}
* fields with values from a resource bundle. The resource bundle must have the
* same name as the class. Each field which should be initialized from the
* bundle, must match a key in the resource bundle or is annotated with a
* {@link I18n} annotation which holds the key. I18nMessages injects also the
* locale and the bundle if it founds a field with the corresponding type.
*
* @author Sebastian Sdorra
* @since 1.37
*/
public final class I18nMessages
{
/** Field description */
private static final Cache<CacheKey, Object> cache =
CacheBuilder.newBuilder().build();
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*/
private I18nMessages() {}
//~--- get methods ----------------------------------------------------------
/**
* Same as {@link #get(java.lang.Class, java.util.Locale)}, with locale
* {@link Locale#ENGLISH}.
*
* @param msgClass message class
* @param <T> type of message class
*
* @return instance of message class
*/
public static <T> T get(Class<T> msgClass)
{
return get(msgClass, Locale.ENGLISH);
}
/**
* Same as {@link #get(java.lang.Class, java.util.Locale)}, with locale
* from servlet request ({@link HttpServletRequest#getLocale()}).
*
*
* @param msgClass message class
* @param request servlet request
* @param <T> type of message class
*
* @return
*/
public static <T> T get(Class<T> msgClass, HttpServletRequest request)
{
return get(msgClass, request.getLocale());
}
/**
* Returns a instance of the given message class with all message fields
* initialized.
*
*
* @param msgClass message class
* @param locale locale
* @param <T> type of the message class
*
* @return instance of message class
*/
public synchronized static <T> T get(Class<T> msgClass, Locale locale)
{
CacheKey ck = new CacheKey(locale, msgClass);
T instance = (T) cache.getIfPresent(ck);
if (instance == null)
{
instance = createInstance(msgClass, locale);
cache.put(ck, instance);
}
return instance;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param msgClass
* @param locale
* @param <T>
*
* @return
*/
private static <T> T createInstance(Class<T> msgClass, Locale locale)
{
Bundle bundle = Bundle.getBundle(msgClass.getName(), locale,
ClassLoaders.getContextClassLoader(msgClass));
T instance = null;
try
{
instance = msgClass.newInstance();
initializeInstance(bundle, locale, msgClass, instance);
}
catch (Exception ex)
{
throw new I18nException("could not instantiate/initialize class", ex);
}
return instance;
}
/**
* Method description
*
*
* @param bundle
* @param locale
* @param msgClass
* @param instance
*
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
private static void initializeInstance(Bundle bundle, Locale locale,
Class msgClass, Object instance)
throws IllegalArgumentException, IllegalAccessException
{
for (Field field : msgClass.getDeclaredFields())
{
if (field.getType().isAssignableFrom(String.class))
{
String key = field.getName();
I18n i18n = field.getAnnotation(I18n.class);
if (i18n != null)
{
key = i18n.value();
}
String value = bundle.getString(key);
if (value != null)
{
field.setAccessible(true);
field.set(instance, value);
}
}
else if (field.getType().isAssignableFrom(Bundle.class))
{
field.setAccessible(true);
field.set(instance, bundle);
}
else if (field.getType().isAssignableFrom(Locale.class))
{
field.setAccessible(true);
field.set(instance, locale);
}
}
}
//~--- inner classes --------------------------------------------------------
/**
* Class description
*
*
* @version Enter version here..., 14/03/15
* @author Enter your name here...
*/
private static class CacheKey
{
/**
* Constructs ...
*
*
* @param locale
* @param msgClass
*/
public CacheKey(Locale locale, Class msgClass)
{
this.locale = locale;
this.msgClass = msgClass;
}
//~--- methods ------------------------------------------------------------
/**
* Method description
*
*
* @param obj
*
* @return
*/
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final CacheKey other = (CacheKey) obj;
return Objects.equal(locale, other.locale)
&& Objects.equal(msgClass, other.msgClass);
}
/**
* Method description
*
*
* @return
*/
@Override
public int hashCode()
{
return Objects.hashCode(locale, msgClass);
}
//~--- fields -------------------------------------------------------------
/** Field description */
private final Locale locale;
/** Field description */
private final Class msgClass;
}
}

View File

@@ -30,8 +30,13 @@
*/ */
package sonia.scm.io; package sonia.scm.io;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.io.Closer;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.io.IOException; import java.io.IOException;
@@ -70,12 +75,15 @@ public final class DeepCopy
{ {
T obj = null; T obj = null;
Closer closer = Closer.create();
try try
{ {
// Write the object out to a byte array // Write the object out to a byte array
FastByteArrayOutputStream fbos = new FastByteArrayOutputStream(); FastByteArrayOutputStream fbos =
ObjectOutputStream out = new ObjectOutputStream(fbos); closer.register(new FastByteArrayOutputStream());
ObjectOutputStream out = closer.register(new ObjectOutputStream(fbos));
out.writeObject(orig); out.writeObject(orig);
out.flush(); out.flush();
@@ -83,7 +91,10 @@ public final class DeepCopy
// Retrieve an input stream from the byte array and read // Retrieve an input stream from the byte array and read
// a copy of the object back in. // a copy of the object back in.
ObjectInputStream in = new ObjectInputStream(fbos.getInputStream()); // use ScmObjectInputStream to solve ClassNotFoundException
// for plugin classes
ObjectInputStream in =
closer.register(new ScmObjectInputStream(fbos.getInputStream()));
obj = (T) in.readObject(); obj = (T) in.readObject();
} }
@@ -91,6 +102,10 @@ public final class DeepCopy
{ {
throw new IOException("could not copy object", ex); throw new IOException("could not copy object", ex);
} }
finally
{
closer.close();
}
return obj; return obj;
} }

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.io;
//~--- non-JDK imports --------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
/**
* {@link ObjectInputStream} implementation which uses the context class loader
* to resolve classes.
*
* @author Sebastian Sdorra
* @since 1.36
*/
public class ScmObjectInputStream extends ObjectInputStream
{
/**
* the logger for ScmObjectInputStream
*/
private static final Logger logger =
LoggerFactory.getLogger(ScmObjectInputStream.class);
//~--- constructors ---------------------------------------------------------
/**
* {@inheritDoc}
*/
public ScmObjectInputStream(InputStream stream) throws IOException
{
super(stream);
}
//~--- methods --------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
Class<?> clazz = null;
ClassLoader classLoader = getClassLoader();
try
{
clazz = classLoader.loadClass(desc.getName());
}
catch (ClassNotFoundException ex)
{
// do not log the exception, because the class
// is mostly found by the parent method.
}
if (clazz == null)
{
clazz = super.resolveClass(desc);
}
return clazz;
}
//~--- get methods ----------------------------------------------------------
/**
* Returns the context class loader if available. If the context class loader
* is not available the method will fall back to the class loader which has
* load this class.
*
*
* @return context class loader or default class loader
*/
private ClassLoader getClassLoader()
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null)
{
logger.debug("could not find context class loader, fall back to default");
classLoader = ScmObjectInputStream.class.getClassLoader();
}
return classLoader;
}
}

View File

@@ -35,6 +35,9 @@ package sonia.scm.search;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.TransformFilter; import sonia.scm.TransformFilter;
import sonia.scm.util.Util; import sonia.scm.util.Util;
@@ -53,6 +56,14 @@ import java.util.Locale;
public final class SearchUtil public final class SearchUtil
{ {
/**
* the logger for SearchUtil
*/
private static final Logger logger =
LoggerFactory.getLogger(SearchUtil.class);
//~--- constructors ---------------------------------------------------------
/** /**
* Constructs ... * Constructs ...
* *
@@ -180,6 +191,77 @@ public final class SearchUtil
return items; return items;
} }
/**
* Method description
*
*
* @param pattern
* @param c
*/
private static void appendChar(StringBuilder pattern, char c)
{
switch (c)
{
case '*' :
pattern.append(".*");
break;
case '?' :
pattern.append(".");
break;
case '(' :
pattern.append("\\(");
break;
case ')' :
pattern.append("\\)");
break;
case '{' :
pattern.append("\\{");
break;
case '}' :
pattern.append("\\}");
break;
case '[' :
pattern.append("\\[");
break;
case ']' :
pattern.append("\\]");
break;
case '|' :
pattern.append("\\|");
break;
case '.' :
pattern.append("\\.");
break;
case '\\' :
pattern.append("\\\\");
break;
default :
pattern.append(c);
}
}
/** /**
* Method description * Method description
* *
@@ -192,19 +274,26 @@ public final class SearchUtil
{ {
String query = request.getQuery().trim(); String query = request.getQuery().trim();
StringBuilder pattern = new StringBuilder();
if (request.isIgnoreCase()) if (request.isIgnoreCase())
{ {
pattern.append("(?i)");
query = query.toLowerCase(Locale.ENGLISH); query = query.toLowerCase(Locale.ENGLISH);
} }
query = query.replace("\\", "\\\\").replace("*", ".*").replace("?", "."); pattern.append(".*");
query = ".*".concat(query).concat(".*");
if (request.isIgnoreCase()) for (char c : query.toCharArray())
{ {
query = "(?i)".concat(query); appendChar(pattern, c);
} }
return query; pattern.append(".*");
logger.trace("converted query \"{}\" to regex pattern \"{}\"",
request.getQuery(), pattern);
return pattern.toString();
} }
} }

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.util;
/**
* Util methods for {@link ClassLoader}s.
*
* @author Sebastian Sdorra
* @since 1.37
*/
public final class ClassLoaders
{
/**
* Constructs ...
*
*/
private ClassLoaders() {}
//~--- get methods ----------------------------------------------------------
/**
* Returns the context {@link ClassLoader} from the current {@link Thread}, if
* the context {@link ClassLoader} is not available the {@link ClassLoader},
* which has load the given context class, is used.
*
*
* @param contextClass context class
*
* @return context class loader
*/
public static ClassLoader getContextClassLoader(Class<?> contextClass)
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null)
{
classLoader = contextClass.getClassLoader();
}
return classLoader;
}
}

View File

@@ -37,6 +37,7 @@ package sonia.scm.util;
import com.google.common.base.CharMatcher; import com.google.common.base.CharMatcher;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -46,11 +47,13 @@ import sonia.scm.config.ScmConfiguration;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -77,6 +80,9 @@ public final class HttpUtil
*/ */
public static final String HEADER_SCM_CLIENT = "X-SCM-Client"; public static final String HEADER_SCM_CLIENT = "X-SCM-Client";
/** Field description */
public static final String HEADER_USERAGENT = "User-Agent";
/** authentication header */ /** authentication header */
public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
@@ -158,6 +164,9 @@ public final class HttpUtil
public static final String STATUS_UNAUTHORIZED_MESSAGE = public static final String STATUS_UNAUTHORIZED_MESSAGE =
"Authorization Required"; "Authorization Required";
/** Field description */
private static final int SKIP_SIZE = 4096;
/** the logger for HttpUtil */ /** the logger for HttpUtil */
private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class); private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
@@ -197,9 +206,9 @@ public final class HttpUtil
*/ */
public static String append(String uri, String suffix) public static String append(String uri, String suffix)
{ {
if ( uri.endsWith(SEPARATOR_PATH) && suffix.startsWith(SEPARATOR_PATH) ) if (uri.endsWith(SEPARATOR_PATH) && suffix.startsWith(SEPARATOR_PATH))
{ {
uri = uri.substring( 0, uri.length() - 1 ); uri = uri.substring(0, uri.length() - 1);
} }
else if (!uri.endsWith(SEPARATOR_PATH) &&!suffix.startsWith(SEPARATOR_PATH)) else if (!uri.endsWith(SEPARATOR_PATH) &&!suffix.startsWith(SEPARATOR_PATH))
{ {
@@ -299,6 +308,38 @@ public final class HttpUtil
return value; return value;
} }
/**
* Skips to complete body of a request.
*
*
* @param request http request
*
* @since 1.37
*/
public static void drainBody(HttpServletRequest request)
{
if (isChunked(request) || (request.getContentLength() > 0))
{
InputStream in = null;
try
{
in = request.getInputStream();
while ((0 < in.skip(SKIP_SIZE)) || (0 <= in.read()))
{
// nothing
}
}
catch (IOException e) {}
finally
{
IOUtil.close(in);
}
}
}
/** /**
* Method description * Method description
* *
@@ -443,8 +484,11 @@ public final class HttpUtil
* @param realmDescription - realm description * @param realmDescription - realm description
* *
* @throws IOException * @throws IOException
*
* @since 1.36
*/ */
public static void sendUnauthorized(HttpServletResponse response, String realmDescription) public static void sendUnauthorized(HttpServletResponse response,
String realmDescription)
throws IOException throws IOException
{ {
sendUnauthorized(null, response, realmDescription); sendUnauthorized(null, response, realmDescription);
@@ -463,8 +507,7 @@ public final class HttpUtil
* @since 1.19 * @since 1.19
*/ */
public static void sendUnauthorized(HttpServletRequest request, public static void sendUnauthorized(HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response, String realmDescription)
String realmDescription)
throws IOException throws IOException
{ {
if ((request == null) ||!isWUIRequest(request)) if ((request == null) ||!isWUIRequest(request))
@@ -483,6 +526,26 @@ public final class HttpUtil
STATUS_UNAUTHORIZED_MESSAGE); STATUS_UNAUTHORIZED_MESSAGE);
} }
/**
* Returns true if the User-Agent header of the current request starts with
* the given string.
*
*
* @param request http request
* @param userAgent string to test against the header
*
* @return true if the header starts with the given string
*
* @since 1.37
*/
public static boolean userAgentStartsWith(HttpServletRequest request,
String userAgent)
{
return Strings.nullToEmpty(request.getHeader(HEADER_USERAGENT)).toLowerCase(
Locale.ENGLISH).startsWith(
Strings.nullToEmpty(userAgent).toLowerCase(Locale.ENGLISH));
}
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/** /**
@@ -669,6 +732,21 @@ public final class HttpUtil
return uri; return uri;
} }
/**
* Returns true if the body of the request is chunked.
*
*
* @param request http request
*
* @return true if the request is chunked
*
* @since 1.37
*/
public static boolean isChunked(HttpServletRequest request)
{
return "chunked".equals(request.getHeader("Transfer-Encoding"));
}
/** /**
* Returns true if the http request is send by the scm-manager web interface. * Returns true if the http request is send by the scm-manager web interface.
* *

View File

@@ -35,7 +35,6 @@ package sonia.scm.web.filter;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -83,6 +82,9 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
/** Field description */ /** Field description */
public static final String HEADER_AUTHORIZATION = "Authorization"; public static final String HEADER_AUTHORIZATION = "Authorization";
/** marker for failed authentication */
private static final String ATTRIBUTE_FAILED_AUTH = "sonia.scm.auth.failed";
/** the logger for BasicAuthenticationFilter */ /** the logger for BasicAuthenticationFilter */
private static final Logger logger = private static final Logger logger =
LoggerFactory.getLogger(BasicAuthenticationFilter.class); LoggerFactory.getLogger(BasicAuthenticationFilter.class);
@@ -182,9 +184,8 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
} }
/** /**
* Sends status code 401 back to client, if no authorization header was found, * Sends status code 403 back to client, if the authentication has failed.
* if a authorization is present and the authentication failed the method will * In all other cases the method will send status code 403 back to client.
* send status code 403.
* *
* @param request servlet request * @param request servlet request
* @param response servlet response * @param response servlet response
@@ -199,18 +200,53 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
HttpServletResponse response, FilterChain chain) HttpServletResponse response, FilterChain chain)
throws IOException, ServletException throws IOException, ServletException
{ {
String authentication = request.getHeader(HEADER_AUTHORIZATION);
if (Strings.isNullOrEmpty(authentication)) // send only forbidden, if the authentication has failed.
// see https://bitbucket.org/sdorra/scm-manager/issue/545/git-clone-with-username-in-url-does-not
if (Boolean.TRUE.equals(request.getAttribute(ATTRIBUTE_FAILED_AUTH)))
{ {
HttpUtil.sendUnauthorized(request, response, configuration.getRealmDescription()); sendFailedAuthenticationError(request, response);
} }
else else
{ {
response.sendError(HttpServletResponse.SC_FORBIDDEN); sendUnauthorizedError(request, response);
} }
} }
/**
* Sends an error for a failed authentication back to client.
*
*
* @param request http request
* @param response http response
*
* @throws IOException
*/
protected void sendFailedAuthenticationError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
HttpUtil.sendUnauthorized(request, response,
configuration.getRealmDescription());
}
/**
* Sends an unauthorized error back to client.
*
*
* @param request http request
* @param response http response
*
* @throws IOException
*/
protected void sendUnauthorizedError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
HttpUtil.sendUnauthorized(request, response,
configuration.getRealmDescription());
}
/** /**
* Method description * Method description
* *
@@ -254,6 +290,10 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
} }
catch (AuthenticationException ex) catch (AuthenticationException ex)
{ {
// add a marker to the request that the authentication has failed
request.setAttribute(ATTRIBUTE_FAILED_AUTH, Boolean.TRUE);
if (logger.isTraceEnabled()) if (logger.isTraceEnabled())
{ {
logger.trace("authentication failed for user ".concat(username), logger.trace("authentication failed for user ".concat(username),
@@ -280,6 +320,6 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */ /** scm main configuration */
private final ScmConfiguration configuration; protected ScmConfiguration configuration;
} }

View File

@@ -49,6 +49,7 @@ import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.PermissionType; import sonia.scm.repository.PermissionType;
import sonia.scm.repository.PermissionUtil; import sonia.scm.repository.PermissionUtil;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException; import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.HttpUtil; import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
@@ -63,7 +64,6 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import sonia.scm.security.Role;
/** /**
* Abstract http filter to check repository permissions. * Abstract http filter to check repository permissions.
@@ -162,7 +162,7 @@ public abstract class PermissionFilter extends HttpFilter
getUserName(subject)); getUserName(subject));
} }
sendAccessDenied(response, subject); sendAccessDenied(request, response, subject);
} }
} }
else else
@@ -199,11 +199,43 @@ public abstract class PermissionFilter extends HttpFilter
subject.getPrincipal()); subject.getPrincipal());
} }
sendAccessDenied(response, subject); sendAccessDenied(request, response, subject);
} }
} }
/**
* Sends a "not enough privileges" error back to client.
*
*
* @param request http request
* @param response http response
*
* @throws IOException
*/
protected void sendNotEnoughPrivilegesError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
/**
* Sends an unauthorized error back to client.
*
*
* @param request http request
* @param response http response
*
* @throws IOException
*/
protected void sendUnauthorizedError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
HttpUtil.sendUnauthorized(response, configuration.getRealmDescription());
}
/** /**
* Extracts the type of the repositroy from url. * Extracts the type of the repositroy from url.
* *
@@ -230,22 +262,23 @@ public abstract class PermissionFilter extends HttpFilter
/** /**
* Send access denied to the servlet response. * Send access denied to the servlet response.
* *
* * @param request current http request object
* @param response current http response object * @param response current http response object
* @param subject user subject * @param subject user subject
* *
* @throws IOException * @throws IOException
*/ */
private void sendAccessDenied(HttpServletResponse response, Subject subject) private void sendAccessDenied(HttpServletRequest request,
HttpServletResponse response, Subject subject)
throws IOException throws IOException
{ {
if (subject.hasRole(Role.USER)) if (subject.hasRole(Role.USER))
{ {
response.sendError(HttpServletResponse.SC_FORBIDDEN); sendNotEnoughPrivilegesError(request, response);
} }
else else
{ {
HttpUtil.sendUnauthorized(response, configuration.getRealmDescription()); sendUnauthorizedError(request, response);
} }
} }
@@ -334,5 +367,5 @@ public abstract class PermissionFilter extends HttpFilter
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** scm-manager global configuration */ /** scm-manager global configuration */
private ScmConfiguration configuration; private final ScmConfiguration configuration;
} }

View File

@@ -0,0 +1,31 @@
# Copyright (c) 2010, Sebastian Sdorra
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of SCM-Manager; nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# http://bitbucket.org/sdorra/scm-manager
#
failedAuthentication = Invalid username or password.
notEnoughPrivileges = You do not have enough access privileges for this operation.

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.i18n;
//~--- non-JDK imports --------------------------------------------------------
import org.junit.Test;
import sonia.scm.repository.Changeset;
import static org.junit.Assert.*;
//~--- JDK imports ------------------------------------------------------------
import java.util.Locale;
import java.util.MissingResourceException;
/**
*
* @author Sebastian Sdorra
*/
public class I18nMessagesTest
{
/**
* Method description
*
*/
@Test
public void testI18n()
{
TestMessages msg = I18nMessages.get(TestMessages.class);
assertEquals("Normal Key", msg.normalKey);
assertEquals("Key with Annotation", msg.keyWithAnnotation);
assertNull(msg.someObject);
assertNotNull(msg.bundle);
assertEquals(Locale.ENGLISH, msg.locale);
}
/**
* Method description
*
*/
@Test
public void testI18nOtherLanguage()
{
TestMessages msg = I18nMessages.get(TestMessages.class, Locale.GERMANY);
assertEquals("Normaler Schlüssel", msg.normalKey);
assertEquals("Schlüssel mit Annotation", msg.keyWithAnnotation);
assertNull(msg.someObject);
assertNotNull(msg.bundle);
assertEquals(Locale.GERMANY, msg.locale);
}
/**
* Method description
*
*/
@Test(expected = MissingResourceException.class)
public void testMissingBundle()
{
Changeset msg = I18nMessages.get(Changeset.class);
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.i18n;
//~--- JDK imports ------------------------------------------------------------
import java.util.Locale;
/**
*
* @author Sebastian Sdorra
*/
public class TestMessages
{
/** Field description */
public Bundle bundle;
/** Field description */
@I18n("key_with_annotation")
public String keyWithAnnotation;
/** Field description */
public Locale locale;
/** Field description */
public String normalKey;
/** Field description */
public Integer someObject;
}

View File

@@ -0,0 +1,328 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.io;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Objects;
import org.junit.Test;
import static org.junit.Assert.*;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.Serializable;
/**
*
* @author Sebastian Sdorra
*/
public class DeepCopyTest
{
/**
* Method description
*
*
* @throws IOException
*/
@Test
public void testDeepCopy() throws IOException
{
Person orig = new Person("Tricia", "McMillan",
new Address("Magrathea", "Mainstreet 3"));
Person copy = DeepCopy.copy(orig);
assertNotSame(orig, copy);
assertEquals(orig, copy);
}
/**
* Method description
*
*
* @throws IOException
*/
@Test(expected = IOException.class)
public void testDeepCopyNonSerializable() throws IOException
{
DeepCopy.copy(new NonSerializable());
}
//~--- inner classes --------------------------------------------------------
/**
* Class description
*
*
* @version Enter version here..., 14/03/08
* @author Enter your name here...
*/
private static class Address implements Serializable
{
/** Field description */
private static final long serialVersionUID = 3200816222378286310L;
//~--- constructors -------------------------------------------------------
/**
* Constructs ...
*
*/
public Address() {}
/**
* Constructs ...
*
*
* @param city
* @param street
*/
public Address(String city, String street)
{
this.city = city;
this.street = street;
}
//~--- methods ------------------------------------------------------------
/**
* Method description
*
*
* @param obj
*
* @return
*/
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final Address other = (Address) obj;
return Objects.equal(city, other.city)
&& Objects.equal(street, other.street);
}
/**
* Method description
*
*
* @return
*/
@Override
public int hashCode()
{
return Objects.hashCode(city, street);
}
//~--- get methods --------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public String getCity()
{
return city;
}
/**
* Method description
*
*
* @return
*/
public String getStreet()
{
return street;
}
//~--- fields -------------------------------------------------------------
/** Field description */
private String city;
/** Field description */
private String street;
}
/**
* Class description
*
*
* @version Enter version here..., 14/03/08
* @author Enter your name here...
*/
private static class NonSerializable {}
/**
* Class description
*
*
* @version Enter version here..., 14/03/08
* @author Enter your name here...
*/
private static class Person implements Serializable
{
/** Field description */
private static final long serialVersionUID = -2098386757802626539L;
//~--- constructors -------------------------------------------------------
/**
* Constructs ...
*
*/
public Person() {}
/**
* Constructs ...
*
*
* @param firstname
* @param lastname
* @param address
*/
public Person(String firstname, String lastname, Address address)
{
this.firstname = firstname;
this.lastname = lastname;
this.address = address;
}
//~--- methods ------------------------------------------------------------
/**
* Method description
*
*
* @param obj
*
* @return
*/
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final Person other = (Person) obj;
return Objects.equal(firstname, other.firstname)
&& Objects.equal(lastname, other.lastname)
&& Objects.equal(address, other.address);
}
/**
* Method description
*
*
* @return
*/
@Override
public int hashCode()
{
return Objects.hashCode(firstname, lastname, address);
}
//~--- get methods --------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Address getAddress()
{
return address;
}
/**
* Method description
*
*
* @return
*/
public String getFirstname()
{
return firstname;
}
/**
* Method description
*
*
* @return
*/
public String getLastname()
{
return lastname;
}
//~--- fields -------------------------------------------------------------
/** Field description */
private Address address;
/** Field description */
private String firstname;
/** Field description */
private String lastname;
}
}

View File

@@ -177,6 +177,12 @@ public class SearchUtilTest
"testhansolo")); "testhansolo"));
assertFalse(SearchUtil.matchesAll(new SearchRequest("test\\hansolo"), assertFalse(SearchUtil.matchesAll(new SearchRequest("test\\hansolo"),
"test\\hnsolo")); "test\\hnsolo"));
assertTrue(SearchUtil.matchesAll(new SearchRequest("{test,hansolo} tst"),
"{test,hansolo} tst"));
assertTrue(SearchUtil.matchesAll(new SearchRequest("(test,hansolo) tst"),
"(test,hansolo) tst"));
assertTrue(SearchUtil.matchesAll(new SearchRequest("[test,hansolo] tst"),
"[test,hansolo] tst"));
} }
/** /**

View File

@@ -184,6 +184,23 @@ public class HttpUtilTest
} }
/**
* Method description
*
*/
@Test
public void userAgentStartsWithTest()
{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader(HttpUtil.HEADER_USERAGENT)).thenReturn(
"git/1.7.10.5997.gaa4aa");
assertTrue(HttpUtil.userAgentStartsWith(request, "git/"));
assertTrue(HttpUtil.userAgentStartsWith(request, "GIT/"));
assertFalse(HttpUtil.userAgentStartsWith(request, "git/a"));
assertFalse(HttpUtil.userAgentStartsWith(request, "sobbo/"));
}
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/** /**

View File

@@ -0,0 +1,30 @@
# Copyright (c) 2010, Sebastian Sdorra
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of SCM-Manager; nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# http://bitbucket.org/sdorra/scm-manager
#
key_with_annotation = Key with Annotation
normalKey = Normal Key

View File

@@ -0,0 +1,30 @@
# Copyright (c) 2010, Sebastian Sdorra
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of SCM-Manager; nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# http://bitbucket.org/sdorra/scm-manager
#
key_with_annotation = Schl\u00fcssel mit Annotation
normalKey = Normaler Schl\u00fcssel

View File

@@ -409,7 +409,7 @@ public class JAXBConfigurationEntryStore<V>
{ {
//J- //J-
writer = new IndentXMLStreamWriter( writer = new IndentXMLStreamWriter(
XMLOutputFactory.newFactory().createXMLStreamWriter( XMLOutputFactory.newInstance().createXMLStreamWriter(
createWriter() createWriter()
) )
); );

View File

@@ -36,6 +36,7 @@ package sonia.scm.repository;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@@ -59,6 +60,7 @@ import org.eclipse.jgit.util.FS;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
@@ -66,9 +68,12 @@ import sonia.scm.util.Util;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -109,6 +114,9 @@ public final class GitUtil
/** Field description */ /** Field description */
private static final int TIMEOUT = 5; private static final int TIMEOUT = 5;
/** Field description */
private static final String USERAGENT_GIT = "git/";
/** the logger for GitUtil */ /** the logger for GitUtil */
private static final Logger logger = LoggerFactory.getLogger(GitUtil.class); private static final Logger logger = LoggerFactory.getLogger(GitUtil.class);
@@ -652,6 +660,19 @@ public final class GitUtil
return name; return name;
} }
/**
* Returns true if the request comes from a git client.
*
*
* @param request servlet request
*
* @return true if the client is git
*/
public static boolean isGitClient(HttpServletRequest request)
{
return HttpUtil.userAgentStartsWith(request, USERAGENT_GIT);
}
/** /**
* Method description * Method description
* *

View File

@@ -0,0 +1,106 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
import sonia.scm.ClientMessages;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.GitUtil;
import sonia.scm.web.filter.AutoLoginModule;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class GitBasicAuthenticationFilter extends BasicAuthenticationFilter
{
/**
* Constructs ...
*
*
* @param configuration
* @param autoLoginModules
*/
@Inject
public GitBasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
{
super(configuration, autoLoginModules);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
*/
@Override
protected void sendFailedAuthenticationError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
System.out.println(ClientMessages.get(request).failedAuthentication());
if (GitUtil.isGitClient(request))
{
GitSmartHttpTools.sendError(request, response,
HttpServletResponse.SC_FORBIDDEN,
ClientMessages.get(request).failedAuthentication());
}
else
{
super.sendFailedAuthenticationError(request, response);
}
}
}

View File

@@ -38,13 +38,20 @@ package sonia.scm.web;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
import sonia.scm.ClientMessages;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.GitUtil;
import sonia.scm.repository.RepositoryProvider; import sonia.scm.repository.RepositoryProvider;
import sonia.scm.web.filter.ProviderPermissionFilter; import sonia.scm.web.filter.ProviderPermissionFilter;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import sonia.scm.config.ScmConfiguration; import javax.servlet.http.HttpServletResponse;
/** /**
* *
@@ -71,19 +78,44 @@ public class GitPermissionFilter extends ProviderPermissionFilter
/** /**
* Constructs ... * Constructs ...
* *
* * @param configuration
*
* @param securityContextProvider
* @param repositoryProvider * @param repositoryProvider
*/ */
@Inject @Inject
public GitPermissionFilter( public GitPermissionFilter(ScmConfiguration configuration,
ScmConfiguration configuration, RepositoryProvider repositoryProvider)
RepositoryProvider repositoryProvider)
{ {
super(configuration, repositoryProvider); super(configuration, repositoryProvider);
} }
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
*/
@Override
protected void sendNotEnoughPrivilegesError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
if (GitUtil.isGitClient(request))
{
GitSmartHttpTools.sendError(request, response,
HttpServletResponse.SC_FORBIDDEN,
ClientMessages.get(request).notEnoughPrivileges());
}
else
{
super.sendNotEnoughPrivilegesError(request, response);
}
}
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/** /**
@@ -100,8 +132,8 @@ public class GitPermissionFilter extends ProviderPermissionFilter
String uri = request.getRequestURI(); String uri = request.getRequestURI();
return uri.endsWith(URI_RECEIVE_PACK) return uri.endsWith(URI_RECEIVE_PACK)
|| (uri.endsWith(URI_REF_INFO) || (uri.endsWith(URI_REF_INFO)
&& PARAMETER_VALUE_RECEIVE.equals( && PARAMETER_VALUE_RECEIVE.equals(
request.getParameter(PARAMETER_SERVICE))); request.getParameter(PARAMETER_SERVICE)));
} }
} }

View File

@@ -40,7 +40,6 @@ import com.google.inject.servlet.ServletModule;
import org.eclipse.jgit.transport.ScmTransportProtocol; import org.eclipse.jgit.transport.ScmTransportProtocol;
import sonia.scm.plugin.ext.Extension; import sonia.scm.plugin.ext.Extension;
import sonia.scm.web.filter.BasicAuthenticationFilter;
/** /**
* *
@@ -68,7 +67,7 @@ public class GitServletModule extends ServletModule
bind(ScmTransportProtocol.class); bind(ScmTransportProtocol.class);
// serlvelts and filters // serlvelts and filters
filter(PATTERN_GIT).through(BasicAuthenticationFilter.class); filter(PATTERN_GIT).through(GitBasicAuthenticationFilter.class);
filter(PATTERN_GIT).through(GitPermissionFilter.class); filter(PATTERN_GIT).through(GitPermissionFilter.class);
serve(PATTERN_GIT).with(ScmGitServlet.class); serve(PATTERN_GIT).with(ScmGitServlet.class);
} }

View File

@@ -37,8 +37,11 @@ package sonia.scm.repository;
import com.github.legman.Subscribe; import com.github.legman.Subscribe;
import com.google.common.base.Objects;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -152,7 +155,7 @@ public class HgHookManager
if (url == null) if (url == null)
{ {
HttpServletRequest request = httpServletRequestProvider.get(); HttpServletRequest request = getHttpServletRequest();
if (request != null) if (request != null)
{ {
@@ -160,10 +163,10 @@ public class HgHookManager
} }
else else
{ {
logger.warn(
"created hook url {} without request, in some cases this could cause problems",
hookUrl);
url = createConfiguredUrl(); url = createConfiguredUrl();
logger.warn(
"created url {} without request, in some cases this could cause problems",
url);
} }
} }
@@ -269,8 +272,14 @@ public class HgHookManager
*/ */
private String createConfiguredUrl() private String createConfiguredUrl()
{ {
//J-
return HttpUtil.getUriWithoutEndSeperator( return HttpUtil.getUriWithoutEndSeperator(
configuration.getBaseUrl()).concat("/hook/hg/"); Objects.firstNonNull(
configuration.getBaseUrl(),
"http://localhost:8080/scm"
)
).concat("/hook/hg/");
//J+
} }
/** /**
@@ -309,6 +318,32 @@ public class HgHookManager
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
private HttpServletRequest getHttpServletRequest()
{
HttpServletRequest request = null;
try
{
request = httpServletRequestProvider.get();
}
catch (ProvisionException ex)
{
logger.debug("http servlet request is not available");
}
catch (OutOfScopeException ex)
{
logger.debug("http servlet request is not available");
}
return request;
}
/** /**
* Method description * Method description
* *

View File

@@ -0,0 +1,99 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.web.filter.AutoLoginModule;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class HgBasicAuthenticationFilter extends BasicAuthenticationFilter
{
/**
* Constructs ...
*
*
* @param configuration
* @param autoLoginModules
*/
@Inject
public HgBasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
{
super(configuration, autoLoginModules);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
*/
@Override
protected void sendFailedAuthenticationError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
if (HgUtil.isHgClient(request))
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
else
{
super.sendFailedAuthenticationError(request, response);
}
}
}

View File

@@ -57,9 +57,6 @@ public class HgPermissionFilter extends ProviderPermissionFilter
/** /**
* Constructs ... * Constructs ...
* *
*
* @param securityContextProvider
*
* @param configuration * @param configuration
* @param repositoryProvider * @param repositoryProvider
*/ */

View File

@@ -75,7 +75,7 @@ public class HgServletModule extends ServletModule
serve(MAPPING_HOOK).with(HgHookCallbackServlet.class); serve(MAPPING_HOOK).with(HgHookCallbackServlet.class);
// register hg cgi servlet // register hg cgi servlet
filter(MAPPING_HG).through(BasicAuthenticationFilter.class); filter(MAPPING_HG).through(HgBasicAuthenticationFilter.class);
filter(MAPPING_HG).through(HgPermissionFilter.class); filter(MAPPING_HG).through(HgPermissionFilter.class);
serve(MAPPING_HG).with(HgCGIServlet.class); serve(MAPPING_HG).with(HgCGIServlet.class);
} }

View File

@@ -50,6 +50,7 @@ import sonia.scm.repository.HgHookManager;
import sonia.scm.repository.HgPythonScript; import sonia.scm.repository.HgPythonScript;
import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.HgRepositoryHandler;
import sonia.scm.repository.spi.javahg.HgFileviewExtension; import sonia.scm.repository.spi.javahg.HgFileviewExtension;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
@@ -58,6 +59,8 @@ import java.io.File;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import javax.servlet.http.HttpServletRequest;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -68,6 +71,9 @@ public final class HgUtil
/** Field description */ /** Field description */
public static final String REVISION_TIP = "tip"; public static final String REVISION_TIP = "tip";
/** Field description */
private static final String USERAGENT_HG = "mercurial/";
/** /**
* the logger for HgUtil * the logger for HgUtil
*/ */
@@ -180,4 +186,17 @@ public final class HgUtil
? REVISION_TIP ? REVISION_TIP
: revision; : revision;
} }
/**
* Returns true if the request comes from a mercurial client.
*
*
* @param request servlet request
*
* @return true if the client is mercurial
*/
public static boolean isHgClient(HttpServletRequest request)
{
return HttpUtil.userAgentStartsWith(request, USERAGENT_HG);
}
} }

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import org.tmatesoft.svn.core.SVNErrorCode;
/**
*
* @author Sebastian Sdorra
*/
public final class ScmSvnErrorCode extends SVNErrorCode
{
/** Field description */
private static final long serialVersionUID = -6864996390796610410L;
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param category
* @param index
* @param description
*/
protected ScmSvnErrorCode(int category, int index, String description)
{
super(category, index, description);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param description
*
* @return
*/
public static ScmSvnErrorCode authzNotEnoughPrivileges(String description)
{
return new ScmSvnErrorCode(AUTHZ_CATEGORY, 4, description);
}
}

View File

@@ -37,23 +37,36 @@ package sonia.scm.repository;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.io.Closeables;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath; import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.internal.io.dav.DAVElement;
import org.tmatesoft.svn.core.internal.server.dav.DAVXMLUtil;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNXMLUtil;
import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.admin.SVNChangeEntry; import org.tmatesoft.svn.core.wc.admin.SVNChangeEntry;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -61,6 +74,9 @@ import java.util.Map;
public final class SvnUtil public final class SvnUtil
{ {
/** Field description */
public static final String XML_CONTENT_TYPE = "text/xml; charset=\"utf-8\"";
/** Field description */ /** Field description */
private static final String ID_TRANSACTION_PREFIX = "-1:"; private static final String ID_TRANSACTION_PREFIX = "-1:";
@@ -70,6 +86,9 @@ public final class SvnUtil
*/ */
private static final char TYPE_UPDATED = 'U'; private static final char TYPE_UPDATED = 'U';
/** Field description */
private static final String USERAGENT_SVN = "svn/";
/** /**
* the logger for SvnUtil * the logger for SvnUtil
*/ */
@@ -232,6 +251,39 @@ public final class SvnUtil
return changesets; return changesets;
} }
/**
* Method description
*
* @param errorCode
*
* @return
*/
public static String createErrorBody(SVNErrorCode errorCode)
{
StringBuffer xmlBuffer = new StringBuffer();
SVNXMLUtil.addXMLHeader(xmlBuffer);
List<String> namespaces = Lists.newArrayList(DAVElement.DAV_NAMESPACE,
DAVElement.SVN_APACHE_PROPERTY_NAMESPACE);
SVNXMLUtil.openNamespaceDeclarationTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX,
DAVXMLUtil.SVN_DAV_ERROR_TAG, namespaces, SVNXMLUtil.PREFIX_MAP,
xmlBuffer);
SVNXMLUtil.openXMLTag(SVNXMLUtil.SVN_APACHE_PROPERTY_PREFIX,
"human-readable", SVNXMLUtil.XML_STYLE_NORMAL, "errcode",
String.valueOf(errorCode.getCode()), xmlBuffer);
xmlBuffer.append(
SVNEncodingUtil.xmlEncodeCDATA(errorCode.getDescription()));
SVNXMLUtil.closeXMLTag(SVNXMLUtil.SVN_APACHE_PROPERTY_PREFIX,
"human-readable", xmlBuffer);
SVNXMLUtil.closeXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX,
DAVXMLUtil.SVN_DAV_ERROR_TAG, xmlBuffer);
return xmlBuffer.toString();
}
/** /**
* Method description * Method description
* *
@@ -266,6 +318,39 @@ public final class SvnUtil
} }
} }
/**
* Method description
*
*
* @param request
* @param response
* @param statusCode
* @param errorCode
*
* @throws IOException
*/
public static void sendError(HttpServletRequest request,
HttpServletResponse response, int statusCode, SVNErrorCode errorCode)
throws IOException
{
HttpUtil.drainBody(request);
response.setStatus(statusCode);
response.setContentType(XML_CONTENT_TYPE);
PrintWriter writer = null;
try
{
writer = response.getWriter();
writer.println(createErrorBody(errorCode));
}
finally
{
Closeables.close(writer, true);
}
}
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/** /**
@@ -311,6 +396,19 @@ public final class SvnUtil
return id.substring(ID_TRANSACTION_PREFIX.length()); return id.substring(ID_TRANSACTION_PREFIX.length());
} }
/**
* Method description
*
*
* @param request
*
* @return
*/
public static boolean isSvnClient(HttpServletRequest request)
{
return HttpUtil.userAgentStartsWith(request, USERAGENT_SVN);
}
/** /**
* Method description * Method description
* *

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.SvnUtil;
import sonia.scm.util.HttpUtil;
import sonia.scm.web.filter.AutoLoginModule;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class SvnBasicAuthenticationFilter extends BasicAuthenticationFilter
{
/**
* Constructs ...
*
*
* @param configuration
* @param autoLoginModules
*/
@Inject
public SvnBasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
{
super(configuration, autoLoginModules);
}
//~--- methods --------------------------------------------------------------
/**
* Sends unauthorized instead of forbidden for svn clients, because the
* svn client prompts again for authentication.
*
*
* @param request http request
* @param response http response
*
* @throws IOException
*/
@Override
protected void sendFailedAuthenticationError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
if (SvnUtil.isSvnClient(request))
{
HttpUtil.sendUnauthorized(response, configuration.getRealmDescription());
}
else
{
super.sendFailedAuthenticationError(request, response);
}
}
}

View File

@@ -39,15 +39,21 @@ import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import sonia.scm.ClientMessages;
import sonia.scm.config.ScmConfiguration; import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.RepositoryProvider; import sonia.scm.repository.RepositoryProvider;
import sonia.scm.repository.ScmSvnErrorCode;
import sonia.scm.repository.SvnUtil;
import sonia.scm.web.filter.ProviderPermissionFilter; import sonia.scm.web.filter.ProviderPermissionFilter;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** /**
* *
@@ -58,22 +64,16 @@ public class SvnPermissionFilter extends ProviderPermissionFilter
{ {
/** Field description */ /** Field description */
private static Set<String> WRITEMETHOD_SET = ImmutableSet.of("MKACTIVITY", private static final Set<String> WRITEMETHOD_SET =
"PROPPATCH", "PUT", ImmutableSet.of("MKACTIVITY", "PROPPATCH", "PUT", "CHECKOUT", "MKCOL",
"CHECKOUT", "MKCOL", "MOVE", "MOVE", "COPY", "DELETE", "LOCK", "UNLOCK", "MERGE");
"COPY", "DELETE", "LOCK",
"UNLOCK", "MERGE");
//~--- constructors --------------------------------------------------------- //~--- constructors ---------------------------------------------------------
/** /**
* Constructs ... * Constructs ...
* *
*
*
*
* @param configuration * @param configuration
* @param securityContextProvider
* @param repository * @param repository
*/ */
@Inject @Inject
@@ -83,6 +83,41 @@ public class SvnPermissionFilter extends ProviderPermissionFilter
super(configuration, repository); super(configuration, repository);
} }
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
*/
@Override
protected void sendNotEnoughPrivilegesError(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
if (SvnUtil.isSvnClient(request))
{
//J-
SvnUtil.sendError(
request,
response,
HttpServletResponse.SC_FORBIDDEN,
ScmSvnErrorCode.authzNotEnoughPrivileges(
ClientMessages.get(request).notEnoughPrivileges()
)
);
//J+
}
else
{
super.sendNotEnoughPrivilegesError(request, response);
}
}
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/** /**

View File

@@ -69,7 +69,7 @@ public class SvnServletModule extends ServletModule
protected void configureServlets() protected void configureServlets()
{ {
filter(PATTERN_SVN).through(SvnGZipFilter.class); filter(PATTERN_SVN).through(SvnGZipFilter.class);
filter(PATTERN_SVN).through(BasicAuthenticationFilter.class); filter(PATTERN_SVN).through(SvnBasicAuthenticationFilter.class);
filter(PATTERN_SVN).through(SvnPermissionFilter.class); filter(PATTERN_SVN).through(SvnPermissionFilter.class);
Map<String, String> parameters = new HashMap<String, String>(); Map<String, String> parameters = new HashMap<String, String>();

View File

@@ -164,13 +164,7 @@
<dependency> <dependency>
<groupId>commons-beanutils</groupId> <groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId> <artifactId>commons-beanutils</artifactId>
<version>1.8.3</version> <version>1.9.1</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
@@ -179,6 +173,17 @@
<version>3.2.1</version> <version>3.2.1</version>
</dependency> </dependency>
<!--
fix installation of httpasswd-plugin
https://groups.google.com/d/topic/scmmanager/eN7UtG8TwW8/discussion
-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency> <dependency>
<groupId>asm</groupId> <groupId>asm</groupId>
<artifactId>asm</artifactId> <artifactId>asm</artifactId>
@@ -300,12 +305,6 @@
<artifactId>selenium-java</artifactId> <artifactId>selenium-java</artifactId>
<version>${selenium.version}</version> <version>${selenium.version}</version>
<scope>test</scope> <scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
@@ -327,12 +326,22 @@
<artifactId>jersey-apache-client</artifactId> <artifactId>jersey-apache-client</artifactId>
<version>${jersey.version}</version> <version>${jersey.version}</version>
<scope>test</scope> <scope>test</scope>
<exclusions> </dependency>
<exclusion>
<artifactId>commons-logging</artifactId> <!-- global excludes -->
<groupId>commons-logging</groupId>
</exclusion> <dependency>
</exclusions> <groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -503,20 +512,6 @@
<profiles> <profiles>
<profile>
<id>cluster</id>
<dependencies>
<dependency>
<groupId>sonia.scm</groupId>
<artifactId>scm-dao-orientdb</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</profile>
<profile> <profile>
<id>release</id> <id>release</id>

View File

@@ -35,42 +35,47 @@ package sonia.scm.plugin;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Throwables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.aether.artifact.Artifact; import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.graph.DependencyFilter; import org.sonatype.aether.graph.DependencyFilter;
import org.sonatype.aether.graph.DependencyNode; import org.sonatype.aether.graph.DependencyNode;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.util.HashSet; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Scanner;
import java.util.Set; import java.util.Set;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
public class AetherDependencyFilter implements DependencyFilter public abstract class AbstractDependencyFilter implements DependencyFilter
{ {
/** Field description */
public static final String EXCLUDE_LIST = "/config/dependencies.list";
//~--- constructors ---------------------------------------------------------
/** /**
* Constructs ... * the logger for AbstractDependencyFilter
*
*/ */
public AetherDependencyFilter() private static final Logger logger =
{ LoggerFactory.getLogger(AbstractDependencyFilter.class);
loadExcludes();
}
//~--- methods -------------------------------------------------------------- //~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*
* @throws IOException
*/
protected abstract Set<String> loadExcludeSet() throws IOException;
/** /**
* Method description * Method description
* *
@@ -91,63 +96,45 @@ public class AetherDependencyFilter implements DependencyFilter
if (artifact != null) if (artifact != null)
{ {
result = !exludeSet.contains(getId(artifact)); String id = getId(artifact);
result = !getExludeSet().contains(id);
if (!result && logger.isDebugEnabled())
{
logger.debug("exlcude dependency {} because it is blacklisted", id);
}
} }
} }
return result; return result;
} }
/**
* Method description
*
*/
private void loadExcludes()
{
Scanner scanner = null;
try
{
scanner = new Scanner(
AetherDependencyFilter.class.getResourceAsStream(EXCLUDE_LIST));
while (scanner.hasNextLine())
{
parseLine(scanner.nextLine());
}
}
finally
{
if (scanner != null)
{
scanner.close();
}
}
}
/**
* Method description
*
*
* @param line
*/
private void parseLine(String line)
{
line = line.trim();
if (Util.isNotEmpty(line))
{
String[] parts = line.split(":");
if (parts.length >= 2)
{
exludeSet.add(parts[0].concat(":").concat(parts[1]));
}
}
}
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
private Set<String> getExludeSet()
{
if (exludeSet == null)
{
try
{
exludeSet = loadExcludeSet();
}
catch (IOException ex)
{
throw Throwables.propagate(ex);
}
}
return exludeSet;
}
/** /**
* Method description * Method description
* *
@@ -164,5 +151,5 @@ public class AetherDependencyFilter implements DependencyFilter
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */ /** Field description */
private Set<String> exludeSet = new HashSet<String>(); private Set<String> exludeSet;
} }

View File

@@ -57,6 +57,7 @@ import org.sonatype.aether.resolution.DependencyRequest;
import org.sonatype.aether.resolution.DependencyResolutionException; import org.sonatype.aether.resolution.DependencyResolutionException;
import org.sonatype.aether.util.artifact.DefaultArtifact; import org.sonatype.aether.util.artifact.DefaultArtifact;
import org.sonatype.aether.util.artifact.JavaScopes; import org.sonatype.aether.util.artifact.JavaScopes;
import org.sonatype.aether.util.filter.AndDependencyFilter;
import org.sonatype.aether.util.filter.DependencyFilterUtils; import org.sonatype.aether.util.filter.DependencyFilterUtils;
import org.sonatype.aether.util.graph.transformer import org.sonatype.aether.util.graph.transformer
.ChainedDependencyGraphTransformer; .ChainedDependencyGraphTransformer;
@@ -77,7 +78,11 @@ public final class Aether
{ {
/** Field description */ /** Field description */
private static final DependencyFilter FILTER = new AetherDependencyFilter(); private static final DependencyFilter FILTER =
new AndDependencyFilter(
new CoreDependencyFilter(),
new BlacklistDependencyFilter()
);
/** /**
* the logger for Aether * the logger for Aether
@@ -167,7 +172,6 @@ public final class Aether
* *
* *
* @param system * @param system
* @param repositoryManager
* @param localRepository * @param localRepository
* @param configuration * @param configuration
* *

View File

@@ -30,6 +30,7 @@
*/ */
package sonia.scm.plugin; package sonia.scm.plugin;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
@@ -116,32 +117,17 @@ public class AetherDependencyResolver
* *
* *
* @param dependency * @param dependency
* @param dependencies
* *
* @throws DependencyCollectionException * @throws DependencyCollectionException
* @throws DependencyResolutionException * @throws DependencyResolutionException
*/ */
public void resolveLocalDependency(Dependency dependency) public void resolveDependencies(Dependency dependency,
List<Dependency> dependencies)
throws DependencyCollectionException, DependencyResolutionException throws DependencyCollectionException, DependencyResolutionException
{ {
CollectRequest request = new CollectRequest(); resolveDependency(new CollectRequest(dependency, dependencies,
remoteRepositories));
request.setRoot(dependency);
resolveDependency(request);
}
/**
* Method description
*
*
* @param dependency
*
* @throws DependencyCollectionException
* @throws DependencyResolutionException
*/
public void resolveRemoteDependency(Dependency dependency)
throws DependencyCollectionException, DependencyResolutionException
{
resolveDependency(new CollectRequest(dependency, remoteRepositories));
} }
/** /**

View File

@@ -199,12 +199,7 @@ public class AetherPluginHandler
new AetherDependencyResolver(configuration, repositorySystem, new AetherDependencyResolver(configuration, repositorySystem,
localRepository, remoteRepositories); localRepository, remoteRepositories);
resolver.resolveRemoteDependency(dependency); resolver.resolveDependencies(dependency, localDependencies);
for (Dependency localDependency : localDependencies)
{
resolver.resolveLocalDependency(localDependency);
}
if (classpath == null) if (classpath == null)
{ {

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.plugin;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
/**
*
* @author Sebastian Sdorra
*/
public class BlacklistDependencyFilter extends AbstractDependencyFilter
{
/** Field description */
private static final String BLACKLIST = "/config/blacklist.list";
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*
* @throws IOException
*/
@Override
protected Set<String> loadExcludeSet() throws IOException
{
return DependencyFilters.loadDependencySet(BLACKLIST);
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.plugin;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
/**
*
* @author Sebastian Sdorra
*/
public class CoreDependencyFilter extends AbstractDependencyFilter
{
/** Field description */
private static final String CORE_DEPENDENCIES = "/config/dependencies.list";
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*
* @throws IOException
*/
@Override
protected Set<String> loadExcludeSet() throws IOException
{
return DependencyFilters.loadDependencySet(CORE_DEPENDENCIES);
}
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.plugin;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.io.Resources;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Set;
/**
*
* @author Sebastian Sdorra
*/
public final class DependencyFilters
{
/**
* Method description
*
*
* @param path
*
* @return
*
* @throws IOException
*/
public static Set<String> loadDependencySet(String path) throws IOException
{
URL url = Resources.getResource(DependencyFilters.class, path);
if (url == null)
{
throw new IllegalArgumentException(
"could not find dependency set at ".concat(path));
}
Builder<String> builder = ImmutableSet.builder();
List<String> lines = Resources.readLines(url, Charsets.UTF_8);
for (String line : lines)
{
parseAndAppendLine(builder, line);
}
return builder.build();
}
/**
* Method description
*
*
* @param builder
* @param line
*/
private static void parseAndAppendLine(Builder<String> builder, String line)
{
line = line.trim();
if (!Strings.isNullOrEmpty(line))
{
String[] parts = line.split(":");
if (parts.length >= 2)
{
builder.add(parts[0].concat(":").concat(parts[1]));
}
}
}
}

View File

@@ -0,0 +1,10 @@
The following dependencies are blacklisted
commons-logging:commons-logging
log4j:log4j
junit:junit
org.mockito:mockito-core
org.mockito:mockito-all
org.mockito:mockito-junit
org.testng:testng
org.powermock:powermock

View File

@@ -64,9 +64,7 @@ Sonia.action.ChangePasswordWindow = Ext.extend(Ext.Window,{
name: 'old-password', name: 'old-password',
fieldLabel: this.oldPasswordText, fieldLabel: this.oldPasswordText,
inputType: 'password', inputType: 'password',
allowBlank: false, allowBlank: false
minLength: 6,
maxLength: 32
},{ },{
id: 'new-password', id: 'new-password',
name: 'new-password', name: 'new-password',

View File

@@ -75,7 +75,7 @@ Ext.apply(Ext.form.VTypes, {
// username validator // username validator
username: function(val){ username: function(val){
return name(val); return this.name(val);
}, },
usernameText: 'The username is invalid.' usernameText: 'The username is invalid.'