Add user soft deletion.

This commit is contained in:
Gucheng Wang 2021-11-06 15:52:03 +08:00
parent db892333fe
commit 9e920181d2
11 changed files with 72 additions and 19 deletions

View File

@ -152,6 +152,7 @@ func (c *ApiController) Signup() {
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false, IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name, SignupApplication: application.Name,
Properties: map[string]string{}, Properties: map[string]string{},
} }

View File

@ -251,7 +251,7 @@ func (c *ApiController) Login() {
user = object.GetUserByField(application.Organization, "name", userInfo.Username) user = object.GetUserByField(application.Organization, "name", userInfo.Username)
} }
if user != nil { if user != nil && user.IsDeleted == false {
// Sign in via OAuth (want to sign up but already have account) // Sign in via OAuth (want to sign up but already have account)
if user.IsForbidden { if user.IsForbidden {
@ -293,6 +293,7 @@ func (c *ApiController) Login() {
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false, IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name, SignupApplication: application.Name,
Properties: properties, Properties: properties,
} }

View File

@ -102,7 +102,7 @@ func CheckPassword(user *User, password string) string {
func CheckUserPassword(organization string, username string, password string) (*User, string) { func CheckUserPassword(organization string, username string, password string) (*User, string) {
user := GetUserByFields(organization, username) user := GetUserByFields(organization, username)
if user == nil { if user == nil || user.IsDeleted == true {
return nil, "the user does not exist, please sign up first" return nil, "the user does not exist, please sign up first"
} }

View File

@ -67,6 +67,7 @@ func initBuiltInUser() {
IsAdmin: true, IsAdmin: true,
IsGlobalAdmin: true, IsGlobalAdmin: true,
IsForbidden: false, IsForbidden: false,
IsDeleted: false,
Properties: make(map[string]string), Properties: make(map[string]string),
} }
AddUser(user) AddUser(user)

View File

@ -24,13 +24,14 @@ type Organization struct {
Name string `xorm:"varchar(100) notnull pk" json:"name"` Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"` CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"` DisplayName string `xorm:"varchar(100)" json:"displayName"`
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"` WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
Favicon string `xorm:"varchar(100)" json:"favicon"` Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"` PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"` PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"` PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"` DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
EnableSoftDeletion bool `json:"enableSoftDeletion"`
} }
func GetOrganizationCount(owner string) int { func GetOrganizationCount(owner string) int {

View File

@ -51,6 +51,7 @@ type User struct {
IsAdmin bool `json:"isAdmin"` IsAdmin bool `json:"isAdmin"`
IsGlobalAdmin bool `json:"isGlobalAdmin"` IsGlobalAdmin bool `json:"isGlobalAdmin"`
IsForbidden bool `json:"isForbidden"` IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"` SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
Hash string `xorm:"varchar(100)" json:"hash"` Hash string `xorm:"varchar(100)" json:"hash"`
PreHash string `xorm:"varchar(100)" json:"preHash"` PreHash string `xorm:"varchar(100)" json:"preHash"`
@ -199,8 +200,8 @@ func UpdateUser(id string, user *User) bool {
} }
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols("owner", "display_name", "avatar", affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols("owner", "display_name", "avatar",
"location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "is_admin", "is_global_admin", "is_forbidden", "location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag",
"hash", "properties").Update(user) "is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "properties").Update(user)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -64,6 +64,7 @@ func createUserFromOriginalUser(originalUser *User, affiliationMap map[int]strin
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false, IsGlobalAdmin: false,
IsForbidden: originalUser.Deleted != 0, IsForbidden: originalUser.Deleted != 0,
IsDeleted: false,
Properties: map[string]string{}, Properties: map[string]string{},
} }
return user return user

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Card, Col, Input, Row, Select} from 'antd'; import {Button, Card, Col, Input, Row, Select, Switch} from 'antd';
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as LdapBackend from "./backend/LdapBackend"; import * as LdapBackend from "./backend/LdapBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
@ -205,6 +205,16 @@ class OrganizationEditPage extends React.Component {
</Row> </Row>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:Soft deletion"), i18next.t("organization:Soft deletion - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.organization.enableSoftDeletion} onChange={checked => {
this.updateOrganizationField('enableSoftDeletion', checked);
}} />
</Col>
</Row>
<Row style={{marginTop: '20px'}}> <Row style={{marginTop: '20px'}}>
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:LDAPs"), i18next.t("general:LDAPs - Tooltip"))} : {Setting.getLabel(i18next.t("general:LDAPs"), i18next.t("general:LDAPs - Tooltip"))} :

View File

@ -14,7 +14,7 @@
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {Button, Popconfirm, Table} from 'antd'; import {Button, Popconfirm, Switch, Table} from 'antd';
import moment from "moment"; import moment from "moment";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
@ -59,6 +59,7 @@ class OrganizationListPage extends React.Component {
PasswordSalt: "", PasswordSalt: "",
phonePrefix: "86", phonePrefix: "86",
defaultAvatar: "https://casbin.org/img/casbin.svg", defaultAvatar: "https://casbin.org/img/casbin.svg",
enableSoftDeletion: false,
} }
} }
@ -158,7 +159,7 @@ class OrganizationListPage extends React.Component {
title: i18next.t("general:Password type"), title: i18next.t("general:Password type"),
dataIndex: 'passwordType', dataIndex: 'passwordType',
key: 'passwordType', key: 'passwordType',
width: '100px', width: '150px',
sorter: (a, b) => a.passwordType.localeCompare(b.passwordType), sorter: (a, b) => a.passwordType.localeCompare(b.passwordType),
}, },
{ {
@ -172,7 +173,7 @@ class OrganizationListPage extends React.Component {
title: i18next.t("organization:Default avatar"), title: i18next.t("organization:Default avatar"),
dataIndex: 'defaultAvatar', dataIndex: 'defaultAvatar',
key: 'defaultAvatar', key: 'defaultAvatar',
width: '50px', width: '120px',
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<a target="_blank" rel="noreferrer" href={text}> <a target="_blank" rel="noreferrer" href={text}>
@ -181,6 +182,18 @@ class OrganizationListPage extends React.Component {
) )
} }
}, },
{
title: i18next.t("organization:Soft deletion"),
dataIndex: 'enableSoftDeletion',
key: 'enableSoftDeletion',
width: '140px',
sorter: (a, b) => a.enableSoftDeletion - b.enableSoftDeletion,
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
)
}
},
{ {
title: i18next.t("general:Action"), title: i18next.t("general:Action"),
dataIndex: '', dataIndex: '',

View File

@ -353,6 +353,16 @@ class UserEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("user:Is deleted"), i18next.t("user:Is deleted - Tooltip"))} :
</Col>
<Col span={(Setting.isMobile()) ? 22 : 2} >
<Switch checked={this.state.user.isDeleted} onChange={checked => {
this.updateUserField('isDeleted', checked);
}} />
</Col>
</Row>
</React.Fragment> </React.Fragment>
) )
} }

