diff --git a/pom.xml b/pom.xml index a4d1254af4..bd853442bc 100644 --- a/pom.xml +++ b/pom.xml @@ -169,6 +169,14 @@ 1.0 + + + compile + + check + + + @@ -438,7 +446,7 @@ 1.1.1 2.5 3.0 - 1.18 + 1.18.1 2.3.20 7.6.14.v20131031 @@ -446,11 +454,11 @@ 1.2.0 - 1.2.2 + 1.2.3 - 3.2.0.201312181205-r - 1.8.3-scm1 + 3.3.0.201403021825-r + 1.8.4-scm2 16.0 diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 8fd5d37daf..65b9e1273e 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -41,7 +41,7 @@ args4j args4j - 2.0.25 + 2.0.26 diff --git a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/GetRepositorySubCommand.java b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/GetRepositorySubCommand.java index c7c497bacf..2e8227fa60 100644 --- a/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/GetRepositorySubCommand.java +++ b/scm-clients/scm-cli-client/src/main/java/sonia/scm/cli/cmd/GetRepositorySubCommand.java @@ -98,7 +98,22 @@ public class GetRepositorySubCommand extends TemplateSubCommand protected void run() { 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) { @@ -117,7 +132,7 @@ public class GetRepositorySubCommand extends TemplateSubCommand /** Field description */ @Argument( - usage = "optionRepositoryId", + usage = "optionRepositoryIdOrTypeAndName", metaVar = "repositoryid", required = true ) diff --git a/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties b/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties index e80757638b..d5c707f090 100644 --- a/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties +++ b/scm-clients/scm-cli-client/src/main/resources/sonia/resources/i18n.properties @@ -44,6 +44,7 @@ optionLoggingLevel = Logging level (DEBUG, INFO, WARN, ERROR) optionTemplate = Template optionTemplateFile = Template file optionRepositoryId = Repository Id +optionRepositoryIdOrTypeAndName = Repository Id or type/name optionRepositoryName = Repository name optionRepositoryType = Repository name optionRepositoryContact = Repository contact @@ -56,7 +57,7 @@ optionPermissionName = Group or user name optionPermissionType = Permission type (READ,WRITE or OWNER) optionUserName = Username -optionUserDisplayName = "Diesplay name +optionUserDisplayName = Display name optionUserMail = E-Mail address optionUserPassword = Password optionUserType = Type diff --git a/scm-core/src/main/java/sonia/scm/ClientMessages.java b/scm-core/src/main/java/sonia/scm/ClientMessages.java new file mode 100644 index 0000000000..d03bb34fad --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/ClientMessages.java @@ -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; +} diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index 338424d824..6163cd7ff2 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -295,6 +295,7 @@ public class ScmConfiguration * * * @return realm description + * @since 1.36 */ public String getRealmDescription() { @@ -577,6 +578,7 @@ public class ScmConfiguration * * * @param realmDescription + * @since 1.36 */ public void setRealmDescription(String realmDescription) { diff --git a/scm-core/src/main/java/sonia/scm/i18n/Bundle.java b/scm-core/src/main/java/sonia/scm/i18n/Bundle.java index be349fddb7..515ced9ef8 100644 --- a/scm-core/src/main/java/sonia/scm/i18n/Bundle.java +++ b/scm-core/src/main/java/sonia/scm/i18n/Bundle.java @@ -30,10 +30,12 @@ */ + package sonia.scm.i18n; //~--- non-JDK imports -------------------------------------------------------- +import sonia.scm.util.ClassLoaders; import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ @@ -41,6 +43,7 @@ import sonia.scm.util.Util; import java.text.MessageFormat; import java.util.Locale; +import java.util.MissingResourceException; import java.util.ResourceBundle; /** @@ -81,7 +84,7 @@ public class Bundle */ 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) { - 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; } + /** + * 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 --------------------------------------------------------------- - /** Field description */ - private ResourceBundle bundle; + /** resource bundle */ + private final ResourceBundle bundle; } diff --git a/scm-core/src/main/java/sonia/scm/i18n/I18n.java b/scm-core/src/main/java/sonia/scm/i18n/I18n.java new file mode 100644 index 0000000000..63d761fb17 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/i18n/I18n.java @@ -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(); +} diff --git a/scm-core/src/main/java/sonia/scm/i18n/I18nException.java b/scm-core/src/main/java/sonia/scm/i18n/I18nException.java new file mode 100644 index 0000000000..dc7d062de7 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/i18n/I18nException.java @@ -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); + } +} diff --git a/scm-core/src/main/java/sonia/scm/i18n/I18nMessages.java b/scm-core/src/main/java/sonia/scm/i18n/I18nMessages.java new file mode 100644 index 0000000000..0c11066f82 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/i18n/I18nMessages.java @@ -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 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 type of message class + * + * @return instance of message class + */ + public static T get(Class 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 type of message class + * + * @return + */ + public static T get(Class 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 type of the message class + * + * @return instance of message class + */ + public synchronized static T get(Class 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 + * + * @return + */ + private static T createInstance(Class 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; + } +} diff --git a/scm-core/src/main/java/sonia/scm/io/DeepCopy.java b/scm-core/src/main/java/sonia/scm/io/DeepCopy.java index a7d5a31bbe..3a5a1675d0 100644 --- a/scm-core/src/main/java/sonia/scm/io/DeepCopy.java +++ b/scm-core/src/main/java/sonia/scm/io/DeepCopy.java @@ -30,8 +30,13 @@ */ + package sonia.scm.io; +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.io.Closer; + //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; @@ -70,12 +75,15 @@ public final class DeepCopy { T obj = null; + Closer closer = Closer.create(); + try { // Write the object out to a byte array - FastByteArrayOutputStream fbos = new FastByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(fbos); + FastByteArrayOutputStream fbos = + closer.register(new FastByteArrayOutputStream()); + ObjectOutputStream out = closer.register(new ObjectOutputStream(fbos)); out.writeObject(orig); out.flush(); @@ -83,7 +91,10 @@ public final class DeepCopy // Retrieve an input stream from the byte array and read // 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(); } @@ -91,6 +102,10 @@ public final class DeepCopy { throw new IOException("could not copy object", ex); } + finally + { + closer.close(); + } return obj; } diff --git a/scm-core/src/main/java/sonia/scm/io/ScmObjectInputStream.java b/scm-core/src/main/java/sonia/scm/io/ScmObjectInputStream.java new file mode 100644 index 0000000000..a8ec919adf --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/io/ScmObjectInputStream.java @@ -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; + } +} diff --git a/scm-core/src/main/java/sonia/scm/search/SearchUtil.java b/scm-core/src/main/java/sonia/scm/search/SearchUtil.java index 81ff258b7a..90c65e3456 100644 --- a/scm-core/src/main/java/sonia/scm/search/SearchUtil.java +++ b/scm-core/src/main/java/sonia/scm/search/SearchUtil.java @@ -35,6 +35,9 @@ package sonia.scm.search; //~--- non-JDK imports -------------------------------------------------------- +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import sonia.scm.TransformFilter; import sonia.scm.util.Util; @@ -53,6 +56,14 @@ import java.util.Locale; public final class SearchUtil { + /** + * the logger for SearchUtil + */ + private static final Logger logger = + LoggerFactory.getLogger(SearchUtil.class); + + //~--- constructors --------------------------------------------------------- + /** * Constructs ... * @@ -180,6 +191,77 @@ public final class SearchUtil 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 * @@ -192,19 +274,26 @@ public final class SearchUtil { String query = request.getQuery().trim(); + StringBuilder pattern = new StringBuilder(); + if (request.isIgnoreCase()) { + pattern.append("(?i)"); query = query.toLowerCase(Locale.ENGLISH); } - query = query.replace("\\", "\\\\").replace("*", ".*").replace("?", "."); - query = ".*".concat(query).concat(".*"); + pattern.append(".*"); - 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(); } } diff --git a/scm-core/src/main/java/sonia/scm/util/ClassLoaders.java b/scm-core/src/main/java/sonia/scm/util/ClassLoaders.java new file mode 100644 index 0000000000..80998ef693 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/ClassLoaders.java @@ -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; + } +} diff --git a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java index 69f913d74d..244ac98719 100644 --- a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -37,6 +37,7 @@ package sonia.scm.util; import com.google.common.base.CharMatcher; import com.google.common.base.Strings; +import com.google.common.io.ByteStreams; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,11 +47,13 @@ import sonia.scm.config.ScmConfiguration; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -77,6 +80,9 @@ public final class HttpUtil */ public static final String HEADER_SCM_CLIENT = "X-SCM-Client"; + /** Field description */ + public static final String HEADER_USERAGENT = "User-Agent"; + /** authentication header */ public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; @@ -158,6 +164,9 @@ public final class HttpUtil public static final String STATUS_UNAUTHORIZED_MESSAGE = "Authorization Required"; + /** Field description */ + private static final int SKIP_SIZE = 4096; + /** the logger for HttpUtil */ private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class); @@ -167,7 +176,7 @@ public final class HttpUtil */ private static final Pattern PATTERN_URLNORMALIZE = Pattern.compile("(?:(http://[^:]+):80(/.+)?|(https://[^:]+):443(/.+)?)"); - + /** * CharMatcher to select cr/lf and '%' characters * @since 1.28 @@ -197,10 +206,10 @@ public final class HttpUtil */ 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)) { uri = uri.concat(SEPARATOR_PATH); @@ -299,6 +308,38 @@ public final class HttpUtil 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 * @@ -443,8 +484,11 @@ public final class HttpUtil * @param realmDescription - realm description * * @throws IOException + * + * @since 1.36 */ - public static void sendUnauthorized(HttpServletResponse response, String realmDescription) + public static void sendUnauthorized(HttpServletResponse response, + String realmDescription) throws IOException { sendUnauthorized(null, response, realmDescription); @@ -463,8 +507,7 @@ public final class HttpUtil * @since 1.19 */ public static void sendUnauthorized(HttpServletRequest request, - HttpServletResponse response, - String realmDescription) + HttpServletResponse response, String realmDescription) throws IOException { if ((request == null) ||!isWUIRequest(request)) @@ -483,6 +526,26 @@ public final class HttpUtil 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 ---------------------------------------------------------- /** @@ -669,6 +732,21 @@ public final class HttpUtil 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. * diff --git a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java index 5992fd4cf7..f88e25a961 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java @@ -35,7 +35,6 @@ package sonia.scm.web.filter; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.base.Strings; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -83,6 +82,9 @@ public class BasicAuthenticationFilter extends AutoLoginFilter /** Field description */ 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 */ private static final Logger logger = 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, - * if a authorization is present and the authentication failed the method will - * send status code 403. + * Sends status code 403 back to client, if the authentication has failed. + * In all other cases the method will send status code 403 back to client. * * @param request servlet request * @param response servlet response @@ -199,18 +200,53 @@ public class BasicAuthenticationFilter extends AutoLoginFilter HttpServletResponse response, FilterChain chain) 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 { - 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 * @@ -254,6 +290,10 @@ public class BasicAuthenticationFilter extends AutoLoginFilter } catch (AuthenticationException ex) { + + // add a marker to the request that the authentication has failed + request.setAttribute(ATTRIBUTE_FAILED_AUTH, Boolean.TRUE); + if (logger.isTraceEnabled()) { logger.trace("authentication failed for user ".concat(username), @@ -280,6 +320,6 @@ public class BasicAuthenticationFilter extends AutoLoginFilter //~--- fields --------------------------------------------------------------- - /** Field description */ - private final ScmConfiguration configuration; + /** scm main configuration */ + protected ScmConfiguration configuration; } diff --git a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java index 9767f1d43e..8900342bda 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java @@ -49,6 +49,7 @@ import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.PermissionType; import sonia.scm.repository.PermissionUtil; import sonia.scm.repository.Repository; +import sonia.scm.security.Role; import sonia.scm.security.ScmSecurityException; import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; @@ -63,7 +64,6 @@ import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import sonia.scm.security.Role; /** * Abstract http filter to check repository permissions. @@ -116,11 +116,11 @@ public abstract class PermissionFilter extends HttpFilter //~--- methods -------------------------------------------------------------- /** - * Checks the permission for the requested repository. If the user has enough + * Checks the permission for the requested repository. If the user has enough * permission, then the filter chain is called. * * - * @param request http request + * @param request http request * @param response http response * @param chain filter chain * @@ -162,7 +162,7 @@ public abstract class PermissionFilter extends HttpFilter getUserName(subject)); } - sendAccessDenied(response, subject); + sendAccessDenied(request, response, subject); } } else @@ -199,11 +199,43 @@ public abstract class PermissionFilter extends HttpFilter 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. * @@ -230,22 +262,23 @@ public abstract class PermissionFilter extends HttpFilter /** * Send access denied to the servlet response. * - * + * @param request current http request object * @param response current http response object * @param subject user subject * * @throws IOException */ - private void sendAccessDenied(HttpServletResponse response, Subject subject) + private void sendAccessDenied(HttpServletRequest request, + HttpServletResponse response, Subject subject) throws IOException { if (subject.hasRole(Role.USER)) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); + sendNotEnoughPrivilegesError(request, response); } else { - HttpUtil.sendUnauthorized(response, configuration.getRealmDescription()); + sendUnauthorizedError(request, response); } } @@ -334,5 +367,5 @@ public abstract class PermissionFilter extends HttpFilter //~--- fields --------------------------------------------------------------- /** scm-manager global configuration */ - private ScmConfiguration configuration; + private final ScmConfiguration configuration; } diff --git a/scm-core/src/main/resources/sonia/scm/ClientMessages.properties b/scm-core/src/main/resources/sonia/scm/ClientMessages.properties new file mode 100644 index 0000000000..9141761056 --- /dev/null +++ b/scm-core/src/main/resources/sonia/scm/ClientMessages.properties @@ -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. \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/i18n/I18nMessagesTest.java b/scm-core/src/test/java/sonia/scm/i18n/I18nMessagesTest.java new file mode 100644 index 0000000000..0760c4efb6 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/i18n/I18nMessagesTest.java @@ -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); + } +} diff --git a/scm-core/src/test/java/sonia/scm/i18n/TestMessages.java b/scm-core/src/test/java/sonia/scm/i18n/TestMessages.java new file mode 100644 index 0000000000..e1901eace9 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/i18n/TestMessages.java @@ -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; +} diff --git a/scm-core/src/test/java/sonia/scm/io/DeepCopyTest.java b/scm-core/src/test/java/sonia/scm/io/DeepCopyTest.java new file mode 100644 index 0000000000..f21c304d58 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/io/DeepCopyTest.java @@ -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; + } +} diff --git a/scm-core/src/test/java/sonia/scm/search/SearchUtilTest.java b/scm-core/src/test/java/sonia/scm/search/SearchUtilTest.java index 8aebfa9dbd..0588729a45 100644 --- a/scm-core/src/test/java/sonia/scm/search/SearchUtilTest.java +++ b/scm-core/src/test/java/sonia/scm/search/SearchUtilTest.java @@ -177,6 +177,12 @@ public class SearchUtilTest "testhansolo")); assertFalse(SearchUtil.matchesAll(new SearchRequest("test\\hansolo"), "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")); } /** diff --git a/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java b/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java index 4f88616662..585bc0e005 100644 --- a/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java +++ b/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java @@ -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 ---------------------------------------------------------- /** diff --git a/scm-core/src/test/resources/sonia/scm/i18n/TestMessages.properties b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages.properties new file mode 100644 index 0000000000..06a80bcae0 --- /dev/null +++ b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages.properties @@ -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 \ No newline at end of file diff --git a/scm-core/src/test/resources/sonia/scm/i18n/TestMessages_de_DE.properties b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages_de_DE.properties new file mode 100644 index 0000000000..c370bc25d8 --- /dev/null +++ b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages_de_DE.properties @@ -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 \ No newline at end of file diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java index 3fafd26e6d..fea2c7220a 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java @@ -409,7 +409,7 @@ public class JAXBConfigurationEntryStore { //J- writer = new IndentXMLStreamWriter( - XMLOutputFactory.newFactory().createXMLStreamWriter( + XMLOutputFactory.newInstance().createXMLStreamWriter( createWriter() ) ); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index 6dd4996b47..8bd530d5e9 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -36,6 +36,7 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; @@ -59,6 +60,7 @@ import org.eclipse.jgit.util.FS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ @@ -66,9 +68,12 @@ import sonia.scm.util.Util; import java.io.File; import java.io.IOException; +import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; + /** * * @author Sebastian Sdorra @@ -109,6 +114,9 @@ public final class GitUtil /** Field description */ private static final int TIMEOUT = 5; + /** Field description */ + private static final String USERAGENT_GIT = "git/"; + /** the logger for GitUtil */ private static final Logger logger = LoggerFactory.getLogger(GitUtil.class); @@ -652,6 +660,19 @@ public final class GitUtil 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 * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java new file mode 100644 index 0000000000..5f71cf4e48 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java @@ -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 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); + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java index c277c0cf7f..f682c355a6 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java @@ -38,13 +38,20 @@ package sonia.scm.web; 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.repository.RepositoryProvider; import sonia.scm.web.filter.ProviderPermissionFilter; //~--- JDK imports ------------------------------------------------------------ +import java.io.IOException; + 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 ... * - * - * - * @param securityContextProvider + * @param configuration * @param repositoryProvider */ @Inject - public GitPermissionFilter( - ScmConfiguration configuration, - RepositoryProvider repositoryProvider) + public GitPermissionFilter(ScmConfiguration configuration, + RepositoryProvider 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 ---------------------------------------------------------- /** @@ -100,8 +132,8 @@ public class GitPermissionFilter extends ProviderPermissionFilter String uri = request.getRequestURI(); return uri.endsWith(URI_RECEIVE_PACK) - || (uri.endsWith(URI_REF_INFO) - && PARAMETER_VALUE_RECEIVE.equals( - request.getParameter(PARAMETER_SERVICE))); + || (uri.endsWith(URI_REF_INFO) + && PARAMETER_VALUE_RECEIVE.equals( + request.getParameter(PARAMETER_SERVICE))); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java index fd7300817b..fdb1ae52d9 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java @@ -40,7 +40,6 @@ import com.google.inject.servlet.ServletModule; import org.eclipse.jgit.transport.ScmTransportProtocol; import sonia.scm.plugin.ext.Extension; -import sonia.scm.web.filter.BasicAuthenticationFilter; /** * @@ -68,7 +67,7 @@ public class GitServletModule extends ServletModule bind(ScmTransportProtocol.class); // serlvelts and filters - filter(PATTERN_GIT).through(BasicAuthenticationFilter.class); + filter(PATTERN_GIT).through(GitBasicAuthenticationFilter.class); filter(PATTERN_GIT).through(GitPermissionFilter.class); serve(PATTERN_GIT).with(ScmGitServlet.class); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java index 304e5b4d32..5f3ab09734 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java @@ -37,8 +37,11 @@ package sonia.scm.repository; import com.github.legman.Subscribe; +import com.google.common.base.Objects; import com.google.inject.Inject; +import com.google.inject.OutOfScopeException; import com.google.inject.Provider; +import com.google.inject.ProvisionException; import com.google.inject.Singleton; import org.slf4j.Logger; @@ -152,7 +155,7 @@ public class HgHookManager if (url == null) { - HttpServletRequest request = httpServletRequestProvider.get(); + HttpServletRequest request = getHttpServletRequest(); if (request != null) { @@ -160,10 +163,10 @@ public class HgHookManager } else { - logger.warn( - "created hook url {} without request, in some cases this could cause problems", - hookUrl); 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() { + //J- 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 ---------------------------------------------------------- + /** + * 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 * diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgBasicAuthenticationFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgBasicAuthenticationFilter.java new file mode 100644 index 0000000000..afbef65557 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgBasicAuthenticationFilter.java @@ -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 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); + } + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java index 73d20530ea..0a9d3e43b9 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java @@ -57,9 +57,6 @@ public class HgPermissionFilter extends ProviderPermissionFilter /** * Constructs ... * - * - * @param securityContextProvider - * * @param configuration * @param repositoryProvider */ diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java index 792bafe80b..5bf080860a 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java @@ -75,7 +75,7 @@ public class HgServletModule extends ServletModule serve(MAPPING_HOOK).with(HgHookCallbackServlet.class); // register hg cgi servlet - filter(MAPPING_HG).through(BasicAuthenticationFilter.class); + filter(MAPPING_HG).through(HgBasicAuthenticationFilter.class); filter(MAPPING_HG).through(HgPermissionFilter.class); serve(MAPPING_HG).with(HgCGIServlet.class); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java index 0d7150205f..ec35762de7 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUtil.java @@ -50,6 +50,7 @@ import sonia.scm.repository.HgHookManager; import sonia.scm.repository.HgPythonScript; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.spi.javahg.HgFileviewExtension; +import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ @@ -58,6 +59,8 @@ import java.io.File; import java.nio.charset.Charset; +import javax.servlet.http.HttpServletRequest; + /** * * @author Sebastian Sdorra @@ -68,6 +71,9 @@ public final class HgUtil /** Field description */ public static final String REVISION_TIP = "tip"; + /** Field description */ + private static final String USERAGENT_HG = "mercurial/"; + /** * the logger for HgUtil */ @@ -180,4 +186,17 @@ public final class HgUtil ? REVISION_TIP : 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); + } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/ScmSvnErrorCode.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/ScmSvnErrorCode.java new file mode 100644 index 0000000000..27d8523517 --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/ScmSvnErrorCode.java @@ -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); + } +} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java index abe5689712..6cc5eb5da9 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java @@ -37,23 +37,36 @@ package sonia.scm.repository; import com.google.common.base.Strings; import com.google.common.collect.Lists; +import com.google.common.io.Closeables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNLogEntry; 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.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.admin.SVNChangeEntry; +import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ +import java.io.IOException; +import java.io.PrintWriter; + import java.util.List; import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + /** * * @author Sebastian Sdorra @@ -61,6 +74,9 @@ import java.util.Map; public final class SvnUtil { + /** Field description */ + public static final String XML_CONTENT_TYPE = "text/xml; charset=\"utf-8\""; + /** Field description */ private static final String ID_TRANSACTION_PREFIX = "-1:"; @@ -70,6 +86,9 @@ public final class SvnUtil */ private static final char TYPE_UPDATED = 'U'; + /** Field description */ + private static final String USERAGENT_SVN = "svn/"; + /** * the logger for SvnUtil */ @@ -232,6 +251,39 @@ public final class SvnUtil return changesets; } + /** + * Method description + * + * @param errorCode + * + * @return + */ + public static String createErrorBody(SVNErrorCode errorCode) + { + StringBuffer xmlBuffer = new StringBuffer(); + + SVNXMLUtil.addXMLHeader(xmlBuffer); + + List 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 * @@ -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 ---------------------------------------------------------- /** @@ -311,6 +396,19 @@ public final class SvnUtil 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 * diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnBasicAuthenticationFilter.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnBasicAuthenticationFilter.java new file mode 100644 index 0000000000..b94b6b5d56 --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnBasicAuthenticationFilter.java @@ -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 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); + } + } +} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java index 164ccfd173..bcaf854118 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java @@ -39,15 +39,21 @@ import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; import com.google.inject.Singleton; +import sonia.scm.ClientMessages; import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.RepositoryProvider; +import sonia.scm.repository.ScmSvnErrorCode; +import sonia.scm.repository.SvnUtil; import sonia.scm.web.filter.ProviderPermissionFilter; //~--- JDK imports ------------------------------------------------------------ +import java.io.IOException; + import java.util.Set; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * @@ -58,22 +64,16 @@ public class SvnPermissionFilter extends ProviderPermissionFilter { /** Field description */ - private static Set WRITEMETHOD_SET = ImmutableSet.of("MKACTIVITY", - "PROPPATCH", "PUT", - "CHECKOUT", "MKCOL", "MOVE", - "COPY", "DELETE", "LOCK", - "UNLOCK", "MERGE"); + private static final Set WRITEMETHOD_SET = + ImmutableSet.of("MKACTIVITY", "PROPPATCH", "PUT", "CHECKOUT", "MKCOL", + "MOVE", "COPY", "DELETE", "LOCK", "UNLOCK", "MERGE"); //~--- constructors --------------------------------------------------------- /** * Constructs ... * - * - * - * * @param configuration - * @param securityContextProvider * @param repository */ @Inject @@ -83,6 +83,41 @@ public class SvnPermissionFilter extends ProviderPermissionFilter 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 ---------------------------------------------------------- /** diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java index 689af8851b..9eda6fe7dd 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnServletModule.java @@ -69,7 +69,7 @@ public class SvnServletModule extends ServletModule protected void configureServlets() { filter(PATTERN_SVN).through(SvnGZipFilter.class); - filter(PATTERN_SVN).through(BasicAuthenticationFilter.class); + filter(PATTERN_SVN).through(SvnBasicAuthenticationFilter.class); filter(PATTERN_SVN).through(SvnPermissionFilter.class); Map parameters = new HashMap(); diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 29c5b00da1..29a836a0eb 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -164,13 +164,7 @@ commons-beanutils commons-beanutils - 1.8.3 - - - commons-logging - commons-logging - - + 1.9.1 @@ -179,6 +173,17 @@ 3.2.1 + + + + commons-codec + commons-codec + 1.9 + + asm asm @@ -300,12 +305,6 @@ selenium-java ${selenium.version} test - - - commons-logging - commons-logging - - @@ -327,12 +326,22 @@ jersey-apache-client ${jersey.version} test - - - commons-logging - commons-logging - - + + + + + + commons-logging + commons-logging + 1.1.3 + provided + + + + log4j + log4j + 1.2.17 + provided @@ -502,20 +511,6 @@ - - - cluster - - - - - sonia.scm - scm-dao-orientdb - 2.0.0-SNAPSHOT - - - - release diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java b/scm-webapp/src/main/java/sonia/scm/plugin/AbstractDependencyFilter.java similarity index 73% rename from scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java rename to scm-webapp/src/main/java/sonia/scm/plugin/AbstractDependencyFilter.java index 1d89ddec7a..d448358b64 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AbstractDependencyFilter.java @@ -35,42 +35,47 @@ package sonia.scm.plugin; //~--- 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.graph.DependencyFilter; import org.sonatype.aether.graph.DependencyNode; -import sonia.scm.util.Util; - //~--- JDK imports ------------------------------------------------------------ -import java.util.HashSet; +import java.io.IOException; + import java.util.List; -import java.util.Scanner; import java.util.Set; /** * * @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() - { - loadExcludes(); - } + private static final Logger logger = + LoggerFactory.getLogger(AbstractDependencyFilter.class); //~--- methods -------------------------------------------------------------- + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + protected abstract Set loadExcludeSet() throws IOException; + /** * Method description * @@ -91,63 +96,45 @@ public class AetherDependencyFilter implements DependencyFilter 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; } - /** - * 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 ---------------------------------------------------------- + /** + * Method description + * + * + * @return + */ + private Set getExludeSet() + { + if (exludeSet == null) + { + try + { + exludeSet = loadExcludeSet(); + } + catch (IOException ex) + { + throw Throwables.propagate(ex); + } + } + + return exludeSet; + } + /** * Method description * @@ -164,5 +151,5 @@ public class AetherDependencyFilter implements DependencyFilter //~--- fields --------------------------------------------------------------- /** Field description */ - private Set exludeSet = new HashSet(); + private Set exludeSet; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/Aether.java b/scm-webapp/src/main/java/sonia/scm/plugin/Aether.java index 80f99cd918..6a27d5758f 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/Aether.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/Aether.java @@ -57,6 +57,7 @@ import org.sonatype.aether.resolution.DependencyRequest; import org.sonatype.aether.resolution.DependencyResolutionException; import org.sonatype.aether.util.artifact.DefaultArtifact; 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.graph.transformer .ChainedDependencyGraphTransformer; @@ -77,7 +78,11 @@ public final class Aether { /** Field description */ - private static final DependencyFilter FILTER = new AetherDependencyFilter(); + private static final DependencyFilter FILTER = + new AndDependencyFilter( + new CoreDependencyFilter(), + new BlacklistDependencyFilter() + ); /** * the logger for Aether @@ -167,7 +172,6 @@ public final class Aether * * * @param system - * @param repositoryManager * @param localRepository * @param configuration * diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java index 1e42d84637..67bf4e8a40 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherDependencyResolver.java @@ -30,6 +30,7 @@ */ + package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- @@ -116,32 +117,17 @@ public class AetherDependencyResolver * * * @param dependency + * @param dependencies * * @throws DependencyCollectionException * @throws DependencyResolutionException */ - public void resolveLocalDependency(Dependency dependency) + public void resolveDependencies(Dependency dependency, + List dependencies) throws DependencyCollectionException, DependencyResolutionException { - CollectRequest request = new CollectRequest(); - - 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)); + resolveDependency(new CollectRequest(dependency, dependencies, + remoteRepositories)); } /** diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java b/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java index f35a6b4976..7de5397a23 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/AetherPluginHandler.java @@ -199,12 +199,7 @@ public class AetherPluginHandler new AetherDependencyResolver(configuration, repositorySystem, localRepository, remoteRepositories); - resolver.resolveRemoteDependency(dependency); - - for (Dependency localDependency : localDependencies) - { - resolver.resolveLocalDependency(localDependency); - } + resolver.resolveDependencies(dependency, localDependencies); if (classpath == null) { diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/BlacklistDependencyFilter.java b/scm-webapp/src/main/java/sonia/scm/plugin/BlacklistDependencyFilter.java new file mode 100644 index 0000000000..b01da86a32 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/BlacklistDependencyFilter.java @@ -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 loadExcludeSet() throws IOException + { + return DependencyFilters.loadDependencySet(BLACKLIST); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/CoreDependencyFilter.java b/scm-webapp/src/main/java/sonia/scm/plugin/CoreDependencyFilter.java new file mode 100644 index 0000000000..528cc19ef5 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/CoreDependencyFilter.java @@ -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 loadExcludeSet() throws IOException + { + return DependencyFilters.loadDependencySet(CORE_DEPENDENCIES); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DependencyFilters.java b/scm-webapp/src/main/java/sonia/scm/plugin/DependencyFilters.java new file mode 100644 index 0000000000..a2088f07b2 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DependencyFilters.java @@ -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 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 builder = ImmutableSet.builder(); + List 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 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])); + } + } + } +} diff --git a/scm-webapp/src/main/resources/config/blacklist.list b/scm-webapp/src/main/resources/config/blacklist.list new file mode 100644 index 0000000000..e6fb1b0be7 --- /dev/null +++ b/scm-webapp/src/main/resources/config/blacklist.list @@ -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 \ No newline at end of file diff --git a/scm-webapp/src/main/webapp/resources/js/action/sonia.action.changepasswordwindow.js b/scm-webapp/src/main/webapp/resources/js/action/sonia.action.changepasswordwindow.js index 35ba82adb2..b1c0a5372f 100644 --- a/scm-webapp/src/main/webapp/resources/js/action/sonia.action.changepasswordwindow.js +++ b/scm-webapp/src/main/webapp/resources/js/action/sonia.action.changepasswordwindow.js @@ -64,9 +64,7 @@ Sonia.action.ChangePasswordWindow = Ext.extend(Ext.Window,{ name: 'old-password', fieldLabel: this.oldPasswordText, inputType: 'password', - allowBlank: false, - minLength: 6, - maxLength: 32 + allowBlank: false },{ id: 'new-password', name: 'new-password', diff --git a/scm-webapp/src/main/webapp/resources/js/override/ext.form.vtypes.js b/scm-webapp/src/main/webapp/resources/js/override/ext.form.vtypes.js index df17eeaee7..91d5307577 100644 --- a/scm-webapp/src/main/webapp/resources/js/override/ext.form.vtypes.js +++ b/scm-webapp/src/main/webapp/resources/js/override/ext.form.vtypes.js @@ -75,7 +75,7 @@ Ext.apply(Ext.form.VTypes, { // username validator username: function(val){ - return name(val); + return this.name(val); }, usernameText: 'The username is invalid.'