Merge pull request #1402 from scm-manager/bugfix/rest-download

user agents are not longer set to be browsers by default
This commit is contained in:
René Pfeuffer
2020-11-03 11:01:58 +01:00
committed by GitHub
11 changed files with 92 additions and 60 deletions

View File

@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Internal server error for git sub modules without tree object ([#1397](https://github.com/scm-manager/scm-manager/pull/1397))
- Do not expose subversion commit with id 0 ([#1395](https://github.com/scm-manager/scm-manager/pull/1395))
- Disable cloning repositories via ssh for anonymous users ([#1403](https://github.com/scm-manager/scm-manager/pull/1403))
- Support anonymous file download through rest api for non-browser clients (e.g. curl or postman) when anonymous mode is set to protocol-only ([#1402](https://github.com/scm-manager/scm-manager/pull/1402))
- SVN diff with property changes ([#1400](https://github.com/scm-manager/scm-manager/pull/1400))
- Branches link in repository overview ([#1404](https://github.com/scm-manager/scm-manager/pull/1404))

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
@@ -37,7 +37,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
//~--- JDK imports ------------------------------------------------------------
/**
* The software agent that is acting on behalf of a user. The user agent
* The software agent that is acting on behalf of a user. The user agent
* represents a browser or one of the repository client (svn, git or hg).
*
* @author Sebastian Sdorra <s.sdorra@gmail.com>
@@ -49,17 +49,16 @@ public final class UserAgent
/**
* Constructs a new user agent
*
*
* @param name
* @param browser
* @param name
* @param basicAuthenticationCharset
* @param browser
*/
private UserAgent(String name, boolean browser,
Charset basicAuthenticationCharset)
private UserAgent(String name, Charset basicAuthenticationCharset, boolean browser, boolean scmClient)
{
this.name = checkNotNull(name);
this.browser = browser;
this.basicAuthenticationCharset = checkNotNull(basicAuthenticationCharset);
this.browser = browser;
this.scmClient = scmClient;
}
//~--- methods --------------------------------------------------------------
@@ -71,8 +70,30 @@ public final class UserAgent
* @param name name of the UserAgent
*
* @return builder for UserAgent
*
* @deprecated Use {@link #browser(String)}, {@link #scmClient(String)} or {@link #other(String)} instead
*/
@Deprecated
public static Builder builder(String name)
{
return other(name);
}
public static Builder browser(String name)
{
final Builder builder = new Builder(name);
builder.browser = true;
return builder;
}
public static Builder scmClient(String name)
{
final Builder builder = new Builder(name);
builder.scmClient = true;
return builder;
}
public static Builder other(String name)
{
return new Builder(name);
}
@@ -127,7 +148,7 @@ public final class UserAgent
//~--- get methods ----------------------------------------------------------
/**
* Returns the {@link Charset}, which is used to decode the basic
* Returns the {@link Charset}, which is used to decode the basic
* authentication header.
*
* @return {@link Charset} for basic authentication
@@ -152,13 +173,23 @@ public final class UserAgent
* Returns {@code true} if UserAgent is a browser.
*
*
* @return {@code true} if UserAgent is a browser
* @return {@code true} if UserAgent is a browser
*/
public boolean isBrowser()
{
return browser;
}
/**
* Returns {@code true} if UserAgent is an scm client (e.g. git, svn or hg).
*
*
* @return {@code true} if UserAgent is an scm client
*/
public boolean isScmClient() {
return scmClient;
}
//~--- inner classes --------------------------------------------------------
/**
@@ -204,7 +235,10 @@ public final class UserAgent
* @param browser {@code true} for a browser
*
* @return {@code this}
*
* @deprecated Use {@link #browser(String)} instead
*/
@Deprecated
public Builder browser(boolean browser)
{
this.browser = browser;
@@ -215,12 +249,11 @@ public final class UserAgent
/**
* Builds the {@link UserAgent}.
*
*
* @return new {@link UserAgent}
*/
public UserAgent build()
{
return new UserAgent(name, browser, basicAuthenticationCharset);
return new UserAgent(name, basicAuthenticationCharset, browser, scmClient);
}
//~--- fields -------------------------------------------------------------
@@ -229,7 +262,10 @@ public final class UserAgent
private final String name;
/** indicator for browsers */
private boolean browser = true;
private boolean browser = false;
/** indicator for browsers */
private boolean scmClient = false;
/** basic authentication charset */
private Charset basicAuthenticationCharset = Charsets.ISO_8859_1;
@@ -244,6 +280,9 @@ public final class UserAgent
/** indicator for browsers */
private final boolean browser;
/** indicator for scm clients (e.g. git, hg, svn) */
private final boolean scmClient;
/** name of UserAgent */
private final String name;
}

View File

@@ -62,7 +62,7 @@ public final class UserAgentParser
/** unknown UserAgent */
@VisibleForTesting
static final UserAgent UNKNOWN = UserAgent.builder("UNKNOWN").build();
static final UserAgent UNKNOWN = UserAgent.other("UNKNOWN").build();
/** logger */
private static final Logger logger =

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
@@ -89,7 +89,7 @@ public class UserAgentParserTest
UserAgent ua = parser.parse(UA_1);
assertEquals(Charsets.ISO_8859_1, ua.getBasicAuthenticationCharset());
assertTrue(ua.isBrowser());
assertFalse(ua.isBrowser());
}
/**
@@ -99,11 +99,11 @@ public class UserAgentParserTest
@Test
public void testParse()
{
UserAgent ua = UserAgent.builder("UA1").build();
UserAgent ua = UserAgent.other("UA1").build();
when(provider1.parseUserAgent(UA_1)).thenReturn(ua);
UserAgent ua2 = UserAgent.builder("UA2").build();
UserAgent ua2 = UserAgent.other("UA2").build();
when(provider2.parseUserAgent(UA_2)).thenReturn(ua2);
@@ -120,7 +120,7 @@ public class UserAgentParserTest
{
when(request.getHeader(HttpUtil.HEADER_USERAGENT)).thenReturn(UA_2);
UserAgent ua = UserAgent.builder("UA2").build();
UserAgent ua = UserAgent.other("UA2").build();
when(provider1.parseUserAgent(UA_2)).thenReturn(ua);
assertEquals(ua, parser.parse(request));
@@ -144,7 +144,7 @@ public class UserAgentParserTest
@Test
public void testParseWithCache()
{
UserAgent ua = UserAgent.builder("UA").build();
UserAgent ua = UserAgent.other("UA").build();
when(cache.get(UA_1)).thenReturn(ua);
assertEquals(ua, parser.parse(UA_1));

View File

@@ -69,8 +69,8 @@ class HttpProtocolServletAuthenticationFilterBaseTest {
@Mock
private FilterChain filterChain;
private UserAgent nonBrowser = UserAgent.builder("i'm not a browser").browser(false).build();
private UserAgent browser = UserAgent.builder("i am a browser").browser(true).build();
private UserAgent nonBrowser = UserAgent.other("i'm not a browser").build();
private UserAgent browser = UserAgent.browser("i am a browser").build();
@BeforeEach
void setUpObjectUnderTest() {

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
@@ -40,36 +40,32 @@ import sonia.scm.plugin.Extension;
*/
@Extension
public class GitUserAgentProvider implements UserAgentProvider {
private static final String PREFIX_JGIT = "jgit/";
@VisibleForTesting
static final UserAgent JGIT = UserAgent.builder("JGit")
.browser(false)
static final UserAgent JGIT = UserAgent.scmClient("JGit")
.basicAuthenticationCharset(Charsets.UTF_8)
.build();
private static final String PREFIX_REGULAR = "git/";
@VisibleForTesting
static final UserAgent GIT = UserAgent.builder("Git")
.browser(false)
static final UserAgent GIT = UserAgent.scmClient("Git")
.basicAuthenticationCharset(Charsets.UTF_8)
.build();
private static final String PREFIX_LFS = "git-lfs/";
@VisibleForTesting
static final UserAgent GIT_LFS = UserAgent.builder("Git Lfs")
.browser(false)
static final UserAgent GIT_LFS = UserAgent.scmClient("Git Lfs")
.basicAuthenticationCharset(Charsets.UTF_8)
.build();
private static final String SUFFIX_MSYSGIT = "msysgit";
@VisibleForTesting
static final UserAgent MSYSGIT = UserAgent.builder("msysGit")
.browser(false)
static final UserAgent MSYSGIT = UserAgent.scmClient("msysGit")
.basicAuthenticationCharset(Charsets.UTF_8)
.build();
@@ -80,7 +76,7 @@ public class GitUserAgentProvider implements UserAgentProvider {
@Override
public UserAgent parseUserAgent(String userAgentString) {
String lowerUserAgent = toLower(userAgentString);
if (isJGit(lowerUserAgent)) {
return JGIT;
} else if (isMsysGit(lowerUserAgent)) {
@@ -93,23 +89,23 @@ public class GitUserAgentProvider implements UserAgentProvider {
return null;
}
}
private String toLower(String value) {
return Strings.nullToEmpty(value).toLowerCase(Locale.ENGLISH);
}
private boolean isJGit(String userAgent) {
return userAgent.startsWith(PREFIX_JGIT);
}
private boolean isMsysGit(String userAgent) {
return userAgent.startsWith(PREFIX_REGULAR) && userAgent.contains(SUFFIX_MSYSGIT);
}
private boolean isGitLFS(String userAgent) {
return userAgent.startsWith(PREFIX_LFS);
}
private boolean isGit(String userAgent) {
return userAgent.startsWith(PREFIX_REGULAR);
}

View File

@@ -45,8 +45,7 @@ public class HgUserAgentProvider implements UserAgentProvider
/** mercurial seems to use system encoding */
@VisibleForTesting
static UserAgent HG = UserAgent.builder("Mercurial").browser(
false).basicAuthenticationCharset(
static UserAgent HG = UserAgent.scmClient("Mercurial").basicAuthenticationCharset(
Charset.defaultCharset()).build();
/** Field description */

View File

@@ -49,13 +49,13 @@ public final class SvnUserAgentProvider implements UserAgentProvider
/** TortoiseSVN */
@VisibleForTesting
static final UserAgent TORTOISE_SVN =
UserAgent.builder("TortoiseSVN").browser(false)
UserAgent.scmClient("TortoiseSVN")
.basicAuthenticationCharset(Charsets.UTF_8).build();
/** Subversion cli client */
@VisibleForTesting
static final UserAgent SVN =
UserAgent.builder("Subversion").browser(false)
UserAgent.scmClient("Subversion")
.basicAuthenticationCharset(Charsets.UTF_8).build();
//~--- methods --------------------------------------------------------------

View File

@@ -41,7 +41,7 @@ public class BrowserUserAgentProvider implements UserAgentProvider
/** Field description */
@VisibleForTesting
static final UserAgent CHROME = UserAgent.builder(
static final UserAgent CHROME = UserAgent.browser(
"Chrome").basicAuthenticationCharset(
Charsets.UTF_8).build();
@@ -50,21 +50,21 @@ public class BrowserUserAgentProvider implements UserAgentProvider
/** Field description */
@VisibleForTesting
static final UserAgent FIREFOX = UserAgent.builder("Firefox").build();
static final UserAgent FIREFOX = UserAgent.browser("Firefox").build();
/** Field description */
private static final String FIREFOX_PATTERN = "firefox";
/** Field description */
@VisibleForTesting
static final UserAgent MSIE = UserAgent.builder("Internet Explorer").build();
static final UserAgent MSIE = UserAgent.browser("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();
static final UserAgent SAFARI = UserAgent.browser("Safari").build();
/** Field description */
private static final String OPERA_PATTERN = "opera";
@@ -74,7 +74,7 @@ public class BrowserUserAgentProvider implements UserAgentProvider
/** Field description */
@VisibleForTesting // todo check charset
static final UserAgent OPERA = UserAgent.builder(
static final UserAgent OPERA = UserAgent.browser(
"Opera").basicAuthenticationCharset(
Charsets.UTF_8).build();

View File

@@ -74,10 +74,7 @@ public class HttpProtocolServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
UserAgent userAgent = userAgentParser.parse(request);
if (userAgent.isBrowser()) {
log.trace("dispatch browser request for user agent {}", userAgent);
dispatcher.dispatch(request, response, request.getRequestURI());
} else {
if (userAgent.isScmClient()) {
String pathInfo = request.getPathInfo();
Optional<NamespaceAndName> namespaceAndName = pathExtractor.fromUri(pathInfo);
if (namespaceAndName.isPresent()) {
@@ -86,6 +83,9 @@ public class HttpProtocolServlet extends HttpServlet {
log.debug("namespace and name not found in request path {}", pathInfo);
response.setStatus(HttpStatus.SC_BAD_REQUEST);
}
} else {
log.trace("dispatch non-scm-client request for user agent {}", userAgent);
dispatcher.dispatch(request, response, request.getRequestURI());
}
}

View File

@@ -91,15 +91,12 @@ class HttpProtocolServletTest {
@BeforeEach
void prepareMocks() {
when(userAgentParser.parse(request)).thenReturn(userAgent);
when(userAgent.isBrowser()).thenReturn(true);
when(userAgent.isScmClient()).thenReturn(false);
when(request.getRequestURI()).thenReturn("uri");
}
@Test
void shouldDispatchBrowserRequests() throws ServletException, IOException {
when(userAgent.isBrowser()).thenReturn(true);
when(request.getRequestURI()).thenReturn("uri");
servlet.service(request, response);
verify(dispatcher).dispatch(request, response, "uri");
@@ -113,7 +110,7 @@ class HttpProtocolServletTest {
@BeforeEach
void prepareMocks() {
when(userAgentParser.parse(request)).thenReturn(userAgent);
when(userAgent.isBrowser()).thenReturn(false);
when(userAgent.isScmClient()).thenReturn(true);
}
@Test