mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-5833 Fix e-mails recognition for SSO (#3019)
This commit is contained in:
+6
@@ -55,6 +55,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
private final boolean trusted;
|
||||
private final boolean isPrivate;
|
||||
private final boolean isAuthHidden;
|
||||
private final boolean isCaseInsensitive;
|
||||
private final String[] requiredFeatures;
|
||||
private final boolean isRequired;
|
||||
private final String[] types;
|
||||
@@ -69,6 +70,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
this.isPrivate = CommonUtils.toBoolean(cfg.getAttribute("private"));
|
||||
this.isRequired = CommonUtils.toBoolean(cfg.getAttribute("required"));
|
||||
this.isAuthHidden = CommonUtils.toBoolean(cfg.getAttribute("authHidden"));
|
||||
this.isCaseInsensitive = CommonUtils.toBoolean(cfg.getAttribute("caseInsensitive"));
|
||||
|
||||
for (IConfigurationElement cfgElement : cfg.getChildren("configuration")) {
|
||||
List<WebAuthProviderProperty> properties = WebAuthProviderRegistry.readProperties(cfgElement);
|
||||
@@ -132,6 +134,10 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
return isAuthHidden;
|
||||
}
|
||||
|
||||
public boolean isCaseInsensitive() {
|
||||
return isCaseInsensitive;
|
||||
}
|
||||
|
||||
public List<WebAuthProviderProperty> getConfigurationParameters() {
|
||||
return new ArrayList<>(configurationParameters.values());
|
||||
}
|
||||
|
||||
+4
-3
@@ -161,12 +161,13 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
if (userName.isEmpty()) {
|
||||
throw new DBWebException("Empty user name");
|
||||
}
|
||||
webSession.addInfoMessage("Create new user - " + userName);
|
||||
String userId = userName.toLowerCase();
|
||||
webSession.addInfoMessage("Create new user - " + userId);
|
||||
|
||||
try {
|
||||
var securityController = webSession.getAdminSecurityController();
|
||||
securityController.createUser(userName, Map.of(), enabled, authRole);
|
||||
var smUser = securityController.getUserById(userName);
|
||||
securityController.createUser(userId, Map.of(), enabled, authRole);
|
||||
var smUser = securityController.getUserById(userId);
|
||||
return new AdminUserInfo(webSession, new WebUser(smUser));
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error creating new user", e);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<plugin>
|
||||
<extension point="org.jkiss.dbeaver.auth.provider">
|
||||
<authProvider id="local" label="Local"
|
||||
caseInsensitive="true"
|
||||
description="Local name/password based authentication"
|
||||
class="io.cloudbeaver.auth.provider.local.LocalAuthProvider"
|
||||
icon="platform:/plugin/org.jkiss.dbeaver.model/icons/tree/key.png">
|
||||
|
||||
+3
-1
@@ -68,7 +68,9 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession>, SMBr
|
||||
throw new DBException("No user password provided");
|
||||
}
|
||||
String clientPasswordHash = AuthPropertyEncryption.hash.encrypt(userName, clientPassword);
|
||||
if (!storedPasswordHash.equals(clientPasswordHash)) {
|
||||
// we also need to check a hash with lower case (CB-5833)
|
||||
String clientPasswordHashLowerCase = AuthPropertyEncryption.hash.encrypt(userName.toLowerCase(), clientPassword);
|
||||
if (!storedPasswordHash.equals(clientPasswordHash) && !clientPasswordHashLowerCase.equals(storedPasswordHash)) {
|
||||
throw new DBException("Invalid user name or password");
|
||||
}
|
||||
|
||||
|
||||
+54
-13
@@ -116,6 +116,9 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
///////////////////////////////////////////
|
||||
// Users
|
||||
|
||||
/**
|
||||
* Creates user. Saves user id in database in lower-case.
|
||||
*/
|
||||
@Override
|
||||
public void createUser(
|
||||
@NotNull String userId,
|
||||
@@ -126,6 +129,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
if (CommonUtils.isEmpty(userId)) {
|
||||
throw new DBCException("Empty user name is not allowed");
|
||||
}
|
||||
userId = userId.toLowerCase(); // creating new users only with lowercase
|
||||
if (isSubjectExists(userId)) {
|
||||
throw new DBCException("User or team '" + userId + "' already exists");
|
||||
}
|
||||
@@ -140,6 +144,9 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates user. Saves user id in database as it is.
|
||||
*/
|
||||
public void createUser(
|
||||
@NotNull Connection dbCon,
|
||||
@NotNull String userId,
|
||||
@@ -910,20 +917,38 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
@NotNull WebAuthProviderDescriptor authProvider,
|
||||
@NotNull Map<String, Object> authParameters,
|
||||
boolean onlyActive // throws exception if user is inactive
|
||||
) throws DBException {
|
||||
String userId = findUserByCredentials(authProvider, authParameters, onlyActive, false);
|
||||
if (userId == null && authProvider.isCaseInsensitive()) {
|
||||
// try to find user id with lower case is auth provider is case-insensitive
|
||||
return findUserByCredentials(authProvider, authParameters, onlyActive, true);
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String findUserByCredentials(
|
||||
@NotNull WebAuthProviderDescriptor authProvider,
|
||||
@NotNull Map<String, Object> authParameters,
|
||||
boolean onlyActive,
|
||||
boolean isCaseInsensitive
|
||||
) throws DBCException {
|
||||
Map<String, Object> identCredentials = new LinkedHashMap<>();
|
||||
Map<String, String> identCredentials = new LinkedHashMap<>();
|
||||
String[] propNames = authParameters.keySet().toArray(new String[0]);
|
||||
for (AuthPropertyDescriptor prop : authProvider.getCredentialParameters(propNames)) {
|
||||
if (prop.isIdentifying()) {
|
||||
String propId = CommonUtils.toString(prop.getId());
|
||||
Object paramValue = authParameters.get(propId);
|
||||
if (paramValue == null) {
|
||||
if (authParameters.get(propId) == null) {
|
||||
throw new DBCException("Authentication parameter '" + prop.getId() + "' is missing");
|
||||
}
|
||||
if (prop.getEncryption() == AuthPropertyEncryption.hash) {
|
||||
throw new DBCException("Hash encryption can't be used in identifying credentials");
|
||||
}
|
||||
identCredentials.put(propId, paramValue);
|
||||
String paramValue = CommonUtils.toString(authParameters.get(propId));
|
||||
identCredentials.put(
|
||||
propId,
|
||||
isCaseInsensitive ? paramValue.toLowerCase() : paramValue
|
||||
);
|
||||
}
|
||||
}
|
||||
if (identCredentials.isEmpty()) {
|
||||
@@ -947,9 +972,9 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(sql.toString()))) {
|
||||
dbStat.setString(1, authProvider.getId());
|
||||
int param = 2;
|
||||
for (Map.Entry<String, Object> credEntry : identCredentials.entrySet()) {
|
||||
for (Map.Entry<String, String> credEntry : identCredentials.entrySet()) {
|
||||
dbStat.setString(param++, credEntry.getKey());
|
||||
dbStat.setString(param++, CommonUtils.toString(credEntry.getValue()));
|
||||
dbStat.setString(param++, credEntry.getValue());
|
||||
}
|
||||
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
@@ -980,6 +1005,15 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
@Override
|
||||
public Map<String, Object> getUserCredentials(String userId, String authProviderId) throws DBCException {
|
||||
WebAuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
|
||||
Map<String, Object> creds = getUserCredentials(authProvider, userId);
|
||||
if (creds.isEmpty() && authProvider.isCaseInsensitive()) {
|
||||
return getUserCredentials(authProvider, userId.toLowerCase());
|
||||
}
|
||||
return creds;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, Object> getUserCredentials(WebAuthProviderDescriptor authProvider, String userId) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
database.normalizeTableNames("SELECT CRED_ID,CRED_VALUE FROM {table_prefix}CB_USER_CREDENTIALS\n" +
|
||||
@@ -990,7 +1024,6 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
Map<String, Object> credentials = new LinkedHashMap<>();
|
||||
|
||||
while (dbResult.next()) {
|
||||
credentials.put(dbResult.getString(1), dbResult.getString(2));
|
||||
}
|
||||
@@ -1182,6 +1215,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
if (CommonUtils.isEmpty(teamId)) {
|
||||
throw new DBCException("Empty team name is not allowed");
|
||||
}
|
||||
teamId = teamId.toLowerCase();
|
||||
if (isSubjectExists(teamId)) {
|
||||
throw new DBCException("User or team '" + teamId + "' already exists");
|
||||
}
|
||||
@@ -2427,13 +2461,20 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
|
||||
return null;
|
||||
}
|
||||
|
||||
userId = userIdFromCredentials;
|
||||
userId = authProvider.isCaseInsensitive() ? userIdFromCredentials.toLowerCase() : userIdFromCredentials;
|
||||
if (!isSubjectExists(userId)) {
|
||||
createUser(userId,
|
||||
Map.of(),
|
||||
true,
|
||||
resolveUserAuthRole(null, authRole)
|
||||
);
|
||||
log.debug("Create user: " + userId);
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
createUser(
|
||||
dbCon,
|
||||
userId,
|
||||
Map.of(),
|
||||
true,
|
||||
resolveUserAuthRole(null, authRole)
|
||||
);
|
||||
} catch (SQLException e) {
|
||||
throw new DBException("Error saving user in database", e);
|
||||
}
|
||||
}
|
||||
setUserCredentials(userId, authProvider.getId(), userCredentials);
|
||||
} else if (userId == null) {
|
||||
|
||||
+38
@@ -16,15 +16,19 @@
|
||||
*/
|
||||
package io.cloudbeaver.test.platform;
|
||||
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
|
||||
import io.cloudbeaver.auth.provider.rp.RPAuthProvider;
|
||||
import io.cloudbeaver.test.WebGQLClient;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthStatus;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.utils.SecurityUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AuthenticationTest {
|
||||
private static final String GQL_OPEN_SESSION = """
|
||||
@@ -39,6 +43,12 @@ public class AuthenticationTest {
|
||||
userId
|
||||
}
|
||||
}""";
|
||||
private static final String GQL_AUTH_LOGOUT = """
|
||||
query authLogoutExtended($provider: ID, $configuration: ID) {
|
||||
result: authLogoutExtended(provider: $provider, configuration: $configuration) {
|
||||
redirectLinks
|
||||
}
|
||||
}""";
|
||||
|
||||
@Test
|
||||
public void testLoginUser() throws Exception {
|
||||
@@ -47,6 +57,34 @@ public class AuthenticationTest {
|
||||
Assert.assertEquals(SMAuthStatus.SUCCESS.name(), JSONUtils.getString(authInfo, "authStatus"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoginUserWithCamelCase() throws Exception {
|
||||
WebGQLClient client = CEServerTestSuite.createClient();
|
||||
for (String userId : Set.of("Test", "tESt", "tesT", "TEST")) {
|
||||
Map<String, Object> credsWithCamelCase = getUserCredentials(userId);
|
||||
// authenticating with user
|
||||
Map<String, Object> authInfo = CEServerTestSuite.authenticateTestUser(client, credsWithCamelCase);
|
||||
Assert.assertEquals(SMAuthStatus.SUCCESS.name(), JSONUtils.getString(authInfo, "authStatus"));
|
||||
Map<String, Object> activeUser = client.sendQuery(GQL_ACTIVE_USER, null);
|
||||
Assert.assertEquals(userId.toLowerCase(), JSONUtils.getString(activeUser, "userId"));
|
||||
// making logout
|
||||
client.sendQuery(GQL_AUTH_LOGOUT, Map.of("provider", "local"));
|
||||
|
||||
activeUser = client.sendQuery(GQL_ACTIVE_USER, null);
|
||||
Assert.assertNotEquals(userId.toLowerCase(), JSONUtils.getString(activeUser, "userId"));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, Object> getUserCredentials(@NotNull String userId) throws Exception {
|
||||
return Map.of(
|
||||
LocalAuthProvider.CRED_USER, userId,
|
||||
LocalAuthProvider.CRED_PASSWORD, SecurityUtils.makeDigest("test")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReverseProxyAnonymousModeLogin() throws Exception {
|
||||
WebGQLClient client = CEServerTestSuite.createClient();
|
||||
|
||||
+8
-1
@@ -101,11 +101,18 @@ public class CEServerTestSuite {
|
||||
}
|
||||
|
||||
public static Map<String, Object> authenticateTestUser(@NotNull WebGQLClient client) throws Exception {
|
||||
return authenticateTestUser(client, TEST_CREDENTIALS);
|
||||
}
|
||||
|
||||
public static Map<String, Object> authenticateTestUser(
|
||||
@NotNull WebGQLClient client,
|
||||
@NotNull Map<String, Object> credentials
|
||||
) throws Exception {
|
||||
return client.sendQuery(
|
||||
WebGQLClient.GQL_AUTHENTICATE,
|
||||
Map.of(
|
||||
"provider", LocalAuthProvider.PROVIDER_ID,
|
||||
"credentials", TEST_CREDENTIALS
|
||||
"credentials", credentials
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
+10
-1
@@ -54,7 +54,16 @@ export const UserFormInfoCredentials = observer<Props>(function UserFormInfoCred
|
||||
return (
|
||||
<Container gap vertical>
|
||||
<GroupTitle keepSize>{translate('authentication_user_credentials')}</GroupTitle>
|
||||
<InputField type="text" name="userId" state={tabState.state} readOnly={editing || disabled} keepSize tiny required>
|
||||
<InputField
|
||||
description={!editing ? translate('authentication_user_name_description') : undefined}
|
||||
type="text"
|
||||
name="userId"
|
||||
state={tabState.state}
|
||||
readOnly={editing || disabled}
|
||||
keepSize
|
||||
tiny
|
||||
required
|
||||
>
|
||||
{translate('authentication_user_name')}
|
||||
</InputField>
|
||||
{local && (
|
||||
|
||||
+2
@@ -104,6 +104,8 @@ export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormStat
|
||||
authRole: getTransformedAuthRole(this.state.authRole),
|
||||
enabled: this.state.enabled,
|
||||
});
|
||||
// userId is modified by backend and may not match value we sent, so we need to update the state
|
||||
this.state.userId = user.userId;
|
||||
this.initialState.userId = user.userId;
|
||||
this.formState.setMode(FormMode.Edit);
|
||||
}
|
||||
|
||||
+4
-2
@@ -31,8 +31,10 @@ interface State {
|
||||
export function useUsersTable(filters: IUserFilters) {
|
||||
const translate = useTranslate();
|
||||
const usersResource = useService(UsersResource);
|
||||
const searchFilter = filters.search.trim().toLowerCase();
|
||||
const enabledStateFilter = filters.status === 'true' ? true : filters.status === 'false' ? false : undefined;
|
||||
const pagination = useOffsetPagination(UsersResource, {
|
||||
key: UsersResourceFilterKey(filters.search.toLowerCase(), filters.status === 'true' ? true : filters.status === 'false' ? false : undefined),
|
||||
key: UsersResourceFilterKey(searchFilter, enabledStateFilter),
|
||||
});
|
||||
const usersLoader = useResource(useUsersTable, usersResource, pagination.currentPage);
|
||||
const notificationService = useService(NotificationService);
|
||||
@@ -48,7 +50,7 @@ export function useUsersTable(filters: IUserFilters) {
|
||||
get users() {
|
||||
const users = Array.from(
|
||||
new Set([
|
||||
...this.usersLoader.resource.get(UsersResourceNewUsers),
|
||||
...this.usersLoader.resource.get(UsersResourceFilterKey(searchFilter, enabledStateFilter)),
|
||||
...usersResource.get(pagination.allPages).filter(isDefined).sort(compareUsers),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -26,6 +26,7 @@ export default [
|
||||
['authentication_identity_provider_dialog_subtitle', 'Choose configuration you want to sign in with'],
|
||||
|
||||
['authentication_user_name', 'Username'],
|
||||
['authentication_user_name_description', "User's identifier is stored in lowercase"],
|
||||
['authentication_user_role', 'Role'],
|
||||
['authentication_user_credentials', 'Credentials'],
|
||||
['authentication_user_meta_parameters', 'Parameters'],
|
||||
|
||||
@@ -10,32 +10,36 @@ export default [
|
||||
['authentication_login', 'Connexion'],
|
||||
['authentication_logout', 'Déconnexion'],
|
||||
['authentication_logout_error', 'Impossible de se déconnecter'],
|
||||
['authentication_authenticate', 'S\'authentifier'],
|
||||
['authentication_authenticate', "S'authentifier"],
|
||||
['authentication_authorizing', 'Autorisation...'],
|
||||
['authentication_auth_federated', 'Fédéré'],
|
||||
['authentication_auth_force_session_logout', 'Forcer la déconnexion de toutes les autres sessions'],
|
||||
['authentication_auth_force_session_logout_popup_title', 'Déconnecter les autres sessions'],
|
||||
['authentication_auth_force_session_logout_popup_message', 'Tous les utilisateurs seront déconnectés de tous les appareils. Voulez-vous continuer ?'],
|
||||
[
|
||||
'authentication_auth_force_session_logout_popup_message',
|
||||
'Tous les utilisateurs seront déconnectés de tous les appareils. Voulez-vous continuer ?',
|
||||
],
|
||||
['authentication_auth_force_session_logout_checkbox_tooltip', 'Pour appliquer cette option, vous devez vous reconnecter'],
|
||||
['authentication_auth_additional', 'Supplémentaire'],
|
||||
['authentication_select_provider', 'Sélectionner un fournisseur disponible'],
|
||||
['authentication_configure', 'Veuillez configurer les méthodes d\'authentification'],
|
||||
['authentication_provider_disabled', 'La méthode d\'authentification est désactivée'],
|
||||
['authentication_configure', "Veuillez configurer les méthodes d'authentification"],
|
||||
['authentication_provider_disabled', "La méthode d'authentification est désactivée"],
|
||||
['authentication_request_token', 'Une authentification supplémentaire est requise'],
|
||||
['authentication_identity_provider_search_placeholder', 'Rechercher le nom ou la description de la configuration...'],
|
||||
['authentication_identity_provider_dialog_subtitle', 'Choisissez la configuration avec laquelle vous souhaitez vous connecter'],
|
||||
|
||||
['authentication_user_name', 'Nom d\'utilisateur'],
|
||||
['authentication_user_name', "Nom d'utilisateur"],
|
||||
['authentication_user_name_description', "User's identifier is stored in lowercase"],
|
||||
['authentication_user_role', 'Rôle'],
|
||||
['authentication_user_credentials', 'Identifiants'],
|
||||
['authentication_user_meta_parameters', 'Paramètres'],
|
||||
['authentication_team_meta_parameters', 'Paramètres'],
|
||||
['authentication_user_password', 'Mot de passe de l\'utilisateur'],
|
||||
['authentication_user_password', "Mot de passe de l'utilisateur"],
|
||||
['authentication_user_password_repeat', 'Répéter le mot de passe'],
|
||||
['authentication_user_team', 'Équipe de l\'utilisateur'],
|
||||
['authentication_user_status', 'Statut de l\'utilisateur'],
|
||||
['authentication_user_team', "Équipe de l'utilisateur"],
|
||||
['authentication_user_status', "Statut de l'utilisateur"],
|
||||
['authentication_user_enabled', 'Activé'],
|
||||
['authentication_user_login_not_set', 'Le nom d\'utilisateur ne peut pas être vide'],
|
||||
['authentication_user_login_not_set', "Le nom d'utilisateur ne peut pas être vide"],
|
||||
['authentication_user_team_not_set', 'Au moins une équipe doit être sélectionnée'],
|
||||
['authentication_user_role_not_set', '{alias:authentication_user_role} est requis'],
|
||||
['authentication_user_password_not_set', '{alias:authentication_user_password} est requis'],
|
||||
@@ -46,8 +50,14 @@ export default [
|
||||
['authentication_team_icon_tooltip', 'Équipe'],
|
||||
|
||||
['plugin_authentication_user_loading_error', 'Impossible de charger les données utilisateur'],
|
||||
['plugin_authentication_loading_error_dialog_title', 'Erreur d\'authentification'],
|
||||
['plugin_authentication_loading_error_dialog_message', 'Une erreur est survenue lors du chargement des données utilisateur. Voulez-vous vous déconnecter ?'],
|
||||
['plugin_authentication_loading_error_dialog_title', "Erreur d'authentification"],
|
||||
[
|
||||
'plugin_authentication_loading_error_dialog_message',
|
||||
'Une erreur est survenue lors du chargement des données utilisateur. Voulez-vous vous déconnecter ?',
|
||||
],
|
||||
|
||||
['plugin_authentication_authentication_method_disabled', 'La méthode d\'authentification est désactivée, veuillez configurer les méthodes d\'authentification dans les paramètres'],
|
||||
[
|
||||
'plugin_authentication_authentication_method_disabled',
|
||||
"La méthode d'authentification est désactivée, veuillez configurer les méthodes d'authentification dans les paramètres",
|
||||
],
|
||||
];
|
||||
|
||||
@@ -20,6 +20,7 @@ export default [
|
||||
['authentication_auth_additional', 'Additional'],
|
||||
['authentication_request_token', 'Autenticazione addizionale richiesta'],
|
||||
['authentication_user_name', 'Nome utente'],
|
||||
['authentication_user_name_description', "User's identifier is stored in lowercase"],
|
||||
['authentication_user_role', 'Role'],
|
||||
['authentication_user_credentials', 'Credenziali'],
|
||||
['authentication_user_meta_parameters', 'Parameters'],
|
||||
|
||||
@@ -26,6 +26,7 @@ export default [
|
||||
['authentication_identity_provider_dialog_subtitle', 'Выберите конфигурацию с помощью которой вы хотите войти'],
|
||||
|
||||
['authentication_user_name', 'Имя'],
|
||||
['authentication_user_name_description', 'Идентификатор пользователя хранится в нижнем регистре'],
|
||||
['authentication_user_role', 'Роль'],
|
||||
['authentication_user_credentials', 'Учетные данные'],
|
||||
['authentication_user_meta_parameters', 'Параметры'],
|
||||
|
||||
@@ -9,7 +9,7 @@ export default [
|
||||
['authentication_login_dialog_title', '认证'],
|
||||
['authentication_login', '登录'],
|
||||
['authentication_logout', '注销'],
|
||||
['authentication_logout_error', "无法注销"],
|
||||
['authentication_logout_error', '无法注销'],
|
||||
['authentication_authenticate', '认证'],
|
||||
['authentication_authorizing', '认证中...'],
|
||||
['authentication_auth_federated', '联合认证'],
|
||||
@@ -26,6 +26,7 @@ export default [
|
||||
['authentication_identity_provider_dialog_subtitle', '选择您要用于登录的配置'],
|
||||
|
||||
['authentication_user_name', '用户名称'],
|
||||
['authentication_user_name_description', "User's identifier is stored in lowercase"],
|
||||
['authentication_user_role', 'Role'],
|
||||
['authentication_user_credentials', '凭据'],
|
||||
['authentication_user_meta_parameters', '参数'],
|
||||
@@ -42,7 +43,7 @@ export default [
|
||||
['authentication_user_login_cant_be_used', '抱歉,不能使用该名称'],
|
||||
['authentication_user_icon_tooltip', '用户'],
|
||||
|
||||
['plugin_authentication_user_loading_error', "无法加载用户数据"],
|
||||
['plugin_authentication_user_loading_error', '无法加载用户数据'],
|
||||
['plugin_authentication_loading_error_dialog_title', '认证错误'],
|
||||
['plugin_authentication_loading_error_dialog_message', '加载用户数据出错,是否注销?'],
|
||||
|
||||
|
||||
Reference in New Issue
Block a user