Browse Source

Merge branch 'master' into release

tags/r1.9.1^0
Florian Zschocke 4 years ago
parent
commit
a93cd37bc6

+ 1
- 1
README.markdown View File

@@ -5,7 +5,7 @@ Gitblit is an open source, pure Java Git solution for managing, viewing, and ser
More information about Gitblit can be found [here](http://gitblit.com).
<a href='https://github.com/fzs/gitblit/releases/latest'><img src='https://img.shields.io/badge/dynamic/json?color=9cf&label=Download&query=%24.name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Ffzs%2Fgitblit%2Freleases%2Flatest'></a>
<a href='https://github.com/gitblit/gitblit/releases/latest'><img src='https://img.shields.io/badge/dynamic/json?color=9cf&label=Download&query=%24.name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fgitblit%2Fgitblit%2Freleases%2Flatest'></a>
License
-------

+ 5
- 5
build.moxie View File

@@ -10,12 +10,12 @@ name: Gitblit
description: pure Java Git solution
groupId: com.gitblit
artifactId: gitblit
version: 1.9.0-SNAPSHOT
version: 1.9.1-SNAPSHOT
inceptionYear: 2011

# Current stable release
releaseVersion: 1.8.0
releaseDate: 2016-06-22
releaseVersion: 1.9.0
releaseDate: 2020-02-01

# Project urls
url: 'http://gitblit.com'
@@ -96,8 +96,8 @@ dependencyDirectory: ext
registeredRepositories:
- { id: central, url: 'https://repo1.maven.org/maven2' }
- { id: mavencentral, url: 'https://repo1.maven.org/maven2' }
- { id: eclipse, url: 'http://repo.eclipse.org/content/groups/releases' }
- { id: eclipse-snapshots, url: 'http://repo.eclipse.org/content/groups/snapshots' }
- { id: eclipse, url: 'https://repo.eclipse.org/content/groups/releases' }
- { id: eclipse-snapshots, url: 'https://repo.eclipse.org/content/groups/snapshots' }
- { id: gitblit, url: 'http://gitblit.github.io/gitblit-maven' }

# Source all dependencies from the following repositories in the specified order

+ 46
- 5
releases.moxie View File

@@ -1,10 +1,50 @@
#
# ${project.version} release
#
r30: {
r31: {
title: ${project.name} ${project.version} released
id: ${project.version}
date: ${project.buildDate}
note: ''
When you have Gitblit installed as a service under Linux or Windows, you may need to edit your service script/definition. The command line to start Gitblit needs to be different, the classpath and class are speficied now.

See notes for release 1.9.0.
''
html: ~
text: ''
!! IMPORTANT BUG FIX FOR PASSWORD HASH UPGRADE !!
There is a severe bug in version 1.9.0, which can lock users out from their accounts.
When updating from a previous version to 1.9.0, existing stored passwords are rehashed
with a more secure password hash mechanism when a user first logs in after the update.
This happens when the password hashing mechanism was left at default and not specifically
set in the configuration. An error in the implementation will destroy the stored password
instead and the user can no longer log in.

Only certain circumstances will lead to this wrong behaviour. It will most likely
affect users of the Gitblit Docker container. If you did not encounter any problems,
update to 1.9.1 to be on the safe side. If you were hit by this bug, we are deeply sorry.
There is no way to fix the affected accounts other than to set a new password.

This is fixed in 1.9.1. Updates of existing installations should be made to 1.9.1, not 1.9.0.
''
security: ~
fixes:
- Fixed broken password hash upgrade destroying existing stored passwords on update.
- Fixed Linux service scripts to use `-cp` parameter instead of `-jar`.
changes: ~
additions: ~
dependencyChanges: ~
contributors: ~
}

#
# 1.9.0 release
#
r30: {
title: Gitblit 1.9.0 released
id: 1.9.0
date: 2020-02-01
note: ''
Gitblit uses Servlet 3.0 and thus drops support for Tomcat 6. Run on Tomcat 6 at your own risk.

@@ -18,7 +58,8 @@ r30: {

When the `realm.ldap.bindpattern` property is set, GitBlit will only bind as the user to LDAP, not to a manager account or anonymously.

Older password storage mechanisms are deprecated, PBKDF2 is the new default. When you switch from plaintext to a hashed scheme, or from the older hashed to the new PBKDF2 scheme, the stored password of a user will be rehashed with the more secure mechanism when the user logs in.
Older password storage mechanisms are deprecated, PBKDF2 is the new default. When you switch from plaintext to a hashed scheme, or from the older hashed to the new PBKDF2 scheme, the stored password of a user will be rehashed with the more secure mechanism when the user logs in.
!! THIS IS BROKEN IN 1.9.0. DO NOT UPDATE TO 1.9.0. USE 1.9.1 INSTEAD !!
''
html: ~
text: ''
@@ -1949,6 +1990,6 @@ r1: {
- James Moger
}

snapshot: &r30
release: &r29
releases: &r[1..29]
snapshot: &r31
release: &r30
releases: &r[1..30]

+ 1
- 1
src/main/distrib/linux/authority.sh View File

@@ -1,2 +1,2 @@
#!/bin/bash
java -cp gitblit.jar:ext/* com.gitblit.authority.GitblitAuthority --baseFolder data
java -cp "gitblit.jar:ext/*" com.gitblit.authority.GitblitAuthority --baseFolder data

+ 5
- 5
src/main/distrib/linux/install-service-fedora.sh View File

@@ -18,16 +18,16 @@ After=network.target
[Service]
User=gitblit
Group=gitblit
Environment="ARGS=-server -Xmx1024M -Djava.awt.headless=true -jar"
Environment="ARGS=-server -Xmx1024M -Djava.awt.headless=true -cp"
EnvironmentFile=-/etc/sysconfig/gitblit
WorkingDirectory=/opt/gitblit
ExecStart=/usr/bin/java \$ARGS gitblit.jar --httpsPort \$GITBLIT_HTTPS_PORT --httpPort \$GITBLIT_HTTP_PORT --baseFolder \$GITBLIT_BASE_FOLDER --dailyLogFile
ExecStop=/usr/bin/java \$ARGS gitblit.jar --baseFolder \$GITBLIT_BASE_FOLDER --stop
ExecStart=/usr/bin/java \$ARGS gitblit.jar:ext/* com.gitblit.GitBlitServer --httpsPort \$GITBLIT_HTTPS_PORT --httpPort \$GITBLIT_HTTP_PORT --baseFolder \$GITBLIT_BASE_FOLDER --dailyLogFile
ExecStop=/usr/bin/java \$ARGS gitblit.jar:ext/* com.gitblit.GitBlitServer --baseFolder \$GITBLIT_BASE_FOLDER --stop

[Install]
WantedBy=multi-user.target
EOF

# Finally copy the files to the destination and register the systemd unit.
sudo su -c "cp /tmp/gitblit.defaults /etc/sysconfig/gitblit && cp /tmp/gitblit.service /etc/systemd/system/"
sudo su -c "systemctl daemon-reload && systemctl enable gitblit.service && systemctl start gitblit.service"
sudo sh -c "cp /tmp/gitblit.defaults /etc/sysconfig/gitblit && cp /tmp/gitblit.service /etc/systemd/system/"
sudo sh -c "systemctl daemon-reload && systemctl enable gitblit.service && systemctl start gitblit.service"

+ 2
- 2
src/main/distrib/linux/migrate-tickets.sh View File

@@ -8,7 +8,7 @@
#
# --------------------------------------------------------------------------

if [[ -z $1 || -z $2 ]]; then
if [ -z $1 ] || [ -z $2 ]; then
echo "Please specify the output ticket service and your baseFolder!";
echo "";
echo "usage:";
@@ -17,5 +17,5 @@ if [[ -z $1 || -z $2 ]]; then
exit 1;
fi

java -cp gitblit.jar:./ext/* com.gitblit.MigrateTickets $1 --baseFolder $2
java -cp "gitblit.jar:ext/*" com.gitblit.MigrateTickets $1 --baseFolder $2


+ 2
- 2
src/main/distrib/linux/reindex-tickets.sh View File

@@ -11,7 +11,7 @@
#
# --------------------------------------------------------------------------

if [[ -z $1 ]]; then
if [ -z $1 ] ; then
echo "Please specify your baseFolder!";
echo "";
echo "usage:";
@@ -20,5 +20,5 @@ if [[ -z $1 ]]; then
exit 1;
fi

java -cp gitblit.jar:./ext/* com.gitblit.ReindexTickets --baseFolder $1
java -cp "gitblit.jar:ext/*" com.gitblit.ReindexTickets --baseFolder $1


+ 3
- 3
src/main/distrib/linux/service-centos.sh View File

@@ -11,7 +11,7 @@ GITBLIT_HTTP_PORT=0
GITBLIT_HTTPS_PORT=8443
GITBLIT_LOG=/var/log/gitblit.log
source ${GITBLIT_PATH}/java-proxy-config.sh
JAVA="java -server -Xmx1024M ${JAVA_PROXY_CONFIG} -Djava.awt.headless=true -jar"
JAVA="java -server -Xmx1024M ${JAVA_PROXY_CONFIG} -Djava.awt.headless=true -cp"

RETVAL=0

@@ -21,7 +21,7 @@ case "$1" in
then
echo $"Starting gitblit server"
cd $GITBLIT_PATH
$JAVA $GITBLIT_PATH/gitblit.jar --httpsPort $GITBLIT_HTTPS_PORT --httpPort $GITBLIT_HTTP_PORT --baseFolder $GITBLIT_BASE_FOLDER --dailyLogFile &
$JAVA "$GITBLIT_PATH/gitblit.jar:$GITBLIT_PATH/ext/*" com.gitblit.GitBlitServer --httpsPort $GITBLIT_HTTPS_PORT --httpPort $GITBLIT_HTTP_PORT --baseFolder $GITBLIT_BASE_FOLDER --dailyLogFile &
echo "."
exit $RETVAL
fi
@@ -32,7 +32,7 @@ case "$1" in
then
echo $"Stopping gitblit server"
cd $GITBLIT_PATH
$JAVA $GITBLIT_PATH/gitblit.jar --baseFolder $GITBLIT_BASE_FOLDER --stop > /dev/null &
$JAVA "$GITBLIT_PATH/gitblit.jar:$GITBLIT_PATH/ext/*" com.gitblit.GitBlitServer --baseFolder $GITBLIT_BASE_FOLDER --stop > /dev/null &
echo "."
exit $RETVAL
fi

+ 1
- 1
src/main/distrib/linux/service-ubuntu.sh View File

@@ -19,7 +19,7 @@ GITBLIT_PATH=/opt/gitblit
GITBLIT_BASE_FOLDER=/opt/gitblit/data
GITBLIT_USER="gitblit"
source ${GITBLIT_PATH}/java-proxy-config.sh
ARGS="-server -Xmx1024M ${JAVA_PROXY_CONFIG} -Djava.awt.headless=true -jar gitblit.jar --baseFolder $GITBLIT_BASE_FOLDER --dailyLogFile"
ARGS="-server -Xmx1024M ${JAVA_PROXY_CONFIG} -Djava.awt.headless=true -cp gitblit.jar:ext/* com.gitblit.GitBlitServer --baseFolder $GITBLIT_BASE_FOLDER --dailyLogFile"

RETVAL=0


+ 49
- 36
src/main/java/com/gitblit/manager/AuthenticationManager.java View File

@@ -18,10 +18,7 @@ package com.gitblit.manager;
import java.nio.charset.Charset;
import java.security.Principal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.Cookie;
@@ -455,7 +452,6 @@ public class AuthenticationManager implements IAuthenticationManager {
/**
* Authenticate a user based on a username and password.
*
* @see IUserService.authenticate(String, char[])
* @param username
* @param password
* @return a user object or null
@@ -474,34 +470,39 @@ public class AuthenticationManager implements IAuthenticationManager {
}
String usernameDecoded = StringUtils.decodeUsername(username);
String pw = new String(password);
if (StringUtils.isEmpty(pw)) {
if (StringUtils.isEmpty(password)) {
// can not authenticate empty password
return null;
}

UserModel user = userManager.getUserModel(usernameDecoded);

// try local authentication
if (user != null && user.isLocalAccount()) {
UserModel returnedUser = authenticateLocal(user, password);
if (returnedUser != null) {
// user authenticated
return returnedUser;
}
} else {
// try registered external authentication providers
for (AuthenticationProvider provider : authenticationProviders) {
if (provider instanceof UsernamePasswordAuthenticationProvider) {
UserModel returnedUser = provider.authenticate(usernameDecoded, password);
if (returnedUser != null) {
// user authenticated
returnedUser.accountType = provider.getAccountType();
return validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);
try {
// try local authentication
if (user != null && user.isLocalAccount()) {
UserModel returnedUser = authenticateLocal(user, password);
if (returnedUser != null) {
// user authenticated
return returnedUser;
}
} else {
// try registered external authentication providers
for (AuthenticationProvider provider : authenticationProviders) {
if (provider instanceof UsernamePasswordAuthenticationProvider) {
UserModel returnedUser = provider.authenticate(usernameDecoded, password);
if (returnedUser != null) {
// user authenticated
returnedUser.accountType = provider.getAccountType();
return validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);
}
}
}
}
}
finally {
// Zero out password array to delete password from memory
Arrays.fill(password, Character.MIN_VALUE);
}

// could not authenticate locally or with a provider
logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}", username,
@@ -520,21 +521,33 @@ public class AuthenticationManager implements IAuthenticationManager {
protected UserModel authenticateLocal(UserModel user, char [] password) {
UserModel returnedUser = null;

PasswordHash pwdHash = PasswordHash.instanceFor(user.password);
if (pwdHash != null) {
if (pwdHash.matches(user.password, password, user.username)) {
// Create a copy of the password that we can use to rehash to upgrade to a more secure hashing method.
// This is done to be independent from the implementation of the PasswordHash, which might already clear out
// the password it gets passed in. This looks a bit stupid, as we could simply clean up the mess, but this
// falls under "better safe than sorry".
char[] pwdToUpgrade = Arrays.copyOf(password, password.length);
try {
PasswordHash pwdHash = PasswordHash.instanceFor(user.password);
if (pwdHash != null) {
if (pwdHash.matches(user.password, password, user.username)) {
returnedUser = user;
}
} else if (user.password.equals(new String(password))) {
// plain-text password
returnedUser = user;
}
} else if (user.password.equals(new String(password))) {
// plain-text password
returnedUser = user;
}
// validate user
returnedUser = validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);
// try to upgrade the stored password hash to a stronger hash, if necessary
upgradeStoredPassword(returnedUser, password, pwdHash);

// validate user
returnedUser = validateAuthentication(returnedUser, AuthenticationType.CREDENTIALS);

// try to upgrade the stored password hash to a stronger hash, if necessary
upgradeStoredPassword(returnedUser, pwdToUpgrade, pwdHash);
}
finally {
// Now we make sure that the password is zeroed out in any case.
Arrays.fill(password, Character.MIN_VALUE);
Arrays.fill(pwdToUpgrade, Character.MIN_VALUE);
}

return returnedUser;
}

+ 15
- 0
src/main/java/com/gitblit/utils/StringUtils.java View File

@@ -56,6 +56,21 @@ public class StringUtils {
return value == null || value.trim().length() == 0;
}
/**
* Returns true if the character array represents an empty String.
* An empty character sequence is defined as a sequence that
* either has no characters at all, or no characters above
* '\u0020' (space).
*
* @param value
* @return true if value is null or represents an empty String
*/
public static boolean isEmpty(char[] value) {
if (value == null || value.length == 0) return true;
for ( char c : value) if (c > '\u0020') return false;
return true;
}
/**
* Replaces carriage returns and line feeds with html line breaks.
*

+ 1
- 1
src/site/federation.mkd View File

@@ -335,6 +335,6 @@ Instead of using `federation.properties` you may directly specify a Gitblit inst
java -cp fedclient.jar;"%CD%/ext/*" com.gitblit.FederationClient --url https://go.gitblit.com --mirror --bare --token 123456789
--repositoriesFolder c:/mymirror
java -cp fedclient.jar:ext/* com.gitblit.FederationClient --url https://go.gitblit.com --mirror --bare --token 123456789
java -cp "fedclient.jar:ext/*" com.gitblit.FederationClient --url https://go.gitblit.com --mirror --bare --token 123456789
--repositoriesFolder /srv/mymirror --daemon --frequency "24 hours"

+ 1
- 1
src/site/rpc.mkd View File

@@ -8,7 +8,7 @@ Gitblit optionally allows a remote client to administer the Gitblit server. Thi
web.enableRpcManagement=false
web.enableRpcAdministration=false
**https** is strongly recommended because passwords are insecurely transmitted form your browser/rpc client using Basic authentication!
**https** is strongly recommended because passwords are insecurely transmitted from your browser/rpc client using Basic authentication!
The Gitblit JSON RPC mechanism, like the Gitblit JGit servlet, syndication/feed servlet, etc, supports request-based authentication. Making an *admin* request will trigger Gitblit's basic authentication mechanism. Listing of repositories, generally, will not trigger this authentication mechanism unless *web.authenticateViewPages=true*. That means its possible to allow anonymous enumeration of repositories that are not *view restricted* or *clone restricted*. Of course, if credentials are provided then all private repositories that are available to the user account will be enumerated in the JSON response.

+ 1
- 1
src/site/setup_go.mkd View File

@@ -17,7 +17,7 @@ Open `data/gitblit.properties` in your favorite text editor and make sure to rev
**NOTE:** You can only have **one** SSL certificate specified for a port.
4. exit the authority app
4. Windows: Execute `gitblit.cmd` or `java -cp gitblit.jar;"%CD%\ext\*" com.gitblit.GitBlitServer --baseFolder data` from a command-line
Linux/OSX: Execute `gitblit.sh` or `java -cp gitblit.jar;ext/* com.gitblit.GitBlitServer --baseFolder data` from a command-line
Linux/OSX: Execute `gitblit.sh` or `java -cp "gitblit.jar:ext/*"" com.gitblit.GitBlitServer --baseFolder data` from a command-line
5. Open your browser to <http://localhost:8080> or <https://localhost:8443> depending on your chosen configuration.
6. Enter the default administrator credentials: **admin / admin** and click the *Login* button
**NOTE:** Make sure to change the administrator username and/or password!!

+ 22
- 0
src/site/upgrade_go.mkd View File

@@ -1,3 +1,25 @@
## Upgrading Gitblit GO (1.9.1+)
The command line to start Gitblit has changed from
```
java -jar gitblit.jar --baseFolder data
```
to
```
java -cp "gitblit.jar:ext/*" com.gitblit.GitBlitServer --baseFolder data
```
or on Windows to
```
java -cp gitblit.jar;"%CD%\ext\*" com.gitblit.GitBlitServer --baseFolder data
```
The class path and main class need to be specified now. If you have installed Gitblit as a service you will need to adjust the service scripts or definitions accordingly.
## Upgrading Gitblit GO (1.7.0+)
The default `gitblit.properties` file has been split into two files: `gitblit.properties`, which is the recommended file for setting your configuration, and `defaults.properties` which are Gitblit's default settings.

+ 85
- 15
src/test/java/com/gitblit/tests/AuthenticationManagerTest.java View File

@@ -19,13 +19,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.*;

import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
@@ -654,16 +648,84 @@ public class AuthenticationManagerTest extends GitblitUnitTest {
public void testAuthenticate() throws Exception {
IAuthenticationManager auth = newAuthenticationManager();


String password = "pass word";
UserModel user = new UserModel("sunnyjim");
user.password = "password";
user.password = password;
users.updateUserModel(user);

assertNotNull(auth.authenticate(user.username, user.password.toCharArray(), null));
char[] pwd = password.toCharArray();
assertNotNull(auth.authenticate(user.username, pwd, null));

// validate that the passed in password has been zeroed out in memory
char[] zeroes = new char[pwd.length];
Arrays.fill(zeroes, Character.MIN_VALUE);
assertArrayEquals(zeroes, pwd);
}


@Test
public void testAuthenticateDisabledUser() throws Exception {
IAuthenticationManager auth = newAuthenticationManager();


String password = "password";
UserModel user = new UserModel("sunnyjim");
user.password = password;
user.disabled = true;
users.updateUserModel(user);

assertNull(auth.authenticate(user.username, password.toCharArray(), null));

user.disabled = false;
users.updateUserModel(user);
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));
}


@Test
public void testAuthenticateEmptyPassword() throws Exception {
IAuthenticationManager auth = newAuthenticationManager();


String password = "password";
UserModel user = new UserModel("sunnyjim");
user.password = password;
users.updateUserModel(user);

assertNull(auth.authenticate(user.username, "".toCharArray(), null));
assertNull(auth.authenticate(user.username, " ".toCharArray(), null));
assertNull(auth.authenticate(user.username, new char[]{' ', '\u0010', '\u0015'}, null));
}




@Test
public void testAuthenticateWrongPassword() throws Exception {
IAuthenticationManager auth = newAuthenticationManager();


String password = "password";
UserModel user = new UserModel("sunnyjim");
user.password = password;
users.updateUserModel(user);
assertNull(auth.authenticate(user.username, user.password.toCharArray(), null));
users.deleteUserModel(user);

assertNull(auth.authenticate(user.username, "helloworld".toCharArray(), null));
}


@Test
public void testAuthenticateNoSuchUser() throws Exception {
IAuthenticationManager auth = newAuthenticationManager();


String password = "password";
UserModel user = new UserModel("sunnyjim");
user.password = password;
users.updateUserModel(user);

assertNull(auth.authenticate("rainyjoe", password.toCharArray(), null));
}


@@ -671,14 +733,18 @@ public class AuthenticationManagerTest extends GitblitUnitTest {
public void testAuthenticateUpgradePlaintext() throws Exception {
IAuthenticationManager auth = newAuthenticationManager();

String password = "topsecret";
UserModel user = new UserModel("sunnyjim");
user.password = "password";
user.password = password;
users.updateUserModel(user);

assertNotNull(auth.authenticate(user.username, user.password.toCharArray(), null));
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));

// validate that plaintext password was automatically updated to hashed one
assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":"));

// validate that the password is still valid and the user can log in
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));
}


@@ -686,14 +752,18 @@ public class AuthenticationManagerTest extends GitblitUnitTest {
public void testAuthenticateUpgradeMD5() throws Exception {
IAuthenticationManager auth = newAuthenticationManager();

String password = "secretAndHashed";
UserModel user = new UserModel("sunnyjim");
user.password = "MD5:5F4DCC3B5AA765D61D8327DEB882CF99";
user.password = "MD5:BD95A1CFD00868B59B3564112D1E5847";
users.updateUserModel(user);

assertNotNull(auth.authenticate(user.username, "password".toCharArray(), null));
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));

// validate that MD5 password was automatically updated to hashed one
assertTrue(user.password.startsWith(PasswordHash.getDefaultType().name() + ":"));

// validate that the password is still valid and the user can log in
assertNotNull(auth.authenticate(user.username, password.toCharArray(), null));
}



+ 14
- 1
src/test/java/com/gitblit/tests/StringUtilsTest.java View File

@@ -26,12 +26,25 @@ public class StringUtilsTest extends GitblitUnitTest {
@Test
public void testIsEmpty() throws Exception {
assertTrue(StringUtils.isEmpty(null));
assertTrue(StringUtils.isEmpty((String)null));
assertTrue(StringUtils.isEmpty(""));
assertTrue(StringUtils.isEmpty(" "));
assertFalse(StringUtils.isEmpty("A"));
}
@Test
public void testIsEmptyCharArray() throws Exception {
assertTrue(StringUtils.isEmpty((char[])null));
assertTrue(StringUtils.isEmpty(new char[0]));
assertTrue(StringUtils.isEmpty(new char[]{ ' ' }));
assertTrue(StringUtils.isEmpty(new char[]{ ' '}));
assertTrue(StringUtils.isEmpty(new char[]{ ' ', ' ' }));
assertTrue(StringUtils.isEmpty(new char[]{ ' ', ' ', ' ' }));
assertFalse(StringUtils.isEmpty(new char[]{ '\u0020', 'f' }));
assertFalse(StringUtils.isEmpty(new char[]{ '\u0148', '\u0020' }));
assertFalse(StringUtils.isEmpty(new char[]{ 'A' }));
}
@Test
public void testBreakLinesForHtml() throws Exception {
String input = "this\nis\r\na\rtest\r\n\r\nof\n\nline\r\rbreaking";

Loading…
Cancel
Save