1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-09 10:50:24 +00:00

update inactive user (#6019)

* update inactive user

* is_active del RepoAPITokens

* update del_repo_api_token

* add inactive_user

* [user admin] add confirmation to 'set user inactive'

* system admin - users
* system admin - organizations - org - member
* org admin - users

---------

Co-authored-by: llj <lingjun.li1@gmail.com>
This commit is contained in:
欢乐马
2024-04-23 16:26:31 +08:00
committed by GitHub
parent aa40b202d1
commit cd7329e711
8 changed files with 171 additions and 48 deletions

View File

@@ -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();
if (this.props.operationBeforeSelect) {
this.props.operationBeforeSelect();
} else {
this.props.selectOption(targetItem);
}
this.togglePopover();
};

View File

@@ -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,7 +153,11 @@ class UserItem extends React.Component {
});
const currentSelectedStatusOption = this.statusOptions.filter(item => item.isSelected)[0];
const itemName = '<span class="op-target">' + Utils.HTMLescape(user.name) + '</span>';
const confirmSetUserInactiveMsg = gettext('Are you sure you want to set {user_placeholder} inactive?').replace('{user_placeholder}', itemName);
return (
<>
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<td>
<a href={href}>{user.name}</a>
@@ -155,6 +169,7 @@ class UserItem extends React.Component {
options={this.statusOptions}
selectOption={this.changeStatus}
toggleItemFreezed={this.props.toggleItemFreezed}
operationBeforeSelect={user.is_active ? this.toggleConfirmInactiveDialog : undefined}
/>
</td>
<td>{`${Utils.formatSize({bytes: user.quota_usage})} / ${this.getQuotaTotal(user.quota_total)}`}</td>
@@ -184,6 +199,16 @@ class UserItem extends React.Component {
)}
</td>
</tr>
{isConfirmInactiveDialogOpen &&
<CommonOperationConfirmationDialog
title={gettext('Set user inactive')}
message={confirmSetUserInactiveMsg}
executeOperation={this.setUserInactive}
confirmBtnText={gettext('Set')}
toggleDialog={this.toggleConfirmInactiveDialog}
/>
}
</>
);
}
}

View File

@@ -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 = '<span class="op-target">' + Utils.HTMLescape(item.name) + '</span>';
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}
/>
</td>
<td>
@@ -298,6 +308,15 @@ class Item extends Component {
toggleDialog={this.toggleResetPasswordDialog}
/>
}
{isConfirmInactiveDialogOpen &&
<CommonOperationConfirmationDialog
title={gettext('Set user inactive')}
message={confirmSetUserInactiveMsg}
executeOperation={this.setUserInactive}
confirmBtnText={gettext('Set')}
toggleDialog={this.toggleConfirmInactiveDialog}
/>
}
</Fragment>
);
}

View File

@@ -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 = '<span class="op-target">' + Utils.HTMLescape(item.name) + '</span>';
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}
/>
</td>
{isPro &&
@@ -567,6 +579,15 @@ class Item extends Component {
toggleDialog={this.toggleRevokeAdminDialog}
/>
}
{isConfirmInactiveDialogOpen &&
<CommonOperationConfirmationDialog
title={gettext('Set user inactive')}
message={confirmSetUserInactiveMsg}
executeOperation={this.setUserInactive}
confirmBtnText={gettext('Set')}
toggleDialog={this.toggleConfirmInactiveDialog}
/>
}
</Fragment>
);
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.