View File

@ -67,6 +67,7 @@ class UserListPage extends React.Component {
createdTime: moment().format(), createdTime: moment().format(),
type: "normal-user", type: "normal-user",
password: "123", password: "123",
passwordSalt: "",
displayName: `New User - ${randomName}`, displayName: `New User - ${randomName}`,
avatar: "https://casbin.org/img/casbin.svg", avatar: "https://casbin.org/img/casbin.svg",
email: "user@example.com", email: "user@example.com",
@ -78,6 +79,7 @@ class UserListPage extends React.Component {
isAdmin: false, isAdmin: false,
isGlobalAdmin: false, isGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
isDeleted: false,
properties: {}, properties: {},
} }
} }
@ -164,7 +166,7 @@ class UserListPage extends React.Component {
title: i18next.t("general:Created time"), title: i18next.t("general:Created time"),
dataIndex: 'createdTime', dataIndex: 'createdTime',
key: 'createdTime', key: 'createdTime',
width: '180px', width: '160px',
sorter: (a, b) => a.createdTime.localeCompare(b.createdTime), sorter: (a, b) => a.createdTime.localeCompare(b.createdTime),
render: (text, record, index) => { render: (text, record, index) => {
return Setting.getFormattedDate(text); return Setting.getFormattedDate(text);
@ -243,7 +245,7 @@ class UserListPage extends React.Component {
title: i18next.t("user:Is admin"), title: i18next.t("user:Is admin"),
dataIndex: 'isAdmin', dataIndex: 'isAdmin',
key: 'isAdmin', key: 'isAdmin',
width: '100px', width: '110px',
sorter: (a, b) => a.isAdmin - b.isAdmin, sorter: (a, b) => a.isAdmin - b.isAdmin,
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
@ -255,7 +257,7 @@ class UserListPage extends React.Component {
title: i18next.t("user:Is global admin"), title: i18next.t("user:Is global admin"),
dataIndex: 'isGlobalAdmin', dataIndex: 'isGlobalAdmin',
key: 'isGlobalAdmin', key: 'isGlobalAdmin',
width: '100px', width: '110px',
sorter: (a, b) => a.isGlobalAdmin - b.isGlobalAdmin, sorter: (a, b) => a.isGlobalAdmin - b.isGlobalAdmin,
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
@ -267,7 +269,7 @@ class UserListPage extends React.Component {
title: i18next.t("user:Is forbidden"), title: i18next.t("user:Is forbidden"),
dataIndex: 'isForbidden', dataIndex: 'isForbidden',
key: 'isForbidden', key: 'isForbidden',
width: '100px', width: '110px',
sorter: (a, b) => a.isForbidden - b.isForbidden, sorter: (a, b) => a.isForbidden - b.isForbidden,
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
@ -275,6 +277,18 @@ class UserListPage extends React.Component {
) )
} }
}, },
{
title: i18next.t("user:Is deleted"),
dataIndex: 'isDeleted',
key: 'isDeleted',
width: '110px',
sorter: (a, b) => a.isDeleted - b.isDeleted,
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
)
}
},
{ {
title: i18next.t("general:Action"), title: i18next.t("general:Action"),
dataIndex: '', dataIndex: '',