mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge branches 'cb_release_1_0_3' and 'devel' of https://github.com/dbeaver/cloudbeaver into cb_release_1_0_3
This commit is contained in:
+2
-2
@@ -44,8 +44,8 @@ cd ..\deploy
|
||||
|
||||
echo Copy static content
|
||||
|
||||
xcopy /E /Q ..\webapp\packages\dbeaver\dist cloudbeaver\web >NUL
|
||||
xcopy /E /Q ..\webapp\packages\cloudbeaver\dist cloudbeaver\web >NUL
|
||||
|
||||
echo Cloudbeaver is ready. Run run-server.bat in cloudbeaver folder to start the server.
|
||||
|
||||
pause
|
||||
pause
|
||||
|
||||
+1
-1
@@ -43,6 +43,6 @@ cd ../deploy
|
||||
|
||||
echo "Copy static content"
|
||||
|
||||
cp -rp ../webapp/packages/dbeaver/dist/* cloudbeaver/web
|
||||
cp -rp ../webapp/packages/cloudbeaver/dist/* cloudbeaver/web
|
||||
|
||||
echo "Cloudbeaver is ready. Run run-server.bat in cloudbeaver folder to start the server."
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-SymbolicName: io.cloudbeaver.connect.provider.sample;singleton:=true
|
||||
Bundle-Version: 1.0.5.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Version: 1.0.6.qualifier
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.connect.provider.sample</artifactId>
|
||||
<version>1.0.5-SNAPSHOT</version>
|
||||
<version>1.0.6-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Version: 1.0.1.qualifier
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.2.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Version: 1.0.3.qualifier
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.0.3-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -4,7 +4,7 @@ Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 1.0.2.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.WebPlatformActivator
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<extension point="io.cloudbeaver.service">
|
||||
<service id="core" label="Core service" description="Core services" class="io.cloudbeaver.service.core.WebServiceBindingCore">
|
||||
|
||||
<permission id="public" label="Public access" category="general"/>
|
||||
</service>
|
||||
<service id="navigator" label="Database navigator" description="Database navigator services" class="io.cloudbeaver.service.navigator.WebServiceBindingNavigator">
|
||||
<premission id="navigate" label="Navigate over database metadata"/>
|
||||
|
||||
@@ -62,6 +62,7 @@ input SQLDataFilter {
|
||||
|
||||
constraints: [ SQLDataFilterConstraint ]
|
||||
where: String
|
||||
orderBy: String
|
||||
}
|
||||
|
||||
type SQLResultColumn {
|
||||
|
||||
@@ -42,10 +42,14 @@ public interface DBWSecurityController {
|
||||
|
||||
void setUserRoles(String userId, String[] roleIds, String grantorId) throws DBCException;
|
||||
|
||||
@NotNull
|
||||
WebRole[] getUserRoles(String userId) throws DBCException;
|
||||
|
||||
WebUser getUserById(String userId) throws DBCException;
|
||||
|
||||
@NotNull
|
||||
WebUser[] findUsers(String userNameMask) throws DBCException;
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Credentials
|
||||
|
||||
@@ -69,8 +73,11 @@ public interface DBWSecurityController {
|
||||
///////////////////////////////////////////
|
||||
// Roles
|
||||
|
||||
@NotNull
|
||||
WebRole[] readAllRoles() throws DBCException;
|
||||
|
||||
WebRole[] findRoles(String roleName) throws DBCException;
|
||||
|
||||
void createRole(WebRole role) throws DBCException;
|
||||
|
||||
void deleteRole(String roleId) throws DBCException;
|
||||
@@ -80,8 +87,10 @@ public interface DBWSecurityController {
|
||||
|
||||
void setSubjectPermissions(String subjectId, String[] permissionIds, String grantorId) throws DBCException;
|
||||
|
||||
@NotNull
|
||||
Set<String> getSubjectPermissions(String subjectId) throws DBCException;
|
||||
|
||||
@NotNull
|
||||
Set<String> getUserPermissions(String userId) throws DBCException;
|
||||
|
||||
///////////////////////////////////////////
|
||||
@@ -94,7 +103,8 @@ public interface DBWSecurityController {
|
||||
void updateSession(WebSession session) throws DBCException;
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Meta info
|
||||
// Permissions
|
||||
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Utils
|
||||
|
||||
@@ -39,6 +39,9 @@ import java.util.Map;
|
||||
public class DBWebException extends DBException implements GraphQLError {
|
||||
|
||||
public static final String ERROR_CODE_SESSION_EXPIRED = "sessionExpired";
|
||||
public static final String ERROR_CODE_ACCESS_DENIED = "accessDenied";
|
||||
public static final String ERROR_CODE_IDENT_REQUIRED = "identRequired";
|
||||
public static final String ERROR_CODE_AUTH_REQUIRED = "authRequired";
|
||||
|
||||
private List<Object> path;
|
||||
private List<SourceLocation> locations;
|
||||
|
||||
-2
@@ -21,8 +21,6 @@ package io.cloudbeaver;
|
||||
*/
|
||||
public class DBWebExceptionAccessDenied extends DBWebException {
|
||||
|
||||
public static final String ERROR_CODE_ACCESS_DENIED = "accessDenied";
|
||||
|
||||
public DBWebExceptionAccessDenied(String message) {
|
||||
super(message, ERROR_CODE_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
+8
-2
@@ -30,6 +30,7 @@ import org.eclipse.core.runtime.Status;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.access.DBASession;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.meta.Association;
|
||||
@@ -53,7 +54,7 @@ import java.util.function.Function;
|
||||
* Web session.
|
||||
* Is the main source of data in web application
|
||||
*/
|
||||
public class WebSession {
|
||||
public class WebSession implements DBASession {
|
||||
|
||||
private static final Log log = Log.getLog(WebSession.class);
|
||||
|
||||
@@ -104,10 +105,15 @@ public class WebSession {
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getId() {
|
||||
public String getSessionId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicationSession() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getCreateTime() {
|
||||
return CBConstants.ISO_DATE_FORMAT.format(createTime);
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@ public class WebSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("> New web session '" + webSession.getId() + "'");
|
||||
log.debug("> New web session '" + webSession.getSessionId() + "'");
|
||||
} else {
|
||||
if (updateInfo) {
|
||||
// Update only once per request
|
||||
@@ -132,7 +132,7 @@ public class WebSessionManager {
|
||||
}
|
||||
|
||||
for (WebSession session : expiredList) {
|
||||
log.debug("> Expire session '" + session.getId() + "'");
|
||||
log.debug("> Expire session '" + session.getSessionId() + "'");
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public class WebRole implements WebAuthSubject {
|
||||
|
||||
private String roleId;
|
||||
private String name;
|
||||
private String icon;
|
||||
private String description;
|
||||
|
||||
private Set<String> permissions = new LinkedHashSet<>();
|
||||
@@ -43,10 +42,6 @@ public class WebRole implements WebAuthSubject {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(String id) {
|
||||
this.roleId = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -55,14 +50,6 @@ public class WebRole implements WebAuthSubject {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class WebUser implements WebAuthSubject {
|
||||
private Map<String, Object> metaParameters = new LinkedHashMap<>();
|
||||
private Map<String, Object> configurationParameters = new LinkedHashMap<>();
|
||||
|
||||
private Set<WebRole> roles = new LinkedHashSet<>();
|
||||
private WebRole[] roles = null;
|
||||
|
||||
private String activeAuthModel;
|
||||
private Map<String, Map<String, Object>> authCredentials = new HashMap<>();
|
||||
@@ -61,7 +61,7 @@ public class WebUser implements WebAuthSubject {
|
||||
}
|
||||
|
||||
public String[] getGrantedRoles() {
|
||||
return roles.stream().map(WebRole::getRoleId).toArray(String[]::new);
|
||||
return Arrays.stream(roles).map(WebRole::getRoleId).toArray(String[]::new);
|
||||
}
|
||||
|
||||
public Map<String, Object> getMetaParameters() {
|
||||
@@ -72,8 +72,12 @@ public class WebUser implements WebAuthSubject {
|
||||
return Collections.unmodifiableMap(configurationParameters);
|
||||
}
|
||||
|
||||
public Set<WebRole> getRoles() {
|
||||
return Collections.unmodifiableSet(roles);
|
||||
public WebRole[] getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(WebRole[] roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -110,4 +110,8 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ package io.cloudbeaver.server;
|
||||
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Application configuration
|
||||
*/
|
||||
@@ -33,6 +37,7 @@ public class CBAppConfig {
|
||||
private String[] enabledDrivers = new String[0];
|
||||
private String[] disabledDrivers = new String[0];
|
||||
private String defaultAuthProvider = LocalAuthProvider.PROVIDER_ID;
|
||||
private Map<String, Object> plugins = new LinkedHashMap<>();
|
||||
|
||||
public boolean isAuthenticationEnabled() {
|
||||
return authenticationEnabled;
|
||||
@@ -77,4 +82,18 @@ public class CBAppConfig {
|
||||
public String getDefaultAuthProvider() {
|
||||
return defaultAuthProvider;
|
||||
}
|
||||
|
||||
public Map<String, Object> getPluginConfig(String pluginId) {
|
||||
Object config = plugins.get(pluginId);
|
||||
if (config instanceof Map) {
|
||||
return (Map<String, Object>) config;
|
||||
} else {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T getPluginOption(String pluginId, String option) {
|
||||
return (T)getPluginConfig(pluginId).get(option);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+44
-3
@@ -76,6 +76,7 @@ class CBSecurityController implements DBWSecurityController {
|
||||
@Override
|
||||
public void deleteUser(String userId) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
deleteAuthSubject(dbCon, userId);
|
||||
JDBCUtils.executeStatement(dbCon, "DELETE FROM CB_USER WHERE USER_ID=?", userId);
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error deleting user from database", e);
|
||||
@@ -102,10 +103,13 @@ class CBSecurityController implements DBWSecurityController {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebRole[] getUserRoles(String userId) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT * FROM CB_USER WHERE USER_ID=?")) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
"SELECT R.* FROM CB_USER_ROLE UR,CB_ROLE R " +
|
||||
"WHERE UR.USER_ID=? AND UR.ROLE_ID=R.ROLE_ID")) {
|
||||
dbStat.setString(1, userId);
|
||||
List<WebRole> roles = new ArrayList<>();
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
@@ -137,6 +141,28 @@ class CBSecurityController implements DBWSecurityController {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebUser[] findUsers(String userNameMask) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT * FROM CB_USER" +
|
||||
(CommonUtils.isEmpty(userNameMask) ? "" : " WHERE USER_ID=?"))) {
|
||||
if (!CommonUtils.isEmpty(userNameMask)) {
|
||||
dbStat.setString(1, userNameMask);
|
||||
}
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
List<WebUser> result = new ArrayList<>();
|
||||
while (dbResult.next()) {
|
||||
result.add(new WebUser(dbResult.getString(1)));
|
||||
}
|
||||
return result.toArray(new WebUser[0]);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while searching credentials", e);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Credentials
|
||||
|
||||
@@ -273,6 +299,7 @@ class CBSecurityController implements DBWSecurityController {
|
||||
///////////////////////////////////////////
|
||||
// Roles
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebRole[] readAllRoles() throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
@@ -301,6 +328,11 @@ class CBSecurityController implements DBWSecurityController {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRole[] findRoles(String roleName) throws DBCException {
|
||||
return readAllRoles();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private WebRole fetchRole(ResultSet dbResult) throws SQLException {
|
||||
WebRole role = new WebRole(dbResult.getString("ROLE_ID"));
|
||||
@@ -328,6 +360,7 @@ class CBSecurityController implements DBWSecurityController {
|
||||
@Override
|
||||
public void deleteRole(String roleId) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
deleteAuthSubject(dbCon, roleId);
|
||||
JDBCUtils.executeStatement(dbCon, "DELETE FROM CB_ROLE WHERE ROLE_ID=?", roleId);
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error deleting role from database", e);
|
||||
@@ -357,6 +390,7 @@ class CBSecurityController implements DBWSecurityController {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getSubjectPermissions(String subjectId) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
@@ -375,6 +409,7 @@ class CBSecurityController implements DBWSecurityController {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getUserPermissions(String userId) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
@@ -430,7 +465,7 @@ class CBSecurityController implements DBWSecurityController {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
"INSERT INTO CB_SESSION(SESSION_ID,USER_ID,CREATE_TIME,LAST_ACCESS_TIME,LAST_ACCESS_REMOTE_ADDRESS,LAST_ACCESS_USER_AGENT) VALUES(?,?,?,?,?,?)")) {
|
||||
dbStat.setString(1, session.getId());
|
||||
dbStat.setString(1, session.getSessionId());
|
||||
WebUser user = session.getUser();
|
||||
if (user == null) {
|
||||
dbStat.setNull(2, Types.VARCHAR);
|
||||
@@ -480,7 +515,7 @@ class CBSecurityController implements DBWSecurityController {
|
||||
dbStat.setNull(4, Types.VARCHAR);
|
||||
}
|
||||
|
||||
dbStat.setString(5, session.getId());
|
||||
dbStat.setString(5, session.getSessionId());
|
||||
if (dbStat.executeUpdate() == 0) {
|
||||
throw new DBCException("Session not exists in database");
|
||||
}
|
||||
@@ -527,5 +562,11 @@ class CBSecurityController implements DBWSecurityController {
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteAuthSubject(Connection dbCon, String subjectId) throws SQLException {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement("DELETE FROM CB_AUTH_SUBJECT WHERE SUBJECT_ID=?")) {
|
||||
dbStat.setString(1, subjectId);
|
||||
dbStat.execute();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -99,6 +99,11 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
getServletRequest(env));
|
||||
}
|
||||
|
||||
protected static WebSession getWebSession(DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
|
||||
return CBPlatform.getInstance().getSessionManager().getWebSession(
|
||||
getServletRequest(env), errorOnNotFound);
|
||||
}
|
||||
|
||||
protected static WebSession findWebSession(DataFetchingEnvironment env) {
|
||||
return CBPlatform.getInstance().getSessionManager().findWebSession(
|
||||
getServletRequest(env));
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
|
||||
Bundle-Version: 1.0.1.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Version: 1.0.2.qualifier
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<extension point="io.cloudbeaver.service">
|
||||
<service id="admin" label="User administration" description="User administration services" class="io.cloudbeaver.service.admin.WebServiceBindingAdmin">
|
||||
|
||||
<permission id="admin" label="Admin Full Access" category="admin"/>
|
||||
</service>
|
||||
</extension>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.admin</artifactId>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
type AdminUserInfo {
|
||||
userName: ID!
|
||||
userId: ID!
|
||||
|
||||
metaParameters: Object!
|
||||
configurationParameters: Object!
|
||||
@@ -8,15 +8,16 @@ type AdminUserInfo {
|
||||
}
|
||||
|
||||
type AdminRoleInfo {
|
||||
roleName: ID!
|
||||
roleId: ID!
|
||||
roleName: String
|
||||
|
||||
rolePermissions: [ID]!
|
||||
}
|
||||
|
||||
type AdminPermissionInfo {
|
||||
id: ID!
|
||||
label: [String]
|
||||
description: [String]
|
||||
label: String
|
||||
description: String
|
||||
|
||||
provider: String!
|
||||
category: String
|
||||
@@ -24,20 +25,21 @@ type AdminPermissionInfo {
|
||||
|
||||
extend type Query {
|
||||
|
||||
listUsers(userName: ID): [AdminUserInfo]!
|
||||
listRoles(roleName: ID): [AdminRoleInfo]!
|
||||
listPermissions(permissionId: ID): [AdminPermissionInfo]
|
||||
listUsers(userId: ID): [AdminUserInfo]!
|
||||
listRoles(roleId: ID): [AdminRoleInfo]!
|
||||
listPermissions: [AdminPermissionInfo]!
|
||||
|
||||
createUser(userName: ID!): AdminUserInfo!
|
||||
deleteUser(userName: ID!): Boolean
|
||||
createUser(userId: ID!): AdminUserInfo!
|
||||
deleteUser(userId: ID!): Boolean
|
||||
|
||||
getUserAdminCredentials(userName: ID!): Object
|
||||
createRole(roleId: ID!): AdminRoleInfo!
|
||||
deleteRole(roleId: ID!): Boolean
|
||||
|
||||
setUserAdminCredentials(userName: ID!, credentials: Object): Boolean
|
||||
grantUserRole(userId: ID!, roleId: ID!): Boolean
|
||||
revokeUserRole(userId: ID!, roleId: ID!): Boolean
|
||||
|
||||
grantUserRole(user: ID!, role: ID!): Boolean
|
||||
revokeUserRole(user: ID!, role: ID!): Boolean
|
||||
setRolePermissions(roleId: ID!, permissions: [ID]!): Boolean
|
||||
|
||||
setRolePermissions(roleID: ID!, permissions: [ID]!): Boolean
|
||||
setUserCredentials(userId: ID!, providerId: ID!, credentials: Object!): Boolean
|
||||
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.registry.WebPermissionDescriptor;
|
||||
|
||||
/**
|
||||
* Web permission ID
|
||||
*/
|
||||
public class AdminPermissionInfo {
|
||||
|
||||
private final WebPermissionDescriptor permission;
|
||||
|
||||
public AdminPermissionInfo(WebPermissionDescriptor permission) {
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return permission.getId();
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return permission.getLabel();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return permission.getDescription();
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return permission.getCategory();
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.admin.impl;
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
/**
|
||||
* Web service implementation
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.model.user.WebRole;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Web role info
|
||||
*/
|
||||
public class AdminRoleInfo {
|
||||
|
||||
private final WebRole role;
|
||||
private List<String> rolePermissions;
|
||||
|
||||
public AdminRoleInfo(WebRole role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getRoleId() {
|
||||
return role.getRoleId();
|
||||
}
|
||||
|
||||
public String getRoleName() {
|
||||
return role.getName();
|
||||
}
|
||||
|
||||
public List<String> getRolePermissions() {
|
||||
return rolePermissions;
|
||||
}
|
||||
|
||||
public void setRolePermissions(List<String> rolePermissions) {
|
||||
this.rolePermissions = rolePermissions;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.model.user.WebRole;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Web user info
|
||||
*/
|
||||
public class AdminUserInfo {
|
||||
|
||||
private final WebUser user;
|
||||
|
||||
private Map<String, Object> metaParameters;
|
||||
private Map<String, Object> configurationParameters;
|
||||
|
||||
public AdminUserInfo(WebUser user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return user.getUserId();
|
||||
}
|
||||
|
||||
public Map<String, Object> getMetaParameters() {
|
||||
return metaParameters;
|
||||
}
|
||||
|
||||
public Map<String, Object> getConfigurationParameters() {
|
||||
return configurationParameters;
|
||||
}
|
||||
|
||||
public String[] getGrantedRoles() throws DBCException {
|
||||
if (user.getRoles() == null) {
|
||||
WebRole[] userRoles = CBPlatform.getInstance().getApplication().getSecurityController().getUserRoles(getUserId());
|
||||
user.setRoles(userRoles);
|
||||
}
|
||||
return user.getGrantedRoles();
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -17,11 +17,56 @@
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.DBWService;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Web service API
|
||||
*/
|
||||
public interface DBWServiceAdmin extends DBWService {
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
List<AdminUserInfo> listUsers(@NotNull WebSession webSession, @Nullable String userName) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
List<AdminRoleInfo> listRoles(@NotNull WebSession webSession, @Nullable String roleName) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
List<AdminPermissionInfo> listPermissions(@NotNull WebSession webSession) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
AdminUserInfo createUser(@NotNull WebSession webSession, String userName) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
boolean deleteUser(@NotNull WebSession webSession, String userName) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
AdminRoleInfo createRole(@NotNull WebSession webSession, String roleId) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
boolean deleteRole(@NotNull WebSession webSession, String roleId) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
boolean grantUserRole(@NotNull WebSession webSession, String user, String role) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
boolean revokeUserRole(@NotNull WebSession webSession, String user, String role) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
boolean setRolePermissions(@NotNull WebSession webSession, String roleID, String[] permissions) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = AdminPermissions.PERMISSION_ADMIN)
|
||||
boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException;
|
||||
|
||||
}
|
||||
|
||||
+22
-4
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.service.DBWBindingContext;
|
||||
import io.cloudbeaver.service.WebServiceBindingBase;
|
||||
@@ -35,10 +34,29 @@ public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmi
|
||||
|
||||
@Override
|
||||
public void bindWiring(DBWBindingContext model) throws DBWebException {
|
||||
model.getQueryType().dataFetcher("listUsers", env -> {
|
||||
throw new DBWebException("Not implemented");
|
||||
});
|
||||
model.getQueryType().dataFetcher("listUsers",
|
||||
env -> getService(env).listUsers(getWebSession(env), env.getArgument("userId")));
|
||||
model.getQueryType().dataFetcher("listRoles",
|
||||
env -> getService(env).listRoles(getWebSession(env), env.getArgument("roleId")));
|
||||
model.getQueryType().dataFetcher("listPermissions",
|
||||
env -> getService(env).listPermissions(getWebSession(env)));
|
||||
model.getQueryType().dataFetcher("createUser",
|
||||
env -> getService(env).createUser(getWebSession(env), env.getArgument("userId")));
|
||||
model.getQueryType().dataFetcher("deleteUser",
|
||||
env -> getService(env).deleteUser(getWebSession(env), env.getArgument("userId")));
|
||||
model.getQueryType().dataFetcher("createRole",
|
||||
env -> getService(env).createRole(getWebSession(env), env.getArgument("roleId")));
|
||||
model.getQueryType().dataFetcher("deleteRole",
|
||||
env -> getService(env).deleteRole(getWebSession(env), env.getArgument("roleId")));
|
||||
|
||||
model.getQueryType().dataFetcher("grantUserRole",
|
||||
env -> getService(env).grantUserRole(getWebSession(env), env.getArgument("userId"), env.getArgument("roleId")));
|
||||
model.getQueryType().dataFetcher("revokeUserRole",
|
||||
env -> getService(env).revokeUserRole(getWebSession(env), env.getArgument("userId"), env.getArgument("roleId")));
|
||||
model.getQueryType().dataFetcher("setRolePermissions",
|
||||
env -> getService(env).setRolePermissions(getWebSession(env), env.getArgument("roleId"), env.getArgument("permissions")));
|
||||
model.getQueryType().dataFetcher("setUserCredentials",
|
||||
env -> getService(env).setUserCredentials(getWebSession(env), env.getArgument("userId"), env.getArgument("providerId"), env.getArgument("credentials")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+196
@@ -16,7 +16,28 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.admin.impl;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.user.WebRole;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
|
||||
import io.cloudbeaver.registry.WebPermissionDescriptor;
|
||||
import io.cloudbeaver.registry.WebServiceDescriptor;
|
||||
import io.cloudbeaver.registry.WebServiceRegistry;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import io.cloudbeaver.service.admin.AdminPermissionInfo;
|
||||
import io.cloudbeaver.service.admin.AdminRoleInfo;
|
||||
import io.cloudbeaver.service.admin.AdminUserInfo;
|
||||
import io.cloudbeaver.service.admin.DBWServiceAdmin;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Web service implementation
|
||||
@@ -24,4 +45,179 @@ import io.cloudbeaver.service.admin.DBWServiceAdmin;
|
||||
public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<AdminUserInfo> listUsers(@NotNull WebSession webSession, String userName) throws DBWebException {
|
||||
try {
|
||||
List<AdminUserInfo> webUsers = new ArrayList<>();
|
||||
for (WebUser user : CBPlatform.getInstance().getApplication().getSecurityController().findUsers(userName)) {
|
||||
webUsers.add(new AdminUserInfo(user));
|
||||
}
|
||||
return webUsers;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error reading users", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<AdminRoleInfo> listRoles(@NotNull WebSession webSession, String roleName) throws DBWebException {
|
||||
try {
|
||||
List<AdminRoleInfo> webUsers = new ArrayList<>();
|
||||
for (WebRole role : CBPlatform.getInstance().getApplication().getSecurityController().findRoles(roleName)) {
|
||||
webUsers.add(new AdminRoleInfo(role));
|
||||
}
|
||||
return webUsers;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error reading users", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<AdminPermissionInfo> listPermissions(@NotNull WebSession webSession) throws DBWebException {
|
||||
try {
|
||||
List<AdminPermissionInfo> permissionInfos = new ArrayList<>();
|
||||
for (WebServiceDescriptor wsd : WebServiceRegistry.getInstance().getWebServices()) {
|
||||
for (WebPermissionDescriptor pd : wsd.getPermissions()) {
|
||||
permissionInfos.add(new AdminPermissionInfo(pd));
|
||||
}
|
||||
}
|
||||
return permissionInfos;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error reading users", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AdminUserInfo createUser(@NotNull WebSession webSession, String userName) throws DBWebException {
|
||||
if (userName.isEmpty()) {
|
||||
throw new DBWebException("Empty user name");
|
||||
}
|
||||
try {
|
||||
WebUser newUser = new WebUser(userName);
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().createUser(newUser);
|
||||
return new AdminUserInfo(newUser);
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error creating new user", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteUser(@NotNull WebSession webSession, String userName) throws DBWebException {
|
||||
if (CommonUtils.equalObjects(userName, webSession.getUser().getUserId())) {
|
||||
throw new DBWebException("You cannot delete yourself");
|
||||
}
|
||||
try {
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().deleteUser(userName);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error deleting user", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AdminRoleInfo createRole(@NotNull WebSession webSession, String roleId) throws DBWebException {
|
||||
if (roleId.isEmpty()) {
|
||||
throw new DBWebException("Empty role ID");
|
||||
}
|
||||
try {
|
||||
WebRole newRole = new WebRole(roleId);
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().createRole(newRole);
|
||||
return new AdminRoleInfo(newRole);
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error creating new role", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteRole(@NotNull WebSession webSession, String roleId) throws DBWebException {
|
||||
try {
|
||||
WebRole[] userRoles = CBPlatform.getInstance().getApplication().getSecurityController().getUserRoles(webSession.getUser().getUserId());
|
||||
if (Arrays.stream(userRoles).anyMatch(webRole -> webRole.getRoleId().equals(roleId))) {
|
||||
throw new DBWebException("You can not delete your own role");
|
||||
}
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().deleteRole(roleId);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error deleting role", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean grantUserRole(@NotNull WebSession webSession, String user, String role) throws DBWebException {
|
||||
WebUser grantor = webSession.getUser();
|
||||
if (grantor == null) {
|
||||
throw new DBWebException("Cannot grant role in anonymous mode");
|
||||
}
|
||||
try {
|
||||
WebRole[] userRoles = CBPlatform.getInstance().getApplication().getSecurityController().getUserRoles(user);
|
||||
String[] roleIds = Arrays.stream(userRoles).map(WebRole::getRoleId).toArray(String[]::new);
|
||||
if (!ArrayUtils.contains(roleIds, role)) {
|
||||
roleIds = ArrayUtils.add(String.class, roleIds, role);
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().setUserRoles(user, roleIds, grantor.getUserId());
|
||||
} else {
|
||||
throw new DBWebException("User '" + user + "' already has role '" + role + "'");
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error granting role", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revokeUserRole(@NotNull WebSession webSession, String user, String role) throws DBWebException {
|
||||
WebUser grantor = webSession.getUser();
|
||||
if (grantor == null) {
|
||||
throw new DBWebException("Cannot grant role in anonymous mode");
|
||||
}
|
||||
try {
|
||||
WebRole[] userRoles = CBPlatform.getInstance().getApplication().getSecurityController().getUserRoles(user);
|
||||
String[] roleIds = Arrays.stream(userRoles).map(WebRole::getRoleId).toArray(String[]::new);
|
||||
if (ArrayUtils.contains(roleIds, role)) {
|
||||
roleIds = ArrayUtils.remove(String.class, roleIds, role);
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().setUserRoles(user, roleIds, grantor.getUserId());
|
||||
} else {
|
||||
throw new DBWebException("User '" + user + "' doesn't have role '" + role + "'");
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error revoking role", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setRolePermissions(@NotNull WebSession webSession, String roleID, String[] permissions) throws DBWebException {
|
||||
WebUser grantor = webSession.getUser();
|
||||
if (grantor == null) {
|
||||
throw new DBWebException("Cannot change permissions in anonymous mode");
|
||||
}
|
||||
try {
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().setSubjectPermissions(roleID, permissions, grantor.getUserId());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error setting role permissions", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException {
|
||||
WebAuthProviderDescriptor authProvider = WebServiceRegistry.getInstance().getAuthProvider(providerId);
|
||||
if (authProvider == null) {
|
||||
throw new DBWebException("Invalid auth provider '" + providerId + "'");
|
||||
}
|
||||
// Check userId credential.
|
||||
// FIXME: It is actually a hack. All crdentials must be passed from client
|
||||
if (LocalAuthProvider.PROVIDER_ID.equals(providerId)) {
|
||||
credentials.put(LocalAuthProvider.CRED_USER, userID);
|
||||
}
|
||||
try {
|
||||
CBPlatform.getInstance().getApplication().getSecurityController().setUserCredentials(userID, authProvider, credentials);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error setting user credentials", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
|
||||
Bundle-Version: 1.0.1.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Version: 1.0.2.qualifier
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.auth</artifactId>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+2
-2
@@ -36,14 +36,14 @@ public class WebServiceBindingAuth extends WebServiceBindingBase<DBWServiceAuth>
|
||||
public void bindWiring(DBWBindingContext model) throws DBWebException {
|
||||
model.getQueryType()
|
||||
.dataFetcher("authLogin", env -> getService(env).authLogin(
|
||||
getWebSession(env),
|
||||
getWebSession(env, false),
|
||||
env.getArgument("provider"),
|
||||
env.getArgument("credentials")))
|
||||
.dataFetcher("authLogout", env -> {
|
||||
getService(env).authLogout(getWebSession(env));
|
||||
return true;
|
||||
})
|
||||
.dataFetcher("sessionUser", env -> getService(env).sessionUser(getWebSession(env)))
|
||||
.dataFetcher("sessionUser", env -> getService(env).sessionUser(getWebSession(env, false)))
|
||||
.dataFetcher("authProviders", env -> getService(env).getAuthProviders(getWebSession(env)))
|
||||
;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
|
||||
Bundle-Version: 1.0.2.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Version: 1.0.3.qualifier
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.0.3-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+3
-4
@@ -17,13 +17,12 @@
|
||||
package io.cloudbeaver.service.data.transfer.impl;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import io.cloudbeaver.service.data.transfer.DBWServiceDataTransfer;
|
||||
import io.cloudbeaver.service.sql.WebSQLContextInfo;
|
||||
import io.cloudbeaver.service.sql.WebSQLProcessor;
|
||||
import io.cloudbeaver.service.data.transfer.DBWServiceDataTransfer;
|
||||
import io.cloudbeaver.service.sql.WebSQLQueryDataContainer;
|
||||
import io.cloudbeaver.service.sql.WebSQLResultsInfo;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -102,7 +101,7 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
|
||||
@NotNull
|
||||
private String makeUniqueFileName(WebSQLProcessor sqlProcessor, DataTransferProcessorDescriptor processor) {
|
||||
return sqlProcessor.getWebSession().getId() + "_" + UUID.randomUUID() + "." + WebDataTransferUtils.getProcessorFileExtension(processor);
|
||||
return sqlProcessor.getWebSession().getSessionId() + "_" + UUID.randomUUID() + "." + WebDataTransferUtils.getProcessorFileExtension(processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
|
||||
Bundle-Version: 1.0.5.qualifier
|
||||
Bundle-Release-Date: 20200518
|
||||
Bundle-Version: 1.0.6.qualifier
|
||||
Bundle-Release-Date: 20200601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.metadata</artifactId>
|
||||
<version>1.0.5-SNAPSHOT</version>
|
||||
<version>1.0.6-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.ce.drivers.feature"
|
||||
label="Cloudbeaver CE 3rd Party Drivers"
|
||||
version="1.0.25.qualifier"
|
||||
version="1.0.26.qualifier"
|
||||
provider-name="DBeaver Corp">
|
||||
|
||||
<plugin id="io.cloudbeaver.resources.drivers.base" download-size="0" install-size="0" version="0.0.0"/>
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.ce.drivers.feature</artifactId>
|
||||
<version>1.0.25-SNAPSHOT</version>
|
||||
<version>1.0.26-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.product.ce.feature"
|
||||
label="%featureName"
|
||||
version="1.0.0.qualifier"
|
||||
version="1.0.1.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.product.ce">
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce.feature</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.sample.feature"
|
||||
label="%featureName"
|
||||
version="1.0.5.qualifier"
|
||||
version="1.0.6.qualifier"
|
||||
provider-name="%providerName">
|
||||
|
||||
<description>
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.sample.feature</artifactId>
|
||||
<version>1.0.5-SNAPSHOT</version>
|
||||
<version>1.0.6-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
+3
-1
@@ -106,7 +106,9 @@ module.exports = {
|
||||
'import/export': 'off',
|
||||
'import/extensions': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'import/no-extraneous-dependencies': 'warn',
|
||||
// import/no-extraneous-dependencies off because peer dependencies are moved to builder
|
||||
// todo probably there is a way to tune this (see packageDir option in documentation)
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'import/no-cycle': 'off',
|
||||
'import-helpers/order-imports': [
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@babel/preset-react',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'react-require',
|
||||
'reshadow/babel',
|
||||
],
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react",
|
||||
['@babel/preset-typescript', { isTSX: true, allExtensions: true }],
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'babel-plugin-transform-typescript-metadata',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
['@babel/plugin-proposal-decorators', { legacy: true }],
|
||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
||||
'react-require',
|
||||
'reshadow/babel',
|
||||
],
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import babelPlugin from 'rollup-plugin-babel';
|
||||
const path = require('path');
|
||||
import postcssPlugin from 'rollup-plugin-postcss';
|
||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
||||
import includePaths from 'rollup-plugin-includepaths';
|
||||
import typescriptPlugin from 'rollup-plugin-typescript2';
|
||||
|
||||
const includePathOptions = {
|
||||
include: {},
|
||||
paths: ['src'],
|
||||
external: [
|
||||
|
||||
],
|
||||
extensions: ['.ts', '.tsx', '.js']
|
||||
};
|
||||
|
||||
export const defaultBuilderOptions = {
|
||||
babelConfig: path.join(__dirname, './babel-rollup.config.js'),
|
||||
typescriptConfig: '', // tbd
|
||||
}
|
||||
|
||||
export function configBuilder(options = defaultBuilderOptions) {
|
||||
|
||||
return {
|
||||
input: 'src/index.ts',
|
||||
output: [
|
||||
{
|
||||
dir: 'dist',
|
||||
format: 'esm',
|
||||
},
|
||||
],
|
||||
external: [
|
||||
// externals beyond peer dependencies
|
||||
],
|
||||
plugins: [
|
||||
includePaths(includePathOptions),
|
||||
nodeResolve(),
|
||||
commonjs({
|
||||
sourceMap: false,
|
||||
}),
|
||||
typescriptPlugin({
|
||||
tsconfig: 'tsconfig.build.json',
|
||||
useTsconfigDeclarationDir: true,
|
||||
}),
|
||||
babelPlugin({
|
||||
exclude: 'node_modules/**',
|
||||
extensions: ['.js', '.ts', '.tsx'],
|
||||
configFile: options.babelConfig,
|
||||
}),
|
||||
postcssPlugin({
|
||||
extract: false,
|
||||
// enable CSS modules for .module.css .module.scss files
|
||||
autoModules: true,
|
||||
// modules: {
|
||||
// generateScopedName: "[local]___[hash:base64:5]"
|
||||
// },
|
||||
inject: true,
|
||||
use: {
|
||||
sass: {
|
||||
includePaths: ['node_modules', '../../node_modules'],
|
||||
}
|
||||
},
|
||||
// this option is added in dkrupenya/rollup-plugin-postcss#develop
|
||||
// todo to be converted to custom loader
|
||||
firstPlugin: require('reshadow/postcss'),
|
||||
plugins: [
|
||||
require('postcss-preset-env')({ stage: 0 }),
|
||||
require('postcss-discard-comments'),
|
||||
// todo move 'reshadow/postcss' to custom loader to run it before postcss-loader
|
||||
// require('reshadow/postcss'),
|
||||
]
|
||||
}),
|
||||
peerDepsExternal(),
|
||||
],
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default configBuilder(defaultBuilderOptions);
|
||||
@@ -1,23 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const {resolve} = require('path');
|
||||
var PeerDepsExternalsPlugin = require('peer-deps-externals-webpack-plugin');
|
||||
|
||||
const commonConfig = require('./common');
|
||||
|
||||
module.exports = (env, argv) => merge(commonConfig(env, argv), {
|
||||
mode: argv.mode || 'development',
|
||||
entry: resolve(__dirname, '../../src/index.ts'), // todo
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: resolve(__dirname, '../../dist'),
|
||||
library: 'basicConnectionPlugin', // todo pass from package.js
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
externals: [
|
||||
/^@dbeaver\/.+$/
|
||||
],
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new PeerDepsExternalsPlugin(), // allow to treat all peerDependencies as externals
|
||||
],
|
||||
});
|
||||
@@ -1,187 +0,0 @@
|
||||
// shared config (dev and prod)
|
||||
const webpack = require('webpack');
|
||||
const ModuleDependencyWarning = require("webpack/lib/ModuleDependencyWarning");
|
||||
const path = require('path');
|
||||
const CircularDependencyPlugin = require('circular-dependency-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
|
||||
// Temporary solution to remove wrong warning messages (it's was fixed https://github.com/microsoft/TypeScript/pull/35200 in typescript 3.8)
|
||||
class IgnoreNotFoundExportPlugin {
|
||||
apply(compiler) {
|
||||
const messageRegExp = /export '.*'( \(reexported as '.*'\))? was not found in/
|
||||
function doneHook(stats) {
|
||||
stats.compilation.warnings = stats.compilation.warnings.filter(function (warn) {
|
||||
if (warn instanceof ModuleDependencyWarning && messageRegExp.test(warn.message)) {
|
||||
return false
|
||||
}
|
||||
return true;
|
||||
})
|
||||
}
|
||||
if (compiler.hooks) {
|
||||
compiler.hooks.done.tap("IgnoreNotFoundExportPlugin", doneHook)
|
||||
} else {
|
||||
compiler.plugin("done", doneHook)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
|
||||
function generateStyleLoaders(options = { hasModule: false }) {
|
||||
const moduleScope = options.hasModule ? 'local' : 'global';
|
||||
const modules = {
|
||||
mode: moduleScope,
|
||||
localIdentName: '[local]___[hash:base64:5]',
|
||||
};
|
||||
|
||||
const postCssPlugins = [
|
||||
require('postcss-preset-env')({ stage: 0 }),
|
||||
require('@csstools/postcss-sass')({
|
||||
includePaths: ['node_modules', path.resolve('../../node_modules')],
|
||||
}),
|
||||
require('postcss-discard-comments'),
|
||||
require('reshadow/postcss')({ scopeBehaviour: moduleScope })
|
||||
];
|
||||
|
||||
// 'use' clause in webpack rules
|
||||
return [
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
hot: argv.mode !== 'production',
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: modules,
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
syntax: 'postcss-scss',
|
||||
plugins: postCssPlugins,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
configFile: path.join(__dirname, '../babel.config.js')
|
||||
},
|
||||
},
|
||||
'source-map-loader'
|
||||
],
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
configFile: path.join(__dirname, '../babel.config.js')
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'eslint-loader',
|
||||
// uncomment to fix after rule changes
|
||||
// options: {
|
||||
// fix: true,
|
||||
// },
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(css|scss|sass)$/,
|
||||
oneOf: [
|
||||
// css-module files ( should have *.module mask )
|
||||
{
|
||||
test: /\.module\.(css|scss|sass)$/,
|
||||
use: generateStyleLoaders({ hasModule: true }),
|
||||
},
|
||||
{
|
||||
include: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
hot: argv.mode !== 'production',
|
||||
},
|
||||
},
|
||||
'css-loader',
|
||||
]
|
||||
},
|
||||
{
|
||||
use: generateStyleLoaders({ hasModule: false }),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 8192,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
namedModules: true,
|
||||
concatenateModules: false,
|
||||
},
|
||||
plugins: [
|
||||
new IgnoreNotFoundExportPlugin(),
|
||||
new webpack.ProgressPlugin({
|
||||
entries: true,
|
||||
modules: true,
|
||||
modulesCount: 100,
|
||||
profile: true,
|
||||
handler: (percentage, message, ...args) => {
|
||||
// custom logic
|
||||
}
|
||||
}),
|
||||
new CircularDependencyPlugin({
|
||||
// exclude detection of files based on a RegExp
|
||||
exclude: /a\.js|node_modules/,
|
||||
// include specific files based on a RegExp
|
||||
// include: /dir/,
|
||||
// add errors to webpack instead of warnings
|
||||
failOnError: false,
|
||||
// allow import cycles that include an asyncronous import,
|
||||
// e.g. via import(/* webpackMode: "weak" */ './file.js')
|
||||
allowAsyncCycles: false,
|
||||
// set the current working directory for displaying module paths
|
||||
cwd: process.cwd(),
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// all options are optional
|
||||
filename: argv.mode !== 'production' ? '[name].css' : '[name].[hash].css',
|
||||
chunkFilename: argv.mode !== 'production' ? '[name].css' : '[name].[hash].css',
|
||||
ignoreOrder: false, // Enable to remove warnings about conflicting order
|
||||
}),
|
||||
// new webpack.optimize.LimitChunkCountPlugin({
|
||||
// maxChunks: 1,
|
||||
// }),
|
||||
],
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
};
|
||||
};
|
||||
+8
-4
@@ -16,6 +16,7 @@
|
||||
"build": "lerna run build --stream",
|
||||
"serve": "lerna run serve --stream",
|
||||
"serve-plugin": "lerna run serve-plugin --stream --parallel",
|
||||
"builder-help": "builder help",
|
||||
"clean-dist": "lerna run clean-dist --stream",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
@@ -23,10 +24,13 @@
|
||||
"@babel/runtime": "7.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.23",
|
||||
"@types/node": "^12.7.8",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.6",
|
||||
"@graphql-codegen/add": "^1.8.1",
|
||||
"@graphql-codegen/cli": "^1.8.1",
|
||||
"@graphql-codegen/near-operation-file-preset": "^1.8.0",
|
||||
"@graphql-codegen/typescript": "^1.8.1",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.8.0",
|
||||
"@graphql-codegen/typescript-graphql-request": "^1.8.1",
|
||||
"@graphql-codegen/typescript-operations": "^1.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.5.0",
|
||||
"@typescript-eslint/parser": "^2.8.0",
|
||||
"eslint": "^6.6.0",
|
||||
|
||||
@@ -6,22 +6,16 @@
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "npm run clean-dist && builder build-plugin",
|
||||
"build": "yarn run clean-dist && builder build-plugin",
|
||||
"serve-plugin": "builder serve-plugin",
|
||||
"clean-dist": "rimraf dist/*",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dbeaver/core": "^0.1.0"
|
||||
"@dbeaver/core": "^0.1.0",
|
||||
"@dbeaver/builder": "^0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dbeaver/core": "^0.1.0",
|
||||
"mobx": "^5.15.4",
|
||||
"mobx-react": "^6.2.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"reakit": "^1.0.0-beta.11",
|
||||
"reshadow": "0.0.1-alpha.77",
|
||||
"rxjs": "^6.5.5"
|
||||
"@dbeaver/core": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"declarationDir": "dist/types",
|
||||
"declaration": true,
|
||||
"noEmit": false,
|
||||
"outDir": "dist/js"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"@dbeaver/**"
|
||||
],
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declarationDir": "dist",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"administration_menu_enter": "Administration",
|
||||
"administration_menu_back": "Back to app"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"administration_menu_enter": "Администрирование",
|
||||
"administration_menu_back": "Вернуться в приложение"
|
||||
}
|
||||
@@ -3,25 +3,18 @@
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"license": "Apache-2.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "npm run clean-dist && builder build-plugin",
|
||||
"build": "yarn run clean-dist && builder build-plugin",
|
||||
"serve-plugin": "builder serve-plugin",
|
||||
"clean-dist": "rimraf dist/*",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dbeaver/core": "^0.1.0"
|
||||
"@dbeaver/core": "^0.1.0",
|
||||
"@dbeaver/builder": "^0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dbeaver/core": "^0.1.0",
|
||||
"mobx": "^5.15.4",
|
||||
"mobx-react": "^6.2.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"reakit": "^1.0.0-beta.11",
|
||||
"reshadow": "0.0.1-alpha.77",
|
||||
"rxjs": "^6.5.5"
|
||||
"@dbeaver/core": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { TabsState, TabList, verticalTabStyles } from '@dbeaver/core/blocks';
|
||||
import { useController } from '@dbeaver/core/di';
|
||||
import { composes, useStyles } from '@dbeaver/core/theming';
|
||||
|
||||
import { AdministrationController } from './AdministrationController';
|
||||
import { DrawerItem } from './DrawerItem';
|
||||
import { ItemContent } from './ItemContent';
|
||||
|
||||
const tabsStyles = composes(
|
||||
css`
|
||||
TabList {
|
||||
composes: theme-background-surface theme-text-on-surface theme-border-color-background from global;
|
||||
}
|
||||
Tab {
|
||||
composes: theme-ripple theme-background-background theme-ripple-selectable from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
Tab {
|
||||
color: inherit;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
const administrationStyles = composes(
|
||||
css`
|
||||
container {
|
||||
composes: theme-background-surface theme-text-on-surface from global;
|
||||
}
|
||||
drawer {
|
||||
composes: theme-border-color-background from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
drawer {
|
||||
width: 250px;
|
||||
padding-top: 16px;
|
||||
border-right: 2px solid;
|
||||
}
|
||||
content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
type Props = {
|
||||
activeItem: string | null;
|
||||
activeItemSub: string | null;
|
||||
activeItemSubParam: string | null;
|
||||
onItemSelect(name: string): void;
|
||||
}
|
||||
|
||||
export const Administration = observer(function Administration({
|
||||
activeItem, activeItemSub, activeItemSubParam, onItemSelect,
|
||||
}: Props) {
|
||||
const controller = useController(AdministrationController);
|
||||
|
||||
return styled(useStyles(verticalTabStyles, administrationStyles, tabsStyles))(
|
||||
<container as='div'>
|
||||
<TabsState currentTabId={activeItem} orientation='vertical'>
|
||||
<drawer as='div'>
|
||||
<TabList aria-label="Administration items">
|
||||
{controller.items.map(item => (
|
||||
<DrawerItem key={item.name} item={item} onSelect={onItemSelect} style={[verticalTabStyles, tabsStyles]} />
|
||||
))}
|
||||
</TabList>
|
||||
</drawer>
|
||||
<content as='div'>
|
||||
<ItemContent
|
||||
activeItemName={activeItem}
|
||||
activeItemSub={activeItemSub}
|
||||
activeItemSubParam={activeItemSubParam}
|
||||
/>
|
||||
</content>
|
||||
</TabsState>
|
||||
</container>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { computed } from 'mobx';
|
||||
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
|
||||
import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService';
|
||||
|
||||
@injectable()
|
||||
export class AdministrationController {
|
||||
@computed get items() {
|
||||
return this.administrationItemService
|
||||
.items
|
||||
.concat()
|
||||
.sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private administrationItemService: AdministrationItemService
|
||||
) {}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
const styles = css`
|
||||
administration-tools {
|
||||
height: 48px;
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = React.PropsWithChildren<{
|
||||
className?: string;
|
||||
}>
|
||||
|
||||
export function AdministrationTools({ children, className }: Props) {
|
||||
|
||||
return styled(styles)(
|
||||
<administration-tools as='div' className={className}>
|
||||
{children}
|
||||
</administration-tools>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import { AdministrationItemDrawerProps } from '../AdministrationItem/IAdministrationItem';
|
||||
|
||||
export const DrawerItem = observer(function DrawerItem({ item, onSelect, style }: AdministrationItemDrawerProps) {
|
||||
const Component = item.getDrawerComponent();
|
||||
|
||||
return <Component item={item} onSelect={onSelect} style={style}/>;
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import { useService } from '@dbeaver/core/di';
|
||||
|
||||
import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService';
|
||||
|
||||
type Props = {
|
||||
activeItemName: string | null;
|
||||
activeItemSub: string | null;
|
||||
activeItemSubParam: string | null;
|
||||
}
|
||||
|
||||
export const ItemContent = observer(function ItemContent({ activeItemName, activeItemSub, activeItemSubParam }: Props) {
|
||||
const administrationItemService = useService(AdministrationItemService);
|
||||
|
||||
if (!activeItemName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const item = administrationItemService.getItem(activeItemName);
|
||||
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (activeItemSub) {
|
||||
const sub = administrationItemService.getItemSub(item, activeItemSub);
|
||||
if (sub) {
|
||||
const Component = sub.getComponent();
|
||||
|
||||
return <Component item={item} sub={sub} param={activeItemSubParam} />;
|
||||
}
|
||||
}
|
||||
|
||||
const Component = item.getContentComponent();
|
||||
|
||||
return <Component item={item} />;
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable } from 'mobx';
|
||||
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
|
||||
import { IAdministrationItem, IAdministrationItemOptions, IAdministrationItemSubItem } from './IAdministrationItem';
|
||||
|
||||
@injectable()
|
||||
export class AdministrationItemService {
|
||||
@observable items: IAdministrationItem[] = []
|
||||
|
||||
getDefaultItem() {
|
||||
if (this.items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.items[0].name;
|
||||
}
|
||||
|
||||
getItem(name: string): IAdministrationItem | null {
|
||||
const item = this.items.find(item => item.name === name);
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
getItemSub(item: IAdministrationItem, subItem: string): IAdministrationItemSubItem | null {
|
||||
const sub = item.sub.find(sub => sub.name === subItem);
|
||||
if (!sub) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
create(options: IAdministrationItemOptions) {
|
||||
if (this.items.some(item => item.name === options.name)) {
|
||||
throw new Error(`Administration item "${options.name}" already exists`);
|
||||
}
|
||||
|
||||
const item: IAdministrationItem = {
|
||||
...options,
|
||||
sub: options.sub || [],
|
||||
order: options.order || Number.MAX_SAFE_INTEGER,
|
||||
};
|
||||
this.items.push(item);
|
||||
}
|
||||
|
||||
async activate(name: string, itemSub: string | null, param: string | null) {
|
||||
const item = this.getItem(name);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.onActivate) {
|
||||
await item.onActivate();
|
||||
}
|
||||
|
||||
if (itemSub) {
|
||||
const sub = this.getItemSub(item, itemSub);
|
||||
if (sub && sub.onActivate) {
|
||||
await sub.onActivate(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { Style } from '@dbeaver/core/theming';
|
||||
|
||||
export type AdministrationItemDrawerProps = {
|
||||
item: IAdministrationItem;
|
||||
onSelect(id: string): void;
|
||||
style: Style[];
|
||||
}
|
||||
export type AdministrationItemDrawerComponent = React.FunctionComponent<AdministrationItemDrawerProps>
|
||||
|
||||
export type AdministrationItemContentProps = {
|
||||
item: IAdministrationItem;
|
||||
}
|
||||
export type AdministrationItemContentComponent = React.FunctionComponent<AdministrationItemContentProps>
|
||||
|
||||
export type AdministrationItemSubContentProps = {
|
||||
item: IAdministrationItem;
|
||||
sub: IAdministrationItemSubItem;
|
||||
param: string | null;
|
||||
}
|
||||
export type AdministrationItemSubContentComponent = React.FunctionComponent<AdministrationItemSubContentProps>
|
||||
|
||||
export type AdministrationItemEvent = () => Promise<void> | void
|
||||
export type AdministrationItemSubEvent = (param: string | null) => Promise<void> | void
|
||||
|
||||
export interface IAdministrationItemSubItem {
|
||||
name: string;
|
||||
getComponent(): AdministrationItemSubContentComponent;
|
||||
onActivate?: AdministrationItemSubEvent;
|
||||
}
|
||||
|
||||
export interface IAdministrationItemOptions {
|
||||
name: string;
|
||||
order?: number;
|
||||
sub?: IAdministrationItemSubItem[];
|
||||
getDrawerComponent(): AdministrationItemDrawerComponent;
|
||||
getContentComponent(): AdministrationItemContentComponent;
|
||||
onActivate?: AdministrationItemEvent;
|
||||
}
|
||||
|
||||
export interface IAdministrationItem extends IAdministrationItemOptions {
|
||||
order: number;
|
||||
sub: IAdministrationItemSubItem[];
|
||||
}
|
||||
@@ -6,28 +6,45 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { SettingsMenuService } from '@dbeaver/core/app';
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
import { SettingsMenuService, ScreenService, AppScreenService } from '@dbeaver/core/app';
|
||||
import { injectable, Bootstrap } from '@dbeaver/core/di';
|
||||
import { PermissionsService } from '@dbeaver/core/root';
|
||||
|
||||
export const ADMINISTRATION_PERMISSION = 'admin';
|
||||
import { AdministrationScreenService } from './AdministrationScreen/AdministrationScreenService';
|
||||
import { EAdminPermission } from './EAdminPermission';
|
||||
|
||||
@injectable()
|
||||
export class AdministrationMenuService {
|
||||
static administrationMenuToken = 'administrationMenu';
|
||||
export class AdministrationMenuService extends Bootstrap {
|
||||
constructor(
|
||||
private settingsMenuService: SettingsMenuService,
|
||||
private permissionsService: PermissionsService,
|
||||
) { }
|
||||
private screenService: ScreenService,
|
||||
private administrationScreenService: AdministrationScreenService,
|
||||
private appScreenService: AppScreenService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
register() {
|
||||
bootstrap() {
|
||||
this.settingsMenuService.addMenuItem(
|
||||
SettingsMenuService.settingsMenuToken,
|
||||
{
|
||||
id: AdministrationMenuService.administrationMenuToken,
|
||||
id: 'administrationMenuEnter',
|
||||
order: 0,
|
||||
isHidden: () => !this.permissionsService.has(ADMINISTRATION_PERMISSION),
|
||||
isHidden: () => !this.permissionsService.has(EAdminPermission.admin)
|
||||
|| this.screenService.isActive(AdministrationScreenService.screenName),
|
||||
title: 'administration_menu_enter',
|
||||
onClick: () => this.administrationScreenService.navigateToRoot(),
|
||||
}
|
||||
);
|
||||
this.settingsMenuService.addMenuItem(
|
||||
SettingsMenuService.settingsMenuToken,
|
||||
{
|
||||
id: 'administrationMenuBack',
|
||||
order: 0,
|
||||
isHidden: () => !this.screenService.isActive(AdministrationScreenService.screenName),
|
||||
title: 'administration_menu_back',
|
||||
onClick: () => this.appScreenService.navigateToRoot(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useService } from '@dbeaver/core/di';
|
||||
import { Translate } from '@dbeaver/core/localization';
|
||||
import { usePermission } from '@dbeaver/core/root';
|
||||
|
||||
import { Administration } from '../Administration/Administration';
|
||||
import { EAdminPermission } from '../EAdminPermission';
|
||||
import { AdministrationScreenService } from './AdministrationScreenService';
|
||||
import { AdministrationTopAppBar } from './AdministrationTopAppBar/AdministrationTopAppBar';
|
||||
|
||||
export const AdministrationScreen = observer(function AdministrationScreen() {
|
||||
const administrationScreenService = useService(AdministrationScreenService);
|
||||
if (!usePermission(EAdminPermission.admin)) {
|
||||
return <Translate token='root_permission_denied'/>;
|
||||
}
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(item: string) => administrationScreenService.navigateToItem(item),
|
||||
[administrationScreenService]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdministrationTopAppBar />
|
||||
<Administration
|
||||
activeItem={administrationScreenService.activeItem}
|
||||
activeItemSub={administrationScreenService.activeItemSub}
|
||||
activeItemSubParam={administrationScreenService.activeItemSubParam}
|
||||
onItemSelect={handleSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { computed } from 'mobx';
|
||||
|
||||
import { ScreenService, RouterService, AppScreenService } from '@dbeaver/core/app';
|
||||
import { injectable, Bootstrap } from '@dbeaver/core/di';
|
||||
import { PermissionsService } from '@dbeaver/core/root';
|
||||
|
||||
import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService';
|
||||
import { EAdminPermission } from '../EAdminPermission';
|
||||
import { AdministrationScreen } from './AdministrationScreen';
|
||||
|
||||
@injectable()
|
||||
export class AdministrationScreenService extends Bootstrap {
|
||||
|
||||
static screenName = 'administration'
|
||||
static itemRouteName = 'administration.item'
|
||||
static itemSubRouteName = 'administration.item.sub'
|
||||
static itemSubParamRouteName = 'administration.item.sub.param'
|
||||
|
||||
@computed get activeItem(): string | null {
|
||||
if (!this.screenService.isActive(AdministrationScreenService.screenName)) {
|
||||
return null;
|
||||
}
|
||||
return this.routerService.params.item || this.administrationItemService.getDefaultItem();
|
||||
}
|
||||
|
||||
@computed get activeItemSub(): string | null {
|
||||
if (!this.screenService.isActive(AdministrationScreenService.screenName)) {
|
||||
return null;
|
||||
}
|
||||
return this.routerService.params.sub || null;
|
||||
}
|
||||
|
||||
@computed get activeItemSubParam(): string | null {
|
||||
if (!this.screenService.isActive(AdministrationScreenService.screenName)) {
|
||||
return null;
|
||||
}
|
||||
return this.routerService.params.param || null;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private screenService: ScreenService,
|
||||
private routerService: RouterService,
|
||||
private permissionsService: PermissionsService,
|
||||
private appScreenService: AppScreenService,
|
||||
private administrationItemService: AdministrationItemService
|
||||
) {
|
||||
super();
|
||||
this.permissionsService.onUpdate.subscribe(this.handleActivate.bind(this));
|
||||
}
|
||||
|
||||
navigateToRoot() {
|
||||
this.routerService.router.navigate(AdministrationScreenService.screenName);
|
||||
}
|
||||
|
||||
navigateToItem(item: string) {
|
||||
this.routerService.router.navigate(AdministrationScreenService.itemRouteName, { item });
|
||||
}
|
||||
|
||||
navigateToItemSub(item: string, sub: string, param?: string) {
|
||||
if (!param) {
|
||||
this.routerService.router.navigate(AdministrationScreenService.itemSubRouteName, { item, sub });
|
||||
return;
|
||||
}
|
||||
this.routerService.router.navigate(AdministrationScreenService.itemSubParamRouteName, { item, sub, param });
|
||||
}
|
||||
|
||||
bootstrap() {
|
||||
this.screenService.create({
|
||||
name: AdministrationScreenService.screenName,
|
||||
routes: [
|
||||
{
|
||||
name: AdministrationScreenService.screenName,
|
||||
path: '/admin',
|
||||
},
|
||||
{
|
||||
name: AdministrationScreenService.itemRouteName,
|
||||
path: '/:item',
|
||||
},
|
||||
{
|
||||
name: AdministrationScreenService.itemSubRouteName,
|
||||
path: '/:sub',
|
||||
},
|
||||
{
|
||||
name: AdministrationScreenService.itemSubParamRouteName,
|
||||
path: '/:param',
|
||||
},
|
||||
],
|
||||
component: AdministrationScreen,
|
||||
onActivate: this.handleActivate.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
private async handleActivate() {
|
||||
if (!this.permissionsService.has(EAdminPermission.admin)) {
|
||||
this.appScreenService.navigateToRoot();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.activeItem) {
|
||||
await this.administrationItemService.activate(this.activeItem, this.activeItemSub, this.activeItemSubParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { Placeholder, TopAppBar } from '@dbeaver/core/blocks';
|
||||
import { useService } from '@dbeaver/core/di';
|
||||
|
||||
import { AdministrationTopAppBarService } from './AdministrationTopAppBarService';
|
||||
|
||||
export function AdministrationTopAppBar() {
|
||||
const administrationTopAppBarService = useService(AdministrationTopAppBarService);
|
||||
|
||||
return (
|
||||
<TopAppBar>
|
||||
<Placeholder container={administrationTopAppBarService.placeholder} />
|
||||
</TopAppBar>
|
||||
);
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { SettingsMenu, Logo } from '@dbeaver/core/app';
|
||||
import { PlaceholderContainer } from '@dbeaver/core/blocks';
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
|
||||
import { Fill } from './Fill';
|
||||
|
||||
@injectable()
|
||||
export class AdministrationTopAppBarService {
|
||||
readonly placeholder = new PlaceholderContainer();
|
||||
|
||||
constructor() {
|
||||
this.placeholder.add(Logo, 0);
|
||||
this.placeholder.add(Fill, 3);
|
||||
this.placeholder.add(SettingsMenu, 4);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
const styles = css`
|
||||
fill {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export function Fill() {
|
||||
return styled(styles)(<fill as='div' />);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
|
||||
import { AdministrationMenuService } from './AdministrationMenuService';
|
||||
|
||||
@injectable()
|
||||
export class Bootstrap {
|
||||
constructor(private administrationMenuService: AdministrationMenuService) { }
|
||||
|
||||
bootstrap() {
|
||||
this.administrationMenuService.register();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export enum EAdminPermission {
|
||||
admin = 'admin'
|
||||
}
|
||||
@@ -2,4 +2,9 @@ import { manifest } from './manifest';
|
||||
|
||||
export default manifest;
|
||||
|
||||
export * from './Administration/AdministrationTools/AdministrationTools';
|
||||
export * from './AdministrationItem/AdministrationItemService';
|
||||
export * from './AdministrationItem/IAdministrationItem';
|
||||
export * from './AdministrationScreen/AdministrationScreenService';
|
||||
export * from './AdministrationScreen/AdministrationTopAppBar/AdministrationTopAppBarService';
|
||||
export * from './AdministrationMenuService';
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
import { IServiceInjector, PluginManifest } from '@dbeaver/core/di';
|
||||
|
||||
import { AdministrationItemService } from './AdministrationItem/AdministrationItemService';
|
||||
import { AdministrationMenuService } from './AdministrationMenuService';
|
||||
import { Bootstrap } from './Bootstrap';
|
||||
import { AdministrationScreenService } from './AdministrationScreen/AdministrationScreenService';
|
||||
import { AdministrationTopAppBarService } from './AdministrationScreen/AdministrationTopAppBar/AdministrationTopAppBarService';
|
||||
|
||||
export const manifest: PluginManifest = {
|
||||
info: {
|
||||
@@ -18,11 +20,11 @@ export const manifest: PluginManifest = {
|
||||
|
||||
providers: [
|
||||
AdministrationMenuService,
|
||||
AdministrationScreenService,
|
||||
AdministrationTopAppBarService,
|
||||
AdministrationItemService,
|
||||
],
|
||||
|
||||
async initialize(services: IServiceInjector) {
|
||||
services
|
||||
.resolveServiceByClass(Bootstrap)
|
||||
.bootstrap();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"declarationDir": "dist/types",
|
||||
"declaration": true,
|
||||
"noEmit": false,
|
||||
"outDir": "dist/js"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"@dbeaver/**"
|
||||
],
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declarationDir": "dist",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
|
||||
@@ -3,31 +3,26 @@
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"license": "Apache-2.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "npm run clean-dist && builder build-plugin",
|
||||
"build": "yarn run clean-dist && builder build-plugin",
|
||||
"serve-plugin": "builder serve-plugin",
|
||||
"clean-dist": "rimraf dist/*",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ag-grid-community/all-modules": "^23.0.2",
|
||||
"@ag-grid-community/core": "^23.0.2",
|
||||
"ag-grid-community": "^23.0.2",
|
||||
"ag-grid-react": "^23.0.3"
|
||||
"@ag-grid-community/all-modules": "^23.1.1",
|
||||
"@ag-grid-community/core": "^23.1.1",
|
||||
"ag-grid-community": "^23.1.1",
|
||||
"ag-grid-react": "^23.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dbeaver/core": "^0.1.0"
|
||||
"@dbeaver/core": "^0.1.0",
|
||||
"@dbeaver/builder": "^0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dbeaver/core": "^0.1.0",
|
||||
"mobx": "^5.15.4",
|
||||
"mobx-react": "^6.2.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"reakit": "^1.0.0-beta.11",
|
||||
"reshadow": "0.0.1-alpha.77",
|
||||
"rxjs": "^6.5.5"
|
||||
"react": "^16.3.0",
|
||||
"react-dom": "^16.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
GridOptions,
|
||||
CellEditingStoppedEvent,
|
||||
} from 'ag-grid-community';
|
||||
import { SortChangedEvent } from 'ag-grid-community/dist/lib/events';
|
||||
import { RowDataTransaction } from 'ag-grid-community/dist/lib/interfaces/rowDataTransaction';
|
||||
import { computed, observable } from 'mobx';
|
||||
|
||||
@@ -24,11 +25,12 @@ import { injectable, IInitializableController, IDestructibleController } from '@
|
||||
|
||||
import { AgGridContext } from './AgGridContext';
|
||||
import {
|
||||
AgGridRow, IAgGridActions, IAgGridCol, IAgGridModel,
|
||||
AgGridRow, IAgGridActions, IAgGridCol, IAgGridModel, SortMode, SortModel,
|
||||
} from './IAgGridModel';
|
||||
import { RowSelection } from './TableSelection/RowSelection';
|
||||
import { TableSelection } from './TableSelection/TableSelection';
|
||||
|
||||
|
||||
@injectable()
|
||||
export class AgGridTableController implements IInitializableController, IDestructibleController {
|
||||
@observable refreshId = 0;
|
||||
@@ -68,6 +70,7 @@ export class AgGridTableController implements IInitializableController, IDestruc
|
||||
onBodyScroll: this.handleBodyScroll.bind(this),
|
||||
|
||||
onCellEditingStopped: this.handleCellEditingStopped.bind(this),
|
||||
onSortChanged: this.handleSortChanged.bind(this),
|
||||
};
|
||||
|
||||
@observable columns: ColDef[] = [];
|
||||
@@ -75,7 +78,7 @@ export class AgGridTableController implements IInitializableController, IDestruc
|
||||
/**
|
||||
* use this object to dynamically change ag-grid properties
|
||||
*/
|
||||
@computed get dynamicOptions() {
|
||||
@computed get dynamicOptions(): GridOptions {
|
||||
return {
|
||||
enableRangeSelection: !!this.selection,
|
||||
};
|
||||
@@ -128,15 +131,22 @@ export class AgGridTableController implements IInitializableController, IDestruc
|
||||
startRow,
|
||||
endRow,
|
||||
successCallback,
|
||||
sortModel,
|
||||
failCallback,
|
||||
} = params;
|
||||
|
||||
try {
|
||||
const length = endRow - startRow;
|
||||
const requestedData = await this.gridModel.onRequestData(startRow, length);
|
||||
const requestedData = await this.gridModel.onRequestData(
|
||||
startRow,
|
||||
length,
|
||||
{
|
||||
sorting: sortModel as SortModel,
|
||||
}
|
||||
);
|
||||
// update columns only once after first data fetching
|
||||
if (!this.columns.length) {
|
||||
this.columns = mapDataToColumns(requestedData.columns || []);
|
||||
if (isColumnsChanged(this.columns, requestedData.columns)) {
|
||||
this.columns = mapDataToColumns(requestedData.columns);
|
||||
}
|
||||
successCallback(
|
||||
this.cloneRows(requestedData.rows),
|
||||
@@ -190,16 +200,22 @@ export class AgGridTableController implements IInitializableController, IDestruc
|
||||
this.setInitialRow(this.gridModel.initialRows);
|
||||
}
|
||||
|
||||
private handleSortChanged(event: SortChangedEvent) {
|
||||
if (this.gridModel.onSortChanged) {
|
||||
const sortModel = event.api.getSortModel() as SortModel;
|
||||
this.gridModel.onSortChanged(sortModel);
|
||||
}
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
|
||||
private resetData(columns?: IAgGridCol[], rows?: AgGridRow[]): void {
|
||||
this.selection.clear();
|
||||
if (this.api) {
|
||||
// only purgeInfiniteCache() doesn't work when cache is empty.
|
||||
// probably it thinks that nothing to delete - nothing to refresh
|
||||
this.api.refreshInfiniteCache(); // it will mark internal state for reload
|
||||
this.api.purgeInfiniteCache(); // it will reset internal state
|
||||
this.columns = columns ? mapDataToColumns(columns) : [];
|
||||
if (columns) {
|
||||
this.columns = mapDataToColumns(columns);
|
||||
}
|
||||
this.setInitialRow(rows);
|
||||
}
|
||||
}
|
||||
@@ -212,7 +228,7 @@ export class AgGridTableController implements IInitializableController, IDestruc
|
||||
addIndex: 0,
|
||||
add: initialRows || [],
|
||||
};
|
||||
this.api!.updateRowData(transaction);
|
||||
this.api!.applyTransaction(transaction);
|
||||
}
|
||||
|
||||
private updateCellValue(rowNumber: number, colNumber: number, value: any): void {
|
||||
@@ -257,16 +273,18 @@ export const INDEX_COLUMN_DEF: ColDef = {
|
||||
suppressNavigable: true,
|
||||
suppressMenu: true,
|
||||
editable: false,
|
||||
sortable: false,
|
||||
cellRenderer: row => row.rowIndex + 1,
|
||||
};
|
||||
|
||||
function mapDataToColumns(columns: IAgGridCol[]): ColDef[] {
|
||||
if (!columns.length) {
|
||||
function mapDataToColumns(columns?: IAgGridCol[]): ColDef[] {
|
||||
if (!columns || !columns.length) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
INDEX_COLUMN_DEF,
|
||||
...columns.map(v => ({
|
||||
colId: v.name,
|
||||
headerName: v.label,
|
||||
field: `${v.position}`,
|
||||
valueGetter: v.dataKind === 'OBJECT' ? getObjectValue : undefined,
|
||||
@@ -280,3 +298,11 @@ function mapDataToColumns(columns: IAgGridCol[]): ColDef[] {
|
||||
function getObjectValue({ data, colDef }: ValueGetterParams) {
|
||||
return data[colDef.field || 'node.id'].value;
|
||||
}
|
||||
|
||||
function isColumnsChanged(oldColumns: ColDef[], newColumns: IAgGridCol[] = []): boolean {
|
||||
const [indexColumn, ...withoutIndexColumn] = oldColumns;
|
||||
if (withoutIndexColumn.length !== newColumns.length) {
|
||||
return true;
|
||||
}
|
||||
return withoutIndexColumn.some((col, ind) => col.colId !== newColumns[ind].name);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,18 @@ import { RowSelection } from './TableSelection/RowSelection';
|
||||
|
||||
export type AgGridRow = any[];
|
||||
|
||||
export type SortMode = 'asc' | 'desc' | null;
|
||||
|
||||
export type SortModel = {
|
||||
colId: string;
|
||||
sort: SortMode;
|
||||
}[];
|
||||
|
||||
|
||||
export interface IRequestDataOptions {
|
||||
sorting?: SortModel;
|
||||
}
|
||||
|
||||
|
||||
export interface IAgGridModel {
|
||||
initialRows: AgGridRow[];
|
||||
@@ -19,8 +31,9 @@ export interface IAgGridModel {
|
||||
|
||||
actions: IAgGridActions | null;
|
||||
// hooks
|
||||
onRequestData(rowOffset: number, count: number): Promise<IRequestedData>;
|
||||
onRequestData(rowOffset: number, count: number, options?: IRequestDataOptions): Promise<IRequestedData>;
|
||||
onCellEditingStopped?(rowNumber: number, colNumber: number, value: any): void;
|
||||
onSortChanged?(sorting: SortModel): void;
|
||||
onEditSave(): void;
|
||||
onEditCancel(): void;
|
||||
}
|
||||
@@ -36,6 +49,7 @@ export interface IAgGridActions {
|
||||
export interface IAgGridCol {
|
||||
icon?: string;
|
||||
label?: string;
|
||||
name?: string;
|
||||
position?: number;
|
||||
dataKind?: string;
|
||||
}
|
||||
|
||||
+63
-4
@@ -6,11 +6,13 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import styled, { css, use } from 'reshadow';
|
||||
|
||||
import { AgEvent, GridApi, Column } from '@ag-grid-community/core';
|
||||
import { StaticImage } from '@dbeaver/core/blocks';
|
||||
import { StaticImage, Icon } from '@dbeaver/core/blocks';
|
||||
|
||||
import { SortMode } from '../IAgGridModel';
|
||||
|
||||
type HeaderProps = {
|
||||
api: GridApi;
|
||||
@@ -21,7 +23,7 @@ type HeaderProps = {
|
||||
menuIcon: string;
|
||||
displayName: string;
|
||||
showColumnMenu: (button?: HTMLDivElement) => void;
|
||||
setSort: (order: string, shiftKey: boolean) => void;
|
||||
setSort: (order: SortMode, shiftKey: boolean) => void;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
@@ -42,6 +44,28 @@ const headerStyles = css`
|
||||
margin-left: 8px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
sort-icon {
|
||||
margin-left: 4px;
|
||||
display: flex;
|
||||
padding: 2px 4px;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
}
|
||||
sort-icon > Icon {
|
||||
width: 8px;
|
||||
fill: #cbcbcb;
|
||||
}
|
||||
sort-icon > Icon:last-child {
|
||||
transform: scaleY(-1);
|
||||
}
|
||||
sort-icon > Icon[|active] {
|
||||
fill: #338ECC;
|
||||
}
|
||||
sort-icon:hover > Icon {
|
||||
width: 9px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -63,12 +87,47 @@ export function TableColumnHeader(props: HeaderProps) {
|
||||
props.api.dispatchEvent(event);
|
||||
}, []);
|
||||
|
||||
const [sortMode, setSortMode] = useState<SortMode>(() => props.column.getSort() as SortMode);
|
||||
useEffect(() => {
|
||||
function onSortChanged() {
|
||||
setSortMode(props.column.getSort() as SortMode);
|
||||
}
|
||||
|
||||
props.column.addEventListener('sortChanged', onSortChanged);
|
||||
return function cleanup() {
|
||||
props.column.removeEventListener('sortChanged', onSortChanged);
|
||||
};
|
||||
});
|
||||
|
||||
const handleSort = useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const sort = props.column.getSort() as SortMode;
|
||||
let nextSort: SortMode;
|
||||
switch (sort) {
|
||||
case 'asc':
|
||||
nextSort = 'desc';
|
||||
break;
|
||||
case 'desc':
|
||||
nextSort = null;
|
||||
break;
|
||||
default:
|
||||
nextSort = 'asc';
|
||||
}
|
||||
props.setSort(nextSort, e.ctrlKey);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return styled(headerStyles)(
|
||||
<table-header as="div" onClick={handleClick}>
|
||||
<icon as="div">
|
||||
<StaticImage icon={props.icon} />
|
||||
</icon>
|
||||
<name as="div">{props.displayName}</name>
|
||||
{props.enableSorting && <sort-icon as="div" onClick={handleSort}>
|
||||
<Icon name="sort-arrow" viewBox="0 0 6 6" {...use({ active: sortMode === 'asc' })} />
|
||||
<Icon name="sort-arrow" viewBox="0 0 6 6" {...use({ active: sortMode === 'desc' })} />
|
||||
</sort-icon>}
|
||||
</table-header>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"declarationDir": "dist/types",
|
||||
"declaration": true,
|
||||
"noEmit": false,
|
||||
"outDir": "dist/js"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"@dbeaver/core/**"
|
||||
],
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declarationDir": "dist",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"authentication_administration_item": "Users",
|
||||
"authentication_administration_confirm_user_deletion": "Confirm user deletion",
|
||||
"authentication_create_user_dialog_title": "Create new user",
|
||||
"authentication_create_user_dialog_create": "Create",
|
||||
"authentication_user_name": "User name",
|
||||
"authentication_user_password": "User password",
|
||||
"authentication_user_password_repeat": "Repeat password",
|
||||
"authentication_user_role": "User role",
|
||||
"authentication_user_passwords_not_match": "Passwords don't match",
|
||||
"authentication_user_user_created": "User was created successfully"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"authentication_administration_item": "Пользователи",
|
||||
"authentication_create_user_dialog_title": "Создание нового пользователя",
|
||||
"authentication_create_user_dialog_create": "Создать",
|
||||
"authentication_user_name": "Имя",
|
||||
"authentication_user_password": "Пароль",
|
||||
"authentication_user_password_repeat": "Повторите пароль",
|
||||
"authentication_user_role": "Роль",
|
||||
"authentication_user_passwords_not_match": "Пароли не совпадают",
|
||||
"authentication_user_user_created": "Пользователь создан успешно"
|
||||
}
|
||||
@@ -3,25 +3,20 @@
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"license": "Apache-2.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "npm run clean-dist && builder build-plugin",
|
||||
"build": "yarn run clean-dist && builder build-plugin",
|
||||
"serve-plugin": "builder serve-plugin",
|
||||
"clean-dist": "rimraf dist/*",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dbeaver/administration": "^0.1.0",
|
||||
"@dbeaver/builder": "^0.1.0",
|
||||
"@dbeaver/core": "^0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dbeaver/core": "^0.1.0",
|
||||
"mobx": "^5.15.4",
|
||||
"mobx-react": "^6.2.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"reakit": "^1.0.0-beta.11",
|
||||
"reshadow": "0.0.1-alpha.76",
|
||||
"rxjs": "^6.5.5"
|
||||
"@dbeaver/administration": "^0.1.0",
|
||||
"@dbeaver/core": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path fill="#F0840E" d="M9 0c2.485 0 4.5 2.015 4.5 4.5S11.485 9 9 9 4.5 6.985 4.5 4.5 6.515 0 9 0zm0 2.25c-1.243 0-2.25 1.007-2.25 2.25S7.757 6.75 9 6.75s2.25-1.007 2.25-2.25S10.243 2.25 9 2.25zm0 7.875c3.004 0 9 1.496 9 4.5V18H0v-3.375c0-3.004 5.996-4.5 9-4.5zm0 2.137c-3.341 0-6.862 1.643-6.862 2.363v1.238h13.725v-1.238c0-.72-3.522-2.363-6.863-2.363z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 452 B |
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { AdministrationItemService } from '@dbeaver/administration';
|
||||
import { injectable, Bootstrap } from '@dbeaver/core/di';
|
||||
|
||||
@injectable()
|
||||
export class AuthAdministrationService extends Bootstrap {
|
||||
constructor(
|
||||
private administrationItemService: AdministrationItemService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
bootstrap() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
import {
|
||||
CachedResource, GraphQLService, AdminRoleInfo
|
||||
} from '@dbeaver/core/sdk';
|
||||
|
||||
@injectable()
|
||||
export class RolesManagerService {
|
||||
readonly roles = new CachedResource(
|
||||
[],
|
||||
this.refreshAsync.bind(this),
|
||||
data => !!data.length
|
||||
)
|
||||
constructor(
|
||||
private graphQLService: GraphQLService,
|
||||
) {
|
||||
}
|
||||
|
||||
private async refreshAsync(
|
||||
data: AdminRoleInfo[],
|
||||
_: any,
|
||||
update: boolean,
|
||||
roleId?: string
|
||||
): Promise<AdminRoleInfo[]> {
|
||||
const { roles } = await this.graphQLService.gql.getRolesList({ roleId });
|
||||
|
||||
if (!roleId) {
|
||||
return roles as AdminRoleInfo[];
|
||||
}
|
||||
|
||||
const index = data.findIndex(role => role.roleId === roleId);
|
||||
if (index !== -1) {
|
||||
data.splice(index, 1, ...roles as AdminRoleInfo[]);
|
||||
} else {
|
||||
data.push(...roles as AdminRoleInfo[]);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import {
|
||||
InputField, SubmittingForm, ErrorMessage, Button
|
||||
} from '@dbeaver/core/blocks';
|
||||
import { useController } from '@dbeaver/core/di';
|
||||
import { useTranslate } from '@dbeaver/core/localization';
|
||||
import { useStyles, composes } from '@dbeaver/core/theming';
|
||||
|
||||
import { CreateUserController } from './CreateUserController';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
`,
|
||||
css`
|
||||
SubmittingForm {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
create-form {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
group {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
margin: 0 12px;
|
||||
}
|
||||
controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
item-title {
|
||||
composes: theme-typography--headline5 from global;
|
||||
padding: 16px;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
export const CreateUser = observer(function CreateUser() {
|
||||
const controller = useController(CreateUserController);
|
||||
const translate = useTranslate();
|
||||
const handleChange = useCallback(
|
||||
(value: string) => controller.login = value,
|
||||
[]
|
||||
);
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<>
|
||||
<item-title as='div'>
|
||||
User creation
|
||||
</item-title>
|
||||
<SubmittingForm>
|
||||
<create-form as='div'>
|
||||
<group as="div">
|
||||
<InputField
|
||||
type='text'
|
||||
name='login'
|
||||
value={controller.login}
|
||||
onChange={handleChange}
|
||||
disabled={controller.isCreating}
|
||||
mod='surface'
|
||||
>
|
||||
{translate('User name')}
|
||||
</InputField>
|
||||
</group>
|
||||
<controls as="div">
|
||||
<Button
|
||||
type="button"
|
||||
mod={['unelevated']}
|
||||
onClick={controller.create}
|
||||
disabled={controller.isCreating}
|
||||
>
|
||||
{translate('Create')}
|
||||
</Button>
|
||||
</controls>
|
||||
</create-form>
|
||||
</SubmittingForm>
|
||||
|
||||
{controller.error.responseMessage && (
|
||||
<ErrorMessage
|
||||
text={controller.error.responseMessage}
|
||||
hasDetails={controller.error.hasDetails}
|
||||
onShowDetails={controller.showDetails}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable } from 'mobx';
|
||||
|
||||
import { AdministrationScreenService } from '@dbeaver/administration';
|
||||
import { ErrorDetailsDialog } from '@dbeaver/core/app';
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
import { CommonDialogService } from '@dbeaver/core/dialogs';
|
||||
import { NotificationService } from '@dbeaver/core/eventsLog';
|
||||
import { GQLErrorCatcher } from '@dbeaver/core/sdk';
|
||||
|
||||
import { UsersManagerService } from '../../UsersManagerService';
|
||||
|
||||
|
||||
@injectable()
|
||||
export class CreateUserController {
|
||||
@observable isCreating = false;
|
||||
@observable login = '';
|
||||
|
||||
readonly error = new GQLErrorCatcher();
|
||||
|
||||
constructor(
|
||||
private notificationService: NotificationService,
|
||||
private administrationScreenService: AdministrationScreenService,
|
||||
private usersManagerService: UsersManagerService,
|
||||
private commonDialogService: CommonDialogService,
|
||||
) { }
|
||||
|
||||
create = async () => {
|
||||
if (this.isCreating) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCreating = true;
|
||||
try {
|
||||
await this.usersManagerService.create(this.login);
|
||||
this.administrationScreenService.navigateToItem('users');
|
||||
} catch (exception) {
|
||||
if (!this.error.catch(exception)) {
|
||||
this.notificationService.logException(exception, 'User create failed');
|
||||
}
|
||||
} finally {
|
||||
this.isCreating = false;
|
||||
}
|
||||
}
|
||||
|
||||
showDetails = () => {
|
||||
if (this.error.exception) {
|
||||
this.commonDialogService.open(ErrorDetailsDialog, this.error.exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import {
|
||||
SubmittingForm, ErrorMessage, InputField, Checkbox
|
||||
} from '@dbeaver/core/blocks';
|
||||
import { useController } from '@dbeaver/core/di';
|
||||
import { DialogComponent, CommonDialogWrapper } from '@dbeaver/core/dialogs';
|
||||
import { useTranslate } from '@dbeaver/core/localization';
|
||||
import { composes, useStyles } from '@dbeaver/core/theming';
|
||||
|
||||
import { CreateUserDialogController } from './CreateUserDialogController';
|
||||
import { CreateUserDialogFooter } from './CreateUserDialogFooter';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
Tab {
|
||||
composes: theme-ripple theme-background-secondary theme-text-on-secondary from global;
|
||||
}
|
||||
ErrorMessage {
|
||||
composes: theme-background-secondary from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
CommonDialogWrapper {
|
||||
min-height: 400px;
|
||||
min-width: 600px;
|
||||
}
|
||||
SubmittingForm {
|
||||
overflow: auto;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
SubmittingForm, create-form {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
create-form {
|
||||
flex-direction: column;
|
||||
padding: 18px 24px;
|
||||
}
|
||||
ErrorMessage {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
padding: 8px 24px;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
export const CreateUserDialog: DialogComponent<null, null> = observer(
|
||||
function CreateUserDialog(props) {
|
||||
const controller = useController(CreateUserDialogController, props.rejectDialog);
|
||||
const translate = useTranslate();
|
||||
const handleLoginChange = useCallback(
|
||||
(value: string) => controller.credentials.login = value,
|
||||
[]
|
||||
);
|
||||
const handlePasswordChange = useCallback(
|
||||
(value: string) => controller.credentials.password = value,
|
||||
[]
|
||||
);
|
||||
const handlePasswordRepeatChange = useCallback(
|
||||
(value: string) => controller.credentials.passwordRepeat = value,
|
||||
[]
|
||||
);
|
||||
const handleRoleChange = useCallback(
|
||||
(roleId: string, value: boolean) => controller.credentials.roles.set(roleId, value),
|
||||
[]
|
||||
);
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<CommonDialogWrapper
|
||||
title={translate('authentication_create_user_dialog_title')}
|
||||
noBodyPadding
|
||||
footer={(
|
||||
<CreateUserDialogFooter
|
||||
isCreating={controller.isCreating}
|
||||
onCancel={props.rejectDialog}
|
||||
onCreate={controller.create}
|
||||
/>
|
||||
)}
|
||||
onReject={props.rejectDialog}
|
||||
>
|
||||
<SubmittingForm onSubmit={controller.create} autoComplete="disabled">
|
||||
<create-form as='div'>
|
||||
<group as="div">
|
||||
<InputField
|
||||
type='text'
|
||||
name='login'
|
||||
value={controller.credentials.login}
|
||||
onChange={handleLoginChange}
|
||||
disabled={controller.isCreating}
|
||||
mod='surface'
|
||||
>
|
||||
{translate('authentication_user_name')}
|
||||
</InputField>
|
||||
</group>
|
||||
<group as="div">
|
||||
<InputField
|
||||
type='password'
|
||||
name='password'
|
||||
value={controller.credentials.password}
|
||||
onChange={handlePasswordChange}
|
||||
disabled={controller.isCreating}
|
||||
mod='surface'
|
||||
>
|
||||
{translate('authentication_user_password')}
|
||||
</InputField>
|
||||
</group>
|
||||
<group as="div">
|
||||
<InputField
|
||||
type='password'
|
||||
name='password_repeat'
|
||||
value={controller.credentials.passwordRepeat}
|
||||
onChange={handlePasswordRepeatChange}
|
||||
disabled={controller.isCreating}
|
||||
mod='surface'
|
||||
>
|
||||
{translate('authentication_user_password_repeat')}
|
||||
</InputField>
|
||||
</group>
|
||||
{controller.roles.map((role, i) => (
|
||||
<group as="div" key={role.roleId}>
|
||||
<Checkbox
|
||||
type='checkbox'
|
||||
name='role'
|
||||
checkboxLabel={role.roleName || role.roleId}
|
||||
onChange={checked => handleRoleChange(role.roleId, checked)}
|
||||
checked={controller.credentials.roles.get(role.roleId)}
|
||||
disabled={controller.isCreating}
|
||||
mod='surface'
|
||||
>
|
||||
{i === 0 && translate('authentication_user_role')}
|
||||
</Checkbox>
|
||||
</group>
|
||||
))}
|
||||
</create-form>
|
||||
</SubmittingForm>
|
||||
{controller.error.responseMessage && (
|
||||
<ErrorMessage
|
||||
text={controller.error.responseMessage}
|
||||
hasDetails={controller.error.hasDetails}
|
||||
onShowDetails={controller.showDetails}
|
||||
/>
|
||||
)}
|
||||
</CommonDialogWrapper>
|
||||
);
|
||||
}
|
||||
);
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable, computed } from 'mobx';
|
||||
|
||||
import { ErrorDetailsDialog } from '@dbeaver/core/app';
|
||||
import { injectable, IInitializableController, IDestructibleController } from '@dbeaver/core/di';
|
||||
import { CommonDialogService } from '@dbeaver/core/dialogs';
|
||||
import { NotificationService } from '@dbeaver/core/eventsLog';
|
||||
import { GQLErrorCatcher } from '@dbeaver/core/sdk';
|
||||
|
||||
import { RolesManagerService } from '../../RolesManagerService';
|
||||
import { UsersManagerService } from '../../UsersManagerService';
|
||||
|
||||
|
||||
@injectable()
|
||||
export class CreateUserDialogController implements IInitializableController, IDestructibleController {
|
||||
@observable isCreating = false;
|
||||
@observable credentials = {
|
||||
login: '',
|
||||
password: '',
|
||||
passwordRepeat: '',
|
||||
roles: new Map<string, boolean>(),
|
||||
};
|
||||
|
||||
@computed get roles() {
|
||||
return this.rolesManagerService.roles.data;
|
||||
}
|
||||
readonly error = new GQLErrorCatcher();
|
||||
private isDistructed = false;
|
||||
private close!: () => void;
|
||||
|
||||
constructor(
|
||||
private notificationService: NotificationService,
|
||||
private usersManagerService: UsersManagerService,
|
||||
private commonDialogService: CommonDialogService,
|
||||
private rolesManagerService: RolesManagerService,
|
||||
) { }
|
||||
|
||||
init(onClose: () => void) {
|
||||
this.close = onClose;
|
||||
this.loadRoles();
|
||||
}
|
||||
|
||||
destruct(): void {
|
||||
this.isDistructed = true;
|
||||
}
|
||||
|
||||
create = async () => {
|
||||
if (this.isCreating) {
|
||||
return;
|
||||
}
|
||||
if (this.credentials.password !== this.credentials.passwordRepeat) {
|
||||
this.notificationService.logError({ title: 'authentication_user_passwords_not_match' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCreating = true;
|
||||
let isUserCreated = false;
|
||||
try {
|
||||
const user = await this.usersManagerService.create(this.credentials.login, false);
|
||||
isUserCreated = !!user;
|
||||
await this.usersManagerService.updateCredentials(user.userId, { password: this.credentials.password });
|
||||
for (const [roleId, checked] of this.credentials.roles) {
|
||||
if (checked) {
|
||||
await this.usersManagerService.grantRole(user.userId, roleId);
|
||||
}
|
||||
}
|
||||
await this.usersManagerService.users.refresh(user.userId);
|
||||
this.notificationService.logInfo({ title: 'authentication_user_user_created' });
|
||||
this.close();
|
||||
} catch (exception) {
|
||||
if (isUserCreated) {
|
||||
await this.deleteUser(this.credentials.login);
|
||||
}
|
||||
if (!this.error.catch(exception) || this.isDistructed) {
|
||||
this.notificationService.logException(exception, 'Error creating new user');
|
||||
}
|
||||
} finally {
|
||||
this.isCreating = false;
|
||||
}
|
||||
}
|
||||
|
||||
showDetails = () => {
|
||||
if (this.error.exception) {
|
||||
this.commonDialogService.open(ErrorDetailsDialog, this.error.exception);
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteUser(userId: string) {
|
||||
try {
|
||||
await this.usersManagerService.delete(userId);
|
||||
} catch (exception) {
|
||||
if (!this.error.catch(exception) || this.isDistructed) {
|
||||
this.notificationService.logException(exception, 'Error deleting partially created user');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async loadRoles() {
|
||||
try {
|
||||
await this.rolesManagerService.roles.load();
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, 'Can\'t load roles');
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { Button } from '@dbeaver/core/blocks';
|
||||
import { useTranslate } from '@dbeaver/core/localization';
|
||||
|
||||
const styles = css`
|
||||
controls {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
margin: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
fill {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export type Props = {
|
||||
isCreating: boolean;
|
||||
onCancel(): void;
|
||||
onCreate(): void;
|
||||
}
|
||||
|
||||
export const CreateUserDialogFooter = observer(
|
||||
function CreateUserDialogFooter({
|
||||
isCreating,
|
||||
onCancel,
|
||||
onCreate,
|
||||
}: Props) {
|
||||
const translate = useTranslate();
|
||||
|
||||
return styled(styles)(
|
||||
<controls as="div">
|
||||
<Button
|
||||
type="button"
|
||||
mod={['outlined']}
|
||||
onClick={onCancel}
|
||||
disabled={isCreating}
|
||||
>
|
||||
{translate('ui_processing_cancel')}
|
||||
</Button>
|
||||
<fill as="div"/>
|
||||
<Button
|
||||
type="button"
|
||||
mod={['unelevated']}
|
||||
onClick={onCreate}
|
||||
disabled={isCreating}
|
||||
>
|
||||
{translate('authentication_create_user_dialog_create')}
|
||||
</Button>
|
||||
</controls>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { AdministrationTools } from '@dbeaver/administration';
|
||||
import { Loader, IconButton } from '@dbeaver/core/blocks';
|
||||
import { useController } from '@dbeaver/core/di';
|
||||
import { useStyles, composes } from '@dbeaver/core/theming';
|
||||
|
||||
import { UsersAdministrationController } from './UsersAdministrationController';
|
||||
import { UsersTable } from './UsersTable/UsersTable';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
AdministrationTools {
|
||||
composes: theme-background-secondary theme-text-on-secondary from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
content {
|
||||
position: relative;
|
||||
padding-top: 16px;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
TableColumnHeader {
|
||||
border-top: solid 1px;
|
||||
}
|
||||
AdministrationTools {
|
||||
display: flex;
|
||||
padding: 0 16px;
|
||||
align-items: center;
|
||||
}
|
||||
IconButton {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
export const UsersAdministration = observer(function UsersAdministration() {
|
||||
const controller = useController(UsersAdministrationController);
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<>
|
||||
<AdministrationTools>
|
||||
<IconButton name="add" viewBox="0 0 28 28" onClick={controller.create} />
|
||||
<IconButton name="trash" viewBox="0 0 28 28" onClick={controller.delete} />
|
||||
<IconButton name="reload" viewBox="0 0 28 28" onClick={controller.update} />
|
||||
</AdministrationTools>
|
||||
<content as='div'>
|
||||
<UsersTable users={controller.users} selectedItems={controller.selectedItems} />
|
||||
{controller.isLoading && <Loader overlay/>}
|
||||
</content>
|
||||
</>
|
||||
);
|
||||
});
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable } from 'mobx';
|
||||
|
||||
import { ErrorDetailsDialog } from '@dbeaver/core/app';
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
import { CommonDialogService, ConfirmationDialog } from '@dbeaver/core/dialogs';
|
||||
import { NotificationService } from '@dbeaver/core/eventsLog';
|
||||
import { GQLErrorCatcher } from '@dbeaver/core/sdk';
|
||||
|
||||
import { UsersManagerService } from '../UsersManagerService';
|
||||
import { CreateUserDialog } from './CreateUserDialog/CreateUserDialog';
|
||||
|
||||
|
||||
@injectable()
|
||||
export class UsersAdministrationController {
|
||||
@observable isDeleting = false;
|
||||
readonly selectedItems = observable<string, boolean>(new Map())
|
||||
readonly error = new GQLErrorCatcher();
|
||||
get users() {
|
||||
return this.usersManagerService.users.data;
|
||||
}
|
||||
get isLoading() {
|
||||
return this.usersManagerService.users.isLoading();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private notificationService: NotificationService,
|
||||
private usersManagerService: UsersManagerService,
|
||||
private commonDialogService: CommonDialogService,
|
||||
) { }
|
||||
|
||||
create = () => {
|
||||
this.commonDialogService.open(CreateUserDialog, null);
|
||||
}
|
||||
|
||||
update = async () => {
|
||||
try {
|
||||
await this.usersManagerService.users.refresh(undefined);
|
||||
} catch (exception) {
|
||||
if (!this.error.catch(exception)) {
|
||||
this.notificationService.logException(exception, 'Users update failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete = async () => {
|
||||
if (this.isDeleting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isDeleting = true;
|
||||
|
||||
try {
|
||||
const deletionList = Array
|
||||
.from(this.selectedItems)
|
||||
.filter(([_, value]) => value)
|
||||
.map(([userId]) => userId);
|
||||
if (deletionList.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await this.commonDialogService.open(ConfirmationDialog, {
|
||||
title: 'authentication_administration_confirm_user_deletion',
|
||||
message: `Would you like to delete users: ${deletionList.join(', ')}`,
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const userId of deletionList) {
|
||||
await this.usersManagerService.delete(userId);
|
||||
}
|
||||
this.selectedItems.clear();
|
||||
await this.usersManagerService.users.refresh(undefined);
|
||||
} catch (exception) {
|
||||
if (!this.error.catch(exception)) {
|
||||
this.notificationService.logException(exception, 'User delete failed');
|
||||
}
|
||||
} finally {
|
||||
this.isDeleting = false;
|
||||
}
|
||||
}
|
||||
|
||||
showDetails = () => {
|
||||
if (this.error.exception) {
|
||||
this.commonDialogService.open(ErrorDetailsDialog, this.error.exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { AdministrationItemService, AdministrationScreenService } from '@dbeaver/administration';
|
||||
import { injectable, Bootstrap } from '@dbeaver/core/di';
|
||||
import { NotificationService } from '@dbeaver/core/eventsLog';
|
||||
|
||||
import { UsersManagerService } from '../UsersManagerService';
|
||||
import { CreateUser } from './CreateUser/CreateUser';
|
||||
import { UsersAdministration } from './UsersAdministration';
|
||||
import { UsersDrawerItem } from './UsersDrawerItem';
|
||||
|
||||
@injectable()
|
||||
export class UsersAdministrationService extends Bootstrap {
|
||||
constructor(
|
||||
private administrationItemService: AdministrationItemService,
|
||||
private administrationScreenService: AdministrationScreenService,
|
||||
private notificationService: NotificationService,
|
||||
private usersManagerService: UsersManagerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
bootstrap() {
|
||||
this.administrationItemService.create({
|
||||
name: 'users',
|
||||
sub: [
|
||||
{
|
||||
name: 'create',
|
||||
getComponent: () => CreateUser,
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
getComponent: () => UsersAdministration,
|
||||
},
|
||||
],
|
||||
getContentComponent: () => UsersAdministration,
|
||||
getDrawerComponent: () => UsersDrawerItem,
|
||||
onActivate: this.loadUsers.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
navToRoot() {
|
||||
this.administrationScreenService.navigateToItem('users');
|
||||
}
|
||||
|
||||
navToCreate() {
|
||||
this.administrationScreenService.navigateToItemSub('users', 'create');
|
||||
}
|
||||
|
||||
navToEdit(userId: string) {
|
||||
this.administrationScreenService.navigateToItemSub('users', 'edit', userId);
|
||||
}
|
||||
|
||||
private async loadUsers() {
|
||||
try {
|
||||
await this.usersManagerService.users.load(undefined);
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, 'Error occurred while loading users');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { AdministrationItemDrawerProps } from '@dbeaver/administration';
|
||||
import { Tab, TabTitle, TabIcon } from '@dbeaver/core/blocks';
|
||||
import { Translate } from '@dbeaver/core/localization';
|
||||
import { useStyles } from '@dbeaver/core/theming';
|
||||
|
||||
export function UsersDrawerItem({ item, onSelect, style }: AdministrationItemDrawerProps) {
|
||||
return styled(useStyles(...style))(
|
||||
<Tab tabId={item.name} onOpen={() => onSelect(item.name)}>
|
||||
<TabIcon icon='/icons/account.svg' />
|
||||
<TabTitle><Translate token='authentication_administration_item'/></TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user