diff --git a/frontend/src/components/single-selector.js b/frontend/src/components/single-selector.js
index 3f6b72288f..73852227fb 100644
--- a/frontend/src/components/single-selector.js
+++ b/frontend/src/components/single-selector.js
@@ -8,6 +8,7 @@ const propTypes = {
currentSelectedOption: PropTypes.object.isRequired,
options: PropTypes.array.isRequired,
selectOption: PropTypes.func.isRequired,
+ operationBeforeSelect: PropTypes.func,
toggleItemFreezed: PropTypes.func
};
@@ -52,7 +53,11 @@ class Selector extends Component {
selectItem = (e, targetItem) => {
e.stopPropagation();
- this.props.selectOption(targetItem);
+ if (this.props.operationBeforeSelect) {
+ this.props.operationBeforeSelect();
+ } else {
+ this.props.selectOption(targetItem);
+ }
this.togglePopover();
};
diff --git a/frontend/src/pages/org-admin/org-user-item.js b/frontend/src/pages/org-admin/org-user-item.js
index 2c6f6f9081..e56bf10a73 100644
--- a/frontend/src/pages/org-admin/org-user-item.js
+++ b/frontend/src/pages/org-admin/org-user-item.js
@@ -6,6 +6,7 @@ import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import toaster from '../../components/toast';
import Selector from '../../components/single-selector';
+import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
const propTypes = {
user: PropTypes.object,
@@ -84,6 +85,11 @@ class UserItem extends React.Component {
this.props.changeStatus(this.props.user.email, isActive);
};
+ setUserInactive = () => {
+ const isActive = false;
+ this.props.changeStatus(this.props.user.email, isActive);
+ };
+
onDropdownToggleClick = (e) => {
e.preventDefault();
this.toggleOperationMenu(e);
@@ -126,8 +132,12 @@ class UserItem extends React.Component {
}
};
+ toggleConfirmInactiveDialog= () => {
+ this.setState({isConfirmInactiveDialogOpen: !this.state.isConfirmInactiveDialogOpen});
+ };
+
render() {
- const { highlight } = this.state;
+ const { highlight, isConfirmInactiveDialogOpen } = this.state;
let { user, currentTab } = this.props;
let href = siteRoot + 'org/useradmin/info/' + encodeURIComponent(user.email) + '/';
let isOperationMenuShow = (user.email !== username) && this.state.showMenu;
@@ -143,47 +153,62 @@ class UserItem extends React.Component {
});
const currentSelectedStatusOption = this.statusOptions.filter(item => item.isSelected)[0];
+ const itemName = '' + Utils.HTMLescape(user.name) + '';
+ const confirmSetUserInactiveMsg = gettext('Are you sure you want to set {user_placeholder} inactive?').replace('{user_placeholder}', itemName);
+
return (
-
-
- {user.name}
- |
-
-
- |
- {`${Utils.formatSize({bytes: user.quota_usage})} / ${this.getQuotaTotal(user.quota_total)}`} |
-
- {user.ctime} /
-
- {user.last_login ? user.last_login : '--'}
- |
-
- {isOperationMenuShow && (
-
-
-
- {gettext('Delete')}
- {gettext('ResetPwd')}
- {currentTab == 'admins' && {gettext('Revoke Admin')}}
-
-
- )}
- |
-
+ <>
+
+
+ {user.name}
+ |
+
+
+ |
+ {`${Utils.formatSize({bytes: user.quota_usage})} / ${this.getQuotaTotal(user.quota_total)}`} |
+
+ {user.ctime} /
+
+ {user.last_login ? user.last_login : '--'}
+ |
+
+ {isOperationMenuShow && (
+
+
+
+ {gettext('Delete')}
+ {gettext('ResetPwd')}
+ {currentTab == 'admins' && {gettext('Revoke Admin')}}
+
+
+ )}
+ |
+
+ {isConfirmInactiveDialogOpen &&
+
+ }
+ >
);
}
}
diff --git a/frontend/src/pages/sys-admin/orgs/org-users.js b/frontend/src/pages/sys-admin/orgs/org-users.js
index b9ca94a92a..b4965de6b9 100644
--- a/frontend/src/pages/sys-admin/orgs/org-users.js
+++ b/frontend/src/pages/sys-admin/orgs/org-users.js
@@ -158,10 +158,18 @@ class Item extends Component {
this.setState({isResetPasswordDialogOpen: !this.state.isResetPasswordDialogOpen});
};
+ toggleConfirmInactiveDialog= () => {
+ this.setState({isConfirmInactiveDialogOpen: !this.state.isConfirmInactiveDialogOpen});
+ };
+
updateStatus= (statusOption) => {
this.props.updateStatus(this.props.item.email, statusOption.value);
};
+ setUserInactive = () => {
+ this.props.updateStatus(this.props.item.email, 'inactive');
+ };
+
updateMembership= (membershipOption) => {
this.props.updateMembership(this.props.item.email, membershipOption.value);
};
@@ -214,11 +222,12 @@ class Item extends Component {
render() {
const { item } = this.props;
- const { highlight, isOpIconShown, isDeleteDialogOpen, isResetPasswordDialogOpen } = this.state;
+ const { highlight, isOpIconShown, isDeleteDialogOpen, isResetPasswordDialogOpen, isConfirmInactiveDialogOpen } = this.state;
const itemName = '' + Utils.HTMLescape(item.name) + '';
let deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', itemName);
let resetPasswordDialogMsg = gettext('Are you sure you want to reset the password of {placeholder} ?').replace('{placeholder}', itemName);
+ const confirmSetUserInactiveMsg = gettext('Are you sure you want to set {user_placeholder} inactive?').replace('{user_placeholder}', itemName);
// for 'user status'
const curStatus = item.active ? 'active' : 'inactive';
@@ -253,6 +262,7 @@ class Item extends Component {
options={this.statusOptions}
selectOption={this.updateStatus}
toggleItemFreezed={this.props.toggleItemFreezed}
+ operationBeforeSelect={item.active ? this.toggleConfirmInactiveDialog : undefined}
/>
@@ -298,6 +308,15 @@ class Item extends Component {
toggleDialog={this.toggleResetPasswordDialog}
/>
}
+ {isConfirmInactiveDialogOpen &&
+
+ }
);
}
diff --git a/frontend/src/pages/sys-admin/users/users-content.js b/frontend/src/pages/sys-admin/users/users-content.js
index 5b616581ff..47b352c317 100644
--- a/frontend/src/pages/sys-admin/users/users-content.js
+++ b/frontend/src/pages/sys-admin/users/users-content.js
@@ -200,7 +200,8 @@ class Item extends Component {
isSetQuotaDialogOpen: false,
isDeleteUserDialogOpen: false,
isResetUserPasswordDialogOpen: false,
- isRevokeAdminDialogOpen: false
+ isRevokeAdminDialogOpen: false,
+ isConfirmInactiveDialogOpen: false
};
}
@@ -246,6 +247,10 @@ class Item extends Component {
this.setState({isRevokeAdminDialogOpen: !this.state.isRevokeAdminDialogOpen});
};
+ toggleConfirmInactiveDialog= () => {
+ this.setState({isConfirmInactiveDialogOpen: !this.state.isConfirmInactiveDialogOpen});
+ };
+
onUserSelected = () => {
this.props.onUserSelected(this.props.item);
};
@@ -258,6 +263,10 @@ class Item extends Component {
this.props.updateUser(this.props.item.email, 'is_active', isActive);
};
+ setUserInactive = () => {
+ this.props.updateUser(this.props.item.email, 'is_active', false);
+ };
+
updateRole = (roleOption) => {
this.props.updateUser(this.props.item.email, 'role', roleOption.value);
};
@@ -386,13 +395,15 @@ class Item extends Component {
isSetQuotaDialogOpen,
isDeleteUserDialogOpen,
isResetUserPasswordDialogOpen,
- isRevokeAdminDialogOpen
+ isRevokeAdminDialogOpen,
+ isConfirmInactiveDialogOpen
} = this.state;
const itemName = '' + Utils.HTMLescape(item.name) + '';
const deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', itemName);
const resetPasswordDialogMsg = gettext('Are you sure you want to reset the password of {placeholder} ?').replace('{placeholder}', itemName);
const revokeAdminDialogMsg = gettext('Are you sure you want to revoke the admin permission of {placeholder} ?').replace('{placeholder}', itemName);
+ const confirmSetUserInactiveMsg = gettext('Are you sure you want to set {user_placeholder} inactive?').replace('{user_placeholder}', itemName);
// for 'user status'
const curStatus = item.is_active ? 'active' : 'inactive';
@@ -473,6 +484,7 @@ class Item extends Component {
options={this.statusOptions}
selectOption={this.updateStatus}
toggleItemFreezed={this.props.toggleItemFreezed}
+ operationBeforeSelect={item.is_active ? this.toggleConfirmInactiveDialog : undefined}
/>
|
{isPro &&
@@ -567,6 +579,15 @@ class Item extends Component {
toggleDialog={this.toggleRevokeAdminDialog}
/>
}
+ {isConfirmInactiveDialogOpen &&
+
+ }
);
}
diff --git a/seahub/api2/endpoints/admin/account.py b/seahub/api2/endpoints/admin/account.py
index a26d7f1e01..37fcd9902a 100644
--- a/seahub/api2/endpoints/admin/account.py
+++ b/seahub/api2/endpoints/admin/account.py
@@ -282,6 +282,13 @@ class Account(APIView):
'is_active invalid.')
user.is_active = is_active
+ if is_active == False:
+ # del tokens and personal repo api tokens (not department)
+ from seahub.utils import inactive_user
+ try:
+ inactive_user(email)
+ except Exception as e:
+ logger.error("Failed to inactive_user %s: %s." % (email, e))
# update password
password = request.data.get("password", None)
diff --git a/seahub/api2/endpoints/admin/users.py b/seahub/api2/endpoints/admin/users.py
index 27d5e9f6c4..f85ceacf6d 100644
--- a/seahub/api2/endpoints/admin/users.py
+++ b/seahub/api2/endpoints/admin/users.py
@@ -355,9 +355,18 @@ def update_user_info(request, user, password, is_active, is_staff, role,
quota_total_mb, institution_name,
upload_rate_limit, download_rate_limit):
+ email = user.username
+
# update basic user info
if is_active is not None:
user.is_active = is_active
+ if is_active == False:
+ # del tokens and personal repo api tokens (not department)
+ from seahub.utils import inactive_user
+ try:
+ inactive_user(email)
+ except Exception as e:
+ logger.error("Failed to inactive_user %s: %s." % (email, e))
if password:
user.set_password(password)
@@ -368,8 +377,6 @@ def update_user_info(request, user, password, is_active, is_staff, role,
# update user
user.save()
- email = user.username
-
# update additional user info
if is_pro_version() and role:
User.objects.update_role(email, role)
diff --git a/seahub/organizations/api/admin/users.py b/seahub/organizations/api/admin/users.py
index 36dc38f74b..052700f355 100644
--- a/seahub/organizations/api/admin/users.py
+++ b/seahub/organizations/api/admin/users.py
@@ -442,6 +442,13 @@ class OrgAdminUser(APIView):
user.is_active = is_active == 'true'
user.save()
+ if not is_active == 'true':
+ # del tokens and personal repo api tokens (not department)
+ from seahub.utils import inactive_user
+ try:
+ inactive_user(email)
+ except Exception as e:
+ logger.error("Failed to inactive_user %s: %s." % (email, e))
# update quota_total
quota_total_mb = request.data.get("quota_total", None)
diff --git a/seahub/utils/__init__.py b/seahub/utils/__init__.py
index 760dbb821f..e192ac1ce4 100644
--- a/seahub/utils/__init__.py
+++ b/seahub/utils/__init__.py
@@ -18,7 +18,7 @@ from urllib.parse import urlparse, urljoin
from constance import config
import seaserv
-from seaserv import seafile_api
+from seaserv import seafile_api, ccnet_api
from django.urls import reverse
from django.core.mail import EmailMessage
@@ -1280,6 +1280,38 @@ def clear_token(username):
TokenV2.objects.filter(user = username).delete()
seafile_api.delete_repo_tokens_by_email(username)
+
+def inactive_user(username):
+ # del tokens and personal repo api tokens (not department)
+ from seahub.repo_api_tokens.models import RepoAPITokens
+ try:
+ clear_token(username)
+ except Exception as e:
+ logger.error("Failed to delete tokens for user %s: %s." % (username, e))
+ try:
+ from seahub.settings import MULTI_TENANCY
+ except ImportError:
+ MULTI_TENANCY = False
+ try:
+ org_id = -1
+ if MULTI_TENANCY:
+ orgs = ccnet_api.get_orgs_by_user(username)
+ if orgs:
+ org = orgs[0]
+ org_id = org.org_id
+ if org_id > 0:
+ owned_repos = seafile_api.get_org_owned_repo_list(
+ org_id, username, ret_corrupted=True)
+ else:
+ owned_repos = seafile_api.get_owned_repo_list(
+ username, ret_corrupted=True)
+ owned_repo_ids = [item.repo_id for item in owned_repos]
+ RepoAPITokens.objects.filter(
+ repo_id__in=owned_repo_ids).delete()
+ except Exception as e:
+ logger.error("Failed to delete repo api tokens for user %s: %s." % (username, e))
+
+
def send_perm_audit_msg(etype, from_user, to, repo_id, path, perm):
"""Send repo permission audit msg.