mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 19:15:52 +01:00
merge with branch issue-627
This commit is contained in:
270
scm-core/src/main/java/sonia/scm/web/UserAgent.java
Normal file
270
scm-core/src/main/java/sonia/scm/web/UserAgent.java
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* 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.common.base.Charsets;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
public final class UserAgent
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
* @param browser
|
||||
* @param basicAuthenticationCharset
|
||||
*/
|
||||
private UserAgent(String name, boolean browser,
|
||||
Charset basicAuthenticationCharset)
|
||||
{
|
||||
this.name = checkNotNull(name);
|
||||
this.browser = browser;
|
||||
this.basicAuthenticationCharset = checkNotNull(basicAuthenticationCharset);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Builder builder(String name)
|
||||
{
|
||||
return new Builder(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param obj
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final UserAgent other = (UserAgent) obj;
|
||||
|
||||
return Objects.equal(name, other.name)
|
||||
&& Objects.equal(browser, other.browser)
|
||||
&& Objects.equal(basicAuthenticationCharset, basicAuthenticationCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(name, browser, basicAuthenticationCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
//J-
|
||||
return Objects.toStringHelper(this)
|
||||
.add("name", name)
|
||||
.add("browser", browser)
|
||||
.add("basicAuthenticationCharset", basicAuthenticationCharset)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Charset getBasicAuthenticationCharset()
|
||||
{
|
||||
return basicAuthenticationCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isBrowser()
|
||||
{
|
||||
return browser;
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 14/10/15
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
public static class Builder
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public Builder(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param basicAuthenticationCharset
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Builder basicAuthenticationCharset(
|
||||
Charset basicAuthenticationCharset)
|
||||
{
|
||||
this.basicAuthenticationCharset =
|
||||
checkNotNull(basicAuthenticationCharset);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param browser
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Builder browser(boolean browser)
|
||||
{
|
||||
this.browser = browser;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public UserAgent build()
|
||||
{
|
||||
return new UserAgent(name, browser, basicAuthenticationCharset);
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final String name;
|
||||
|
||||
/** Field description */
|
||||
private boolean browser = true;
|
||||
|
||||
/** Field description */
|
||||
private Charset basicAuthenticationCharset = Charsets.ISO_8859_1;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final Charset basicAuthenticationCharset;
|
||||
|
||||
/** Field description */
|
||||
private final boolean browser;
|
||||
|
||||
/** Field description */
|
||||
private final String name;
|
||||
}
|
||||
156
scm-core/src/main/java/sonia/scm/web/UserAgentParser.java
Normal file
156
scm-core/src/main/java/sonia/scm/web/UserAgentParser.java
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 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.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
@Singleton
|
||||
public final class UserAgentParser
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final String CACHE_NAME = "sonia.scm.user-agent";
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final UserAgent UNKNOWN = UserAgent.builder("UNKNOWN").build();
|
||||
|
||||
/** Field description */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UserAgentParser.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param providers
|
||||
* @param cacheManager
|
||||
*/
|
||||
@Inject
|
||||
public UserAgentParser(Set<UserAgentProvider> providers,
|
||||
CacheManager cacheManager)
|
||||
{
|
||||
this.providers = providers;
|
||||
this.cache = cacheManager.getCache(String.class, UserAgent.class,
|
||||
CACHE_NAME);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public UserAgent parse(HttpServletRequest request)
|
||||
{
|
||||
return parse(request.getHeader(HttpUtil.HEADER_USERAGENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param userAgent
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public UserAgent parse(String userAgent)
|
||||
{
|
||||
String uas = Strings.nullToEmpty(userAgent).toLowerCase(Locale.ENGLISH);
|
||||
UserAgent ua = cache.get(uas);
|
||||
|
||||
if (ua == null)
|
||||
{
|
||||
for (UserAgentProvider provider : providers)
|
||||
{
|
||||
ua = provider.parseUserAgent(uas);
|
||||
|
||||
if (ua != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ua == null)
|
||||
{
|
||||
ua = UNKNOWN;
|
||||
}
|
||||
|
||||
// cache.put(uas, ua);
|
||||
}
|
||||
|
||||
logger.trace("return user-agent {} for {}", ua, userAgent);
|
||||
|
||||
return ua;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final Cache<String, UserAgent> cache;
|
||||
|
||||
/** Field description */
|
||||
private final Set<UserAgentProvider> providers;
|
||||
}
|
||||
56
scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java
Normal file
56
scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import sonia.scm.plugin.ExtensionPoint;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
@ExtensionPoint(multi = true)
|
||||
public interface UserAgentProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param userAgentString
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public UserAgent parseUserAgent(String userAgentString);
|
||||
}
|
||||
@@ -35,6 +35,7 @@ package sonia.scm.web.filter;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -51,6 +52,7 @@ import sonia.scm.SCMContext;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
import sonia.scm.web.UserAgentParser;
|
||||
import sonia.scm.util.Util;
|
||||
import sonia.scm.web.security.WebSecurityContext;
|
||||
|
||||
@@ -59,6 +61,9 @@ import sonia.scm.web.security.WebSecurityContext;
|
||||
import com.sun.jersey.core.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -66,6 +71,7 @@ import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import sonia.scm.web.UserAgent;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -87,6 +93,9 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
|
||||
/** marker for failed authentication */
|
||||
private static final String ATTRIBUTE_FAILED_AUTH = "sonia.scm.auth.failed";
|
||||
|
||||
/** default encoding to decode basic authentication header */
|
||||
public static final Charset DEFAULT_ENCODING = Charsets.ISO_8859_1;
|
||||
|
||||
/** the logger for BasicAuthenticationFilter */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(BasicAuthenticationFilter.class);
|
||||
@@ -94,11 +103,13 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
* Constructs a new basic authenticaton filter
|
||||
*
|
||||
*
|
||||
* @param securityContextProvider
|
||||
* @deprecated use the constructor with out arguments instead.
|
||||
* @deprecated use
|
||||
* {@link #BasicAuthenticationFilter(ScmConfiguration, Set, UserAgentParser)
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public BasicAuthenticationFilter(
|
||||
@@ -111,17 +122,68 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
|
||||
* @param autoLoginModules auto login modules
|
||||
*
|
||||
* @since 1.21
|
||||
*
|
||||
* @deprecated use
|
||||
* {@link #BasicAuthenticationFilter(ScmConfiguration, Set, UserAgentParser)
|
||||
* instead
|
||||
*/
|
||||
@Inject
|
||||
@Deprecated
|
||||
public BasicAuthenticationFilter(ScmConfiguration configuration,
|
||||
Set<AutoLoginModule> autoLoginModules)
|
||||
{
|
||||
this(configuration, autoLoginModules, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new basic authentication filter
|
||||
*
|
||||
* @param configuration scm-manager global configuration
|
||||
* @param autoLoginModules auto login modules
|
||||
* @param userAgentParser parser for user-agent header
|
||||
*
|
||||
* @since 1.42
|
||||
*/
|
||||
@Inject
|
||||
public BasicAuthenticationFilter(ScmConfiguration configuration,
|
||||
Set<AutoLoginModule> autoLoginModules, UserAgentParser userAgentParser)
|
||||
{
|
||||
super(autoLoginModules);
|
||||
this.configuration = configuration;
|
||||
this.userAgentParser = userAgentParser;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Decode base64 of the basic authentication header. The method will use
|
||||
* the charset provided by the {@link UserAgent}, if the
|
||||
* {@link UserAgentParser} is not available the method will be fall back to
|
||||
* ISO-8859-1.
|
||||
*
|
||||
* @param request http request
|
||||
* @param authentication base64 encoded basic authentication string
|
||||
*
|
||||
* @return decoded basic authentication header
|
||||
*
|
||||
* @see <a href="http://goo.gl/tZEBS3">issue 627</a>
|
||||
* @see <a href="http://goo.gl/NhbZ2F">Stackoverflow Basic Authentication</a>
|
||||
*
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
protected String decodeAuthenticationHeader(HttpServletRequest request,
|
||||
String authentication)
|
||||
throws UnsupportedEncodingException
|
||||
{
|
||||
Charset encoding = DEFAULT_ENCODING;
|
||||
|
||||
if (userAgentParser != null)
|
||||
{
|
||||
encoding = userAgentParser.parse(request).getBasicAuthenticationCharset();
|
||||
}
|
||||
|
||||
return new String(Base64.decode(authentication), encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -228,13 +290,15 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
|
||||
* @param authentication
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private User authenticate(HttpServletRequest request,
|
||||
HttpServletResponse response, Subject subject, String authentication)
|
||||
throws IOException
|
||||
{
|
||||
String token = authentication.substring(6);
|
||||
|
||||
token = new String(Base64.decode(token.getBytes()));
|
||||
String token = decodeAuthenticationHeader(request,
|
||||
authentication.substring(6));
|
||||
|
||||
int index = token.indexOf(CREDENTIAL_SEPARATOR);
|
||||
User user = null;
|
||||
@@ -365,4 +429,7 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
|
||||
|
||||
/** scm main configuration */
|
||||
protected ScmConfiguration configuration;
|
||||
|
||||
/** Field description */
|
||||
protected UserAgentParser userAgentParser;
|
||||
}
|
||||
|
||||
188
scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java
Normal file
188
scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* 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.common.base.Charsets;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@triology.de>
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class UserAgentParserTest
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_1 = "mozilla/5.0";
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_2 = "wget/1.5.3";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Before
|
||||
public void prepare()
|
||||
{
|
||||
Set<UserAgentProvider> providers = Sets.newHashSet(provider1, provider2);
|
||||
|
||||
when(cacheManager.getCache(String.class, UserAgent.class,
|
||||
UserAgentParser.CACHE_NAME)).thenReturn(cache);
|
||||
parser = new UserAgentParser(providers, cacheManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testDefaultValues()
|
||||
{
|
||||
UserAgent ua = parser.parse(UA_1);
|
||||
|
||||
assertEquals(Charsets.ISO_8859_1, ua.getBasicAuthenticationCharset());
|
||||
assertTrue(ua.isBrowser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParse()
|
||||
{
|
||||
UserAgent ua = UserAgent.builder("UA1").build();
|
||||
|
||||
when(provider1.parseUserAgent(UA_1)).thenReturn(ua);
|
||||
|
||||
UserAgent ua2 = UserAgent.builder("UA2").build();
|
||||
|
||||
when(provider2.parseUserAgent(UA_2)).thenReturn(ua2);
|
||||
|
||||
assertEquals(ua, parser.parse(UA_1));
|
||||
assertEquals(ua2, parser.parse(UA_2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParseHttpServletRequest()
|
||||
{
|
||||
when(request.getHeader(HttpUtil.HEADER_USERAGENT)).thenReturn(UA_2);
|
||||
|
||||
UserAgent ua = UserAgent.builder("UA2").build();
|
||||
|
||||
when(provider1.parseUserAgent(UA_2)).thenReturn(ua);
|
||||
assertEquals(ua, parser.parse(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParseNotFound()
|
||||
{
|
||||
assertEquals(UserAgentParser.UNKNOWN, parser.parse(UA_1));
|
||||
assertEquals(UserAgentParser.UNKNOWN, parser.parse(UA_2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParseWithCache()
|
||||
{
|
||||
UserAgent ua = UserAgent.builder("UA").build();
|
||||
|
||||
when(cache.get(UA_1)).thenReturn(ua);
|
||||
assertEquals(ua, parser.parse(UA_1));
|
||||
assertEquals(UserAgentParser.UNKNOWN, parser.parse(UA_2));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@Mock
|
||||
private Cache<String, UserAgent> cache;
|
||||
|
||||
/** Field description */
|
||||
@Mock
|
||||
private CacheManager cacheManager;
|
||||
|
||||
/** Field description */
|
||||
private UserAgentParser parser;
|
||||
|
||||
/** Field description */
|
||||
@Mock
|
||||
private UserAgentProvider provider1;
|
||||
|
||||
/** Field description */
|
||||
@Mock
|
||||
private UserAgentProvider provider2;
|
||||
|
||||
/** Field description */
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
}
|
||||
@@ -67,12 +67,13 @@ public class GitBasicAuthenticationFilter extends BasicAuthenticationFilter
|
||||
*
|
||||
* @param configuration
|
||||
* @param autoLoginModules
|
||||
* @param userAgentParser
|
||||
*/
|
||||
@Inject
|
||||
public GitBasicAuthenticationFilter(ScmConfiguration configuration,
|
||||
Set<AutoLoginModule> autoLoginModules)
|
||||
Set<AutoLoginModule> autoLoginModules, UserAgentParser userAgentParser)
|
||||
{
|
||||
super(configuration, autoLoginModules);
|
||||
super(configuration, autoLoginModules, userAgentParser);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 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.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import sonia.scm.plugin.ext.Extension;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
@Extension
|
||||
public class GitUserAgentProvider implements UserAgentProvider
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final UserAgent GIT = UserAgent.builder("Git").browser(
|
||||
false).basicAuthenticationCharset(
|
||||
Charsets.UTF_8).build();
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final UserAgent MSYSGIT = UserAgent.builder("msysGit").browser(
|
||||
false).basicAuthenticationCharset(
|
||||
Charsets.UTF_8).build();
|
||||
|
||||
/** Field description */
|
||||
private static final String PREFIX = "git/";
|
||||
|
||||
/** Field description */
|
||||
private static final String SUFFIX = "msysgit";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param userAgentString
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserAgent parseUserAgent(String userAgentString)
|
||||
{
|
||||
UserAgent ua = null;
|
||||
|
||||
if (userAgentString.startsWith(PREFIX))
|
||||
{
|
||||
if (userAgentString.contains(SUFFIX))
|
||||
{
|
||||
ua = MSYSGIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ua = GIT;
|
||||
}
|
||||
}
|
||||
|
||||
return ua;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.common.base.Strings;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@triology.de>
|
||||
*/
|
||||
public class GitUserAgentProviderTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParseUserAgent()
|
||||
{
|
||||
assertEquals(GitUserAgentProvider.GIT, parse("git/1.7.9.5"));
|
||||
assertEquals(GitUserAgentProvider.MSYSGIT, parse("git/1.8.3.msysgit.0"));
|
||||
assertNull(parse("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param v
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private UserAgent parse(String v)
|
||||
{
|
||||
return provider.parseUserAgent(
|
||||
Strings.nullToEmpty(v).toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final GitUserAgentProvider provider = new GitUserAgentProvider();
|
||||
}
|
||||
@@ -63,12 +63,13 @@ public class HgBasicAuthenticationFilter extends BasicAuthenticationFilter
|
||||
*
|
||||
* @param configuration
|
||||
* @param autoLoginModules
|
||||
* @param userAgentParser
|
||||
*/
|
||||
@Inject
|
||||
public HgBasicAuthenticationFilter(ScmConfiguration configuration,
|
||||
Set<AutoLoginModule> autoLoginModules)
|
||||
Set<AutoLoginModule> autoLoginModules, UserAgentParser userAgentParser)
|
||||
{
|
||||
super(configuration, autoLoginModules);
|
||||
super(configuration, autoLoginModules, userAgentParser);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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.common.annotations.VisibleForTesting;
|
||||
|
||||
import sonia.scm.plugin.ext.Extension;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
@Extension
|
||||
public class HgUserAgentProvider implements UserAgentProvider
|
||||
{
|
||||
|
||||
/** mercurial seems to use system encoding */
|
||||
@VisibleForTesting
|
||||
static UserAgent HG = UserAgent.builder("Mercurial").browser(
|
||||
false).basicAuthenticationCharset(
|
||||
Charset.defaultCharset()).build();
|
||||
|
||||
/** Field description */
|
||||
private static final String PREFIX = "mercurial";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param userAgentString
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserAgent parseUserAgent(String userAgentString)
|
||||
{
|
||||
UserAgent ua = null;
|
||||
|
||||
if (userAgentString.startsWith(PREFIX))
|
||||
{
|
||||
ua = HG;
|
||||
}
|
||||
|
||||
return ua;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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.common.base.Strings;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@triology.de>
|
||||
*/
|
||||
public class HgUserAgentProviderTest
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_1 = "mercurial/proto-1.0";
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_2 =
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParseUserAgent()
|
||||
{
|
||||
assertEquals(HgUserAgentProvider.HG, parse(UA_1));
|
||||
assertNull(parse(UA_2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param v
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private UserAgent parse(String v)
|
||||
{
|
||||
return provider.parseUserAgent(
|
||||
Strings.nullToEmpty(v).toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final HgUserAgentProvider provider = new HgUserAgentProvider();
|
||||
}
|
||||
@@ -65,12 +65,13 @@ public class SvnBasicAuthenticationFilter extends BasicAuthenticationFilter
|
||||
*
|
||||
* @param configuration
|
||||
* @param autoLoginModules
|
||||
* @param userAgentParser
|
||||
*/
|
||||
@Inject
|
||||
public SvnBasicAuthenticationFilter(ScmConfiguration configuration,
|
||||
Set<AutoLoginModule> autoLoginModules)
|
||||
Set<AutoLoginModule> autoLoginModules, UserAgentParser userAgentParser)
|
||||
{
|
||||
super(configuration, autoLoginModules);
|
||||
super(configuration, autoLoginModules, userAgentParser);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 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.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import sonia.scm.plugin.ext.Extension;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
@Extension
|
||||
public final class SvnUserAgentProvider implements UserAgentProvider
|
||||
{
|
||||
|
||||
/** ua prefix */
|
||||
private static final String PREFIX = "svn";
|
||||
|
||||
/** ua suffix */
|
||||
private static final String SUFFIX = "tortoisesvn";
|
||||
|
||||
/** TortoiseSVN */
|
||||
@VisibleForTesting
|
||||
static final UserAgent TORTOISE_SVN =
|
||||
UserAgent.builder("TortoiseSVN").browser(false)
|
||||
.basicAuthenticationCharset(Charsets.UTF_8).build();
|
||||
|
||||
/** Subversion cli client */
|
||||
@VisibleForTesting
|
||||
static final UserAgent SVN =
|
||||
UserAgent.builder("Subversion").browser(false)
|
||||
.basicAuthenticationCharset(Charsets.UTF_8).build();
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param userAgentString
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserAgent parseUserAgent(String userAgentString)
|
||||
{
|
||||
UserAgent ua = null;
|
||||
|
||||
if (userAgentString.startsWith(PREFIX))
|
||||
{
|
||||
if (userAgentString.contains(SUFFIX))
|
||||
{
|
||||
ua = TORTOISE_SVN;
|
||||
}
|
||||
else
|
||||
{
|
||||
ua = SVN;
|
||||
}
|
||||
}
|
||||
|
||||
return ua;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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 org.junit.Test;
|
||||
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@triology.de>
|
||||
*/
|
||||
public class SvnUserAgentProviderTest
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_1 =
|
||||
"SVN/1.8.8 (x64-microsoft-windows) serf/1.3.4 TortoiseSVN-1.8.6.25419";
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_2 = "SVN/1.5.4 (r33841) neon/0.28.3";
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_3 = "SVN/1.6.3 (r38063) neon/0.28.4";
|
||||
|
||||
/** Field description */
|
||||
private static final String UA_4 =
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0;Google Wireless Transcoder;)";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParseUserAgent()
|
||||
{
|
||||
assertEquals(SvnUserAgentProvider.TORTOISE_SVN, parse(UA_1));
|
||||
assertEquals(SvnUserAgentProvider.SVN, parse(UA_2));
|
||||
assertEquals(SvnUserAgentProvider.SVN, parse(UA_3));
|
||||
assertNull(parse(UA_4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param ua
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private UserAgent parse(String ua)
|
||||
{
|
||||
return suap.parseUserAgent(ua.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final SvnUserAgentProvider suap = new SvnUserAgentProvider();
|
||||
}
|
||||
@@ -157,6 +157,7 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import sonia.scm.web.UserAgentParser;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -343,6 +344,9 @@ public class ScmServletModule extends ServletModule
|
||||
bind(HookContextFactory.class);
|
||||
bind(HookEventFacade.class);
|
||||
|
||||
// bind user-agent parser
|
||||
bind(UserAgentParser.class);
|
||||
|
||||
// bind debug logging filter
|
||||
if ("true".equalsIgnoreCase(System.getProperty(SYSTEM_PROPERTY_DEBUG_HTTP)))
|
||||
{
|
||||
|
||||
@@ -80,6 +80,7 @@ import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import javax.xml.bind.JAXB;
|
||||
import sonia.scm.web.BrowserUserAgentProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -586,6 +587,12 @@ public class DefaultPluginLoader implements PluginLoader
|
||||
DefaultAuthenticationHandler.class
|
||||
)
|
||||
);
|
||||
extensions.add(
|
||||
new AnnotatedClass<Extension>(
|
||||
Extensions.createExtension(),
|
||||
BrowserUserAgentProvider.class
|
||||
)
|
||||
);
|
||||
//J+
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 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.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import sonia.scm.plugin.ext.Extension;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
*/
|
||||
@Extension
|
||||
public class BrowserUserAgentProvider implements UserAgentProvider
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final UserAgent CHROME = UserAgent.builder(
|
||||
"Chrome").basicAuthenticationCharset(
|
||||
Charsets.UTF_8).build();
|
||||
|
||||
/** Field description */
|
||||
private static final String CHROME_PATTERN = "chrome";
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final UserAgent FIREFOX = UserAgent.builder("Firefox").build();
|
||||
|
||||
/** Field description */
|
||||
private static final String FIREFOX_PATTERN = "firefox";
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final UserAgent MSIE = UserAgent.builder("Internet Explorer").build();
|
||||
|
||||
/** Field description */
|
||||
private static final String MSIE_PATTERN = "msie";
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting // todo check charset
|
||||
static final UserAgent SAFARI = UserAgent.builder("Safari").build();
|
||||
|
||||
/** Field description */
|
||||
private static final String OPERA_PATTERN = "opera";
|
||||
|
||||
/** Field description */
|
||||
private static final String SAFARI_PATTERN = "safari";
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting // todo check charset
|
||||
static final UserAgent OPERA = UserAgent.builder(
|
||||
"Opera").basicAuthenticationCharset(
|
||||
Charsets.UTF_8).build();
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param userAgentString
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserAgent parseUserAgent(String userAgentString)
|
||||
{
|
||||
UserAgent ua = null;
|
||||
|
||||
if (userAgentString.contains(CHROME_PATTERN))
|
||||
{
|
||||
ua = CHROME;
|
||||
}
|
||||
else if (userAgentString.contains(FIREFOX_PATTERN))
|
||||
{
|
||||
ua = FIREFOX;
|
||||
}
|
||||
else if (userAgentString.contains(OPERA_PATTERN))
|
||||
{
|
||||
ua = OPERA;
|
||||
}
|
||||
else if (userAgentString.contains(MSIE_PATTERN))
|
||||
{
|
||||
ua = MSIE;
|
||||
}
|
||||
else if (userAgentString.contains(SAFARI_PATTERN))
|
||||
{
|
||||
ua = SAFARI;
|
||||
}
|
||||
|
||||
return ua;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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.common.base.Strings;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@triology.de>
|
||||
*/
|
||||
public class BrowserUserAgentProviderTest
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String CHROME =
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36";
|
||||
|
||||
/** Field description */
|
||||
private static final String FIREFOX =
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.2; en-GB; rv:1.8.1.18) Gecko/20081029 Firefox/2.0.0.18";
|
||||
|
||||
/** Field description */
|
||||
private static final String MSIE =
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; )";
|
||||
|
||||
/** Field description */
|
||||
private static final String OPERA =
|
||||
"Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.00";
|
||||
|
||||
/** Field description */
|
||||
private static final String SAFARI =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/6.1.3 Safari/537.75.14";
|
||||
|
||||
/** Field description */
|
||||
private static final String WGET = "Wget/1.5.3";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testParseUserAgent()
|
||||
{
|
||||
assertEquals(BrowserUserAgentProvider.MSIE, parse(MSIE));
|
||||
assertEquals(BrowserUserAgentProvider.FIREFOX, parse(FIREFOX));
|
||||
assertEquals(BrowserUserAgentProvider.OPERA, parse(OPERA));
|
||||
assertEquals(BrowserUserAgentProvider.CHROME, parse(CHROME));
|
||||
assertEquals(BrowserUserAgentProvider.SAFARI, parse(SAFARI));
|
||||
assertNull(parse(WGET));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param v
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private UserAgent parse(String v)
|
||||
{
|
||||
return provider.parseUserAgent(
|
||||
Strings.nullToEmpty(v).toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final BrowserUserAgentProvider provider =
|
||||
new BrowserUserAgentProvider();
|
||||
}
|
||||
Reference in New Issue
Block a user