Show warning for control/whitespace chars in passwords

pull/172/head
M66B 6 years ago
parent b696b637e7
commit 4fa7c48d2b

@ -30,7 +30,9 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -88,6 +90,7 @@ public class FragmentAccount extends FragmentBase {
private EditText etPort; private EditText etPort;
private EditText etUser; private EditText etUser;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private TextView tvCharacters;
private Button btnOAuth; private Button btnOAuth;
private TextView tvOAuthSupport; private TextView tvOAuthSupport;
private EditText etRealm; private EditText etRealm;
@ -195,6 +198,7 @@ public class FragmentAccount extends FragmentBase {
cbInsecure = view.findViewById(R.id.cbInsecure); cbInsecure = view.findViewById(R.id.cbInsecure);
etUser = view.findViewById(R.id.etUser); etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters);
btnOAuth = view.findViewById(R.id.btnOAuth); btnOAuth = view.findViewById(R.id.btnOAuth);
tvOAuthSupport = view.findViewById(R.id.tvOAuthSupport); tvOAuthSupport = view.findViewById(R.id.tvOAuthSupport);
etRealm = view.findViewById(R.id.etRealm); etRealm = view.findViewById(R.id.etRealm);
@ -310,6 +314,28 @@ public class FragmentAccount extends FragmentBase {
} }
}); });
tilPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
}
@Override
public void afterTextChanged(Editable s) {
String password = s.toString();
boolean warning = (Helper.containsWhiteSpace(password) ||
Helper.containsControlChars(password));
tvCharacters.setVisibility(warning &&
tilPassword.getVisibility() == View.VISIBLE
? View.VISIBLE : View.GONE);
}
});
btnOAuth.setOnClickListener(new View.OnClickListener() { btnOAuth.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -436,6 +462,7 @@ public class FragmentAccount extends FragmentBase {
rgEncryption.setVisibility(View.GONE); rgEncryption.setVisibility(View.GONE);
cbInsecure.setVisibility(View.GONE); cbInsecure.setVisibility(View.GONE);
tvCharacters.setVisibility(View.GONE);
btnAdvanced.setVisibility(View.GONE); btnAdvanced.setVisibility(View.GONE);

@ -99,6 +99,7 @@ public class FragmentIdentity extends FragmentBase {
private EditText etPort; private EditText etPort;
private EditText etUser; private EditText etUser;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private TextView tvCharacters;
private Button btnOAuth; private Button btnOAuth;
private EditText etRealm; private EditText etRealm;
private CheckBox cbUseIp; private CheckBox cbUseIp;
@ -183,6 +184,7 @@ public class FragmentIdentity extends FragmentBase {
etPort = view.findViewById(R.id.etPort); etPort = view.findViewById(R.id.etPort);
etUser = view.findViewById(R.id.etUser); etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters);
btnOAuth = view.findViewById(R.id.btnOAuth); btnOAuth = view.findViewById(R.id.btnOAuth);
etRealm = view.findViewById(R.id.etRealm); etRealm = view.findViewById(R.id.etRealm);
cbUseIp = view.findViewById(R.id.cbUseIp); cbUseIp = view.findViewById(R.id.cbUseIp);
@ -292,6 +294,23 @@ public class FragmentIdentity extends FragmentBase {
} }
}); });
tilPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
}
@Override
public void afterTextChanged(Editable s) {
checkPassword(s.toString());
}
});
btnColor.setOnClickListener(new View.OnClickListener() { btnColor.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -347,6 +366,7 @@ public class FragmentIdentity extends FragmentBase {
public void onClick(View v) { public void onClick(View v) {
int visibility = (grpAdvanced.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); int visibility = (grpAdvanced.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
grpAdvanced.setVisibility(visibility); grpAdvanced.setVisibility(visibility);
checkPassword(tilPassword.getEditText().getText().toString());
if (visibility == View.VISIBLE) if (visibility == View.VISIBLE)
new Handler().post(new Runnable() { new Handler().post(new Runnable() {
@Override @Override
@ -452,6 +472,7 @@ public class FragmentIdentity extends FragmentBase {
btnAutoConfig.setEnabled(false); btnAutoConfig.setEnabled(false);
pbAutoConfig.setVisibility(View.GONE); pbAutoConfig.setVisibility(View.GONE);
cbInsecure.setVisibility(View.GONE); cbInsecure.setVisibility(View.GONE);
tvCharacters.setVisibility(View.GONE);
btnAdvanced.setVisibility(View.GONE); btnAdvanced.setVisibility(View.GONE);
@ -515,6 +536,15 @@ public class FragmentIdentity extends FragmentBase {
}.execute(this, args, "identity:config"); }.execute(this, args, "identity:config");
} }
private void checkPassword(String password) {
boolean warning = (Helper.containsWhiteSpace(password) ||
Helper.containsControlChars(password));
tvCharacters.setVisibility(warning &&
grpAdvanced.getVisibility() == View.VISIBLE
? View.VISIBLE : View.GONE);
}
private void onSave(boolean should) { private void onSave(boolean should) {
EntityAccount account = (EntityAccount) spAccount.getSelectedItem(); EntityAccount account = (EntityAccount) spAccount.getSelectedItem();

@ -26,7 +26,9 @@ import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -64,6 +66,7 @@ public class FragmentPop extends FragmentBase {
private EditText etPort; private EditText etPort;
private EditText etUser; private EditText etUser;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private TextView tvCharacters;
private EditText etName; private EditText etName;
private ViewButtonColor btnColor; private ViewButtonColor btnColor;
@ -113,6 +116,7 @@ public class FragmentPop extends FragmentBase {
cbInsecure = view.findViewById(R.id.cbInsecure); cbInsecure = view.findViewById(R.id.cbInsecure);
etUser = view.findViewById(R.id.etUser); etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters);
etName = view.findViewById(R.id.etName); etName = view.findViewById(R.id.etName);
btnColor = view.findViewById(R.id.btnColor); btnColor = view.findViewById(R.id.btnColor);
@ -132,6 +136,28 @@ public class FragmentPop extends FragmentBase {
pbWait = view.findViewById(R.id.pbWait); pbWait = view.findViewById(R.id.pbWait);
tilPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
}
@Override
public void afterTextChanged(Editable s) {
String password = s.toString();
boolean warning = (Helper.containsWhiteSpace(password) ||
Helper.containsControlChars(password));
tvCharacters.setVisibility(warning &&
tilPassword.getVisibility() == View.VISIBLE
? View.VISIBLE : View.GONE);
}
});
btnColor.setOnClickListener(new View.OnClickListener() { btnColor.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -169,6 +195,7 @@ public class FragmentPop extends FragmentBase {
// Initialize // Initialize
Helper.setViewsEnabled(view, false); Helper.setViewsEnabled(view, false);
tvCharacters.setVisibility(View.GONE);
pbSave.setVisibility(View.GONE); pbSave.setVisibility(View.GONE);
grpError.setVisibility(View.GONE); grpError.setVisibility(View.GONE);

@ -24,7 +24,9 @@ import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.util.Patterns; import android.util.Patterns;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -59,6 +61,7 @@ public class FragmentQuickSetup extends FragmentBase {
private EditText etName; private EditText etName;
private EditText etEmail; private EditText etEmail;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private TextView tvCharacters;
private Button btnCheck; private Button btnCheck;
private ContentLoadingProgressBar pbCheck; private ContentLoadingProgressBar pbCheck;
@ -88,6 +91,7 @@ public class FragmentQuickSetup extends FragmentBase {
etName = view.findViewById(R.id.etName); etName = view.findViewById(R.id.etName);
etEmail = view.findViewById(R.id.etEmail); etEmail = view.findViewById(R.id.etEmail);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters);
btnCheck = view.findViewById(R.id.btnCheck); btnCheck = view.findViewById(R.id.btnCheck);
pbCheck = view.findViewById(R.id.pbCheck); pbCheck = view.findViewById(R.id.pbCheck);
@ -119,6 +123,28 @@ public class FragmentQuickSetup extends FragmentBase {
} }
}); });
tilPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
}
@Override
public void afterTextChanged(Editable s) {
String password = s.toString();
boolean warning = (Helper.containsWhiteSpace(password) ||
Helper.containsControlChars(password));
tvCharacters.setVisibility(warning &&
tilPassword.getVisibility() == View.VISIBLE
? View.VISIBLE : View.GONE);
}
});
btnCheck.setOnClickListener(new View.OnClickListener() { btnCheck.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -149,6 +175,7 @@ public class FragmentQuickSetup extends FragmentBase {
}); });
// Initialize // Initialize
tvCharacters.setVisibility(View.GONE);
pbCheck.setVisibility(View.GONE); pbCheck.setVisibility(View.GONE);
pbSave.setVisibility(View.GONE); pbSave.setVisibility(View.GONE);
btnHelp.setVisibility(View.GONE); btnHelp.setVisibility(View.GONE);

@ -584,6 +584,26 @@ public class Helper {
return result.toArray(new String[0]); return result.toArray(new String[0]);
} }
static boolean containsWhiteSpace(String text) {
return text.matches(".*\\s+.*");
}
static boolean containsControlChars(String text) {
for (int offset = 0; offset < text.length(); ) {
int codePoint = text.codePointAt(offset);
offset += Character.charCount(codePoint);
switch (Character.getType(codePoint)) {
case Character.CONTROL: // \p{Cc}
case Character.FORMAT: // \p{Cf}
case Character.PRIVATE_USE: // \p{Co}
case Character.SURROGATE: // \p{Cs}
case Character.UNASSIGNED: // \p{Cn}
return true;
}
}
return false;
}
// Files // Files
static String sanitizeFilename(String name) { static String sanitizeFilename(String name) {

@ -243,6 +243,18 @@
android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/tvCharacters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:text="@string/title_setup_password_chars"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textIsSelectable="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<Button <Button
android:id="@+id/btnOAuth" android:id="@+id/btnOAuth"
style="@style/buttonStyleSmall" style="@style/buttonStyleSmall"
@ -254,7 +266,7 @@
android:tag="disable" android:tag="disable"
android:text="@string/title_setup_authentication" android:text="@string/title_setup_authentication"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" /> app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<TextView <TextView
android:id="@+id/tvOAuthSupport" android:id="@+id/tvOAuthSupport"

@ -430,6 +430,18 @@
android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/tvCharacters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:text="@string/title_setup_password_chars"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textIsSelectable="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<Button <Button
android:id="@+id/btnOAuth" android:id="@+id/btnOAuth"
style="@style/buttonStyleSmall" style="@style/buttonStyleSmall"
@ -441,7 +453,7 @@
android:tag="disable" android:tag="disable"
android:text="@string/title_setup_authentication" android:text="@string/title_setup_authentication"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" /> app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<TextView <TextView
android:id="@+id/tvRealm" android:id="@+id/tvRealm"

@ -163,6 +163,18 @@
android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/tvCharacters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:text="@string/title_setup_password_chars"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textIsSelectable="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<!-- name --> <!-- name -->
<TextView <TextView
@ -173,7 +185,7 @@
android:text="@string/title_account_name" android:text="@string/title_account_name"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" /> app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<TextView <TextView
android:id="@+id/tvNameRemark" android:id="@+id/tvNameRemark"

@ -61,6 +61,18 @@
android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/tvCharacters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:text="@string/title_setup_password_chars"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textIsSelectable="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<TextView <TextView
android:id="@+id/tvHint" android:id="@+id/tvHint"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -70,7 +82,7 @@
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="italic" android:textStyle="italic"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" /> app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<Button <Button
android:id="@+id/btnCheck" android:id="@+id/btnCheck"

@ -182,6 +182,7 @@
<string name="title_setup_import">Import settings</string> <string name="title_setup_import">Import settings</string>
<string name="title_setup_import_do">Imported accounts will be added without overwriting any existing ones</string> <string name="title_setup_import_do">Imported accounts will be added without overwriting any existing ones</string>
<string name="title_setup_password">Password</string> <string name="title_setup_password">Password</string>
<string name="title_setup_password_chars">Password contains control or whitespace characters</string>
<string name="title_setup_password_repeat">Repeat password</string> <string name="title_setup_password_repeat">Repeat password</string>
<string name="title_setup_password_missing">Password missing</string> <string name="title_setup_password_missing">Password missing</string>
<string name="title_setup_password_different">Passwords don\'t match</string> <string name="title_setup_password_different">Passwords don\'t match</string>

Loading…
Cancel
Save