diff --git a/object/init.go b/object/init.go index ba03f664..68a8695a 100644 --- a/object/init.go +++ b/object/init.go @@ -84,6 +84,7 @@ func initBuiltInOrganization() bool { {Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, + {Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, }, } AddOrganization(organization) diff --git a/object/organization.go b/object/organization.go index f6320aa4..58e7d7b8 100644 --- a/object/organization.go +++ b/object/organization.go @@ -46,7 +46,7 @@ type Organization struct { EnableSoftDeletion bool `json:"enableSoftDeletion"` IsProfilePublic bool `json:"isProfilePublic"` - AccountItems []*AccountItem `xorm:"varchar(2000)" json:"accountItems"` + AccountItems []*AccountItem `xorm:"varchar(3000)" json:"accountItems"` } func GetOrganizationCount(owner, field, value string) int { diff --git a/object/user.go b/object/user.go index 6d6e48fe..153fb7db 100644 --- a/object/user.go +++ b/object/user.go @@ -114,6 +114,8 @@ type User struct { LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"` SigninWrongTimes int `json:"signinWrongTimes"` + + ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"` } type Userinfo struct { @@ -128,6 +130,13 @@ type Userinfo struct { Phone string `json:"phone,omitempty"` } +type ManagedAccount struct { + Application string `xorm:"varchar(100)" json:"application"` + Username string `xorm:"varchar(100)" json:"username"` + Password string `xorm:"varchar(100)" json:"password"` + SigninUrl string `xorm:"varchar(200)" json:"signinUrl"` +} + func GetGlobalUserCount(field, value string) int { session := GetSession("", -1, -1, field, value, "", "") count, err := session.Count(&User{}) @@ -334,6 +343,12 @@ func GetMaskedUser(user *User) *User { if user.Password != "" { user.Password = "***" } + + if user.ManagedAccounts != nil { + for _, manageAccount := range user.ManagedAccounts { + manageAccount.Password = "***" + } + } return user } @@ -378,7 +393,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo columns = []string{ "owner", "display_name", "avatar", "location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "signup_application", - "is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", + "is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "signin_wrong_times", "last_signin_wrong_time", } } diff --git a/web/src/AccountTable.js b/web/src/AccountTable.js index dd2dd7e5..debcd78d 100644 --- a/web/src/AccountTable.js +++ b/web/src/AccountTable.js @@ -95,6 +95,7 @@ class AccountTable extends React.Component { {name: "Is forbidden", displayName: i18next.t("user:Is forbidden")}, {name: "Is deleted", displayName: i18next.t("user:Is deleted")}, {name: "WebAuthn credentials", displayName: i18next.t("user:WebAuthn credentials")}, + {name: "Managed accounts", displayName: i18next.t("user:Managed accounts")}, ]; const getItemDisplayName = (text) => { diff --git a/web/src/ManagedAccountTable.js b/web/src/ManagedAccountTable.js new file mode 100644 index 00000000..f2a19a8b --- /dev/null +++ b/web/src/ManagedAccountTable.js @@ -0,0 +1,165 @@ +// Copyright 2022 The Casdoor Authors. All Rights Reserved. +// +// 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. + +import React from "react"; +import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons"; +import {Button, Col, Input, Row, Select, Table, Tooltip} from "antd"; +import * as Setting from "./Setting"; +import i18next from "i18next"; + +const {Option} = Select; + +class ManagedAccountTable extends React.Component { + constructor(props) { + super(props); + this.state = { + classes: props, + }; + } + + updateTable(table) { + this.props.onUpdateTable(table); + } + + updateField(table, index, key, value) { + table[index][key] = value; + this.updateTable(table); + } + + addRow(table) { + const row = {application: "", username: "", password: "", signinUrl: ""}; + if (table === undefined || table === null) { + table = []; + } + table = Setting.addRow(table, row); + this.updateTable(table); + } + + deleteRow(table, i) { + table = Setting.deleteRow(table, i); + this.updateTable(table); + } + + upRow(table, i) { + table = Setting.swapRow(table, i - 1, i); + this.updateTable(table); + } + + downRow(table, i) { + table = Setting.swapRow(table, i, i + 1); + this.updateTable(table); + } + + renderTable(table) { + const columns = [ + { + title: i18next.t("general:Application"), + dataIndex: "application", + key: "application", + render: (text, record, index) => { + const items = this.props.applications; + const signinUrlMap = new Map(); + for (const application of items) { + signinUrlMap.set(application.name, application.signinUrl); + } + return ( + + ); + }, + }, + { + title: i18next.t("signup:Username"), + dataIndex: "username", + key: "username", + width: "420px", + render: (text, record, index) => { + return ( + { + this.updateField(table, index, "username", e.target.value); + }} /> + ); + }, + }, + { + title: i18next.t("general:Password"), + dataIndex: "password", + key: "password", + width: "420px", + render: (text, record, index) => { + return ( + { + this.updateField(table, index, "password", e.target.value); + }} /> + ); + }, + }, + { + title: i18next.t("general:Action"), + key: "action", + width: "100px", + render: (text, record, index) => { + return ( +
+ +
+ ); + }, + }, + ]; + + return ( + ( +
+ {this.props.title}     + +
+ )} + /> + ); + } + + render() { + return ( +
+ +
+ { + this.renderTable(this.props.table) + } + + + + ); + } +} + +export default ManagedAccountTable; diff --git a/web/src/UserEditPage.js b/web/src/UserEditPage.js index 04b3ce67..5795756a 100644 --- a/web/src/UserEditPage.js +++ b/web/src/UserEditPage.js @@ -28,6 +28,7 @@ import OAuthWidget from "./common/OAuthWidget"; import SamlWidget from "./common/SamlWidget"; import SelectRegionBox from "./SelectRegionBox"; import WebAuthnCredentialTable from "./WebauthnCredentialTable"; +import ManagedAccountTable from "./ManagedAccountTable"; import {Controlled as CodeMirror} from "react-codemirror2"; import "codemirror/lib/codemirror.css"; @@ -543,7 +544,7 @@ class UserEditPage extends React.Component { ); - } else if(accountItem.name === "WebAuthn credentials") { + } else if (accountItem.name === "WebAuthn credentials") { return ( @@ -554,6 +555,22 @@ class UserEditPage extends React.Component { ); + } else if (accountItem.name === "Managed accounts") { + return ( + + + {Setting.getLabel(i18next.t("user:Managed accounts"), i18next.t("user:Managed accounts"))} : + + + {this.updateUserField("managedAccounts", table);}} + applications={this.state.applications} + /> + + + ); } } diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index cdc5b302..e66dc78b 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -657,6 +657,7 @@ "Link": "Link", "Location": "Standort", "Location - Tooltip": "Standort - Tooltip", + "Managed accounts": "Managed accounts", "Modify password...": "Passwort ändern...", "New Email": "Neue E-Mail", "New Password": "Neues Passwort", diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index 47720611..dffa762e 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -657,6 +657,7 @@ "Link": "Link", "Location": "Location", "Location - Tooltip": "Location - Tooltip", + "Managed accounts": "Managed accounts", "Modify password...": "Modify password...", "New Email": "New Email", "New Password": "New Password", diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index 228dc5c7..1f340013 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -657,6 +657,7 @@ "Link": "Lier", "Location": "Localisation", "Location - Tooltip": "Localisation - Infobulle", + "Managed accounts": "Managed accounts", "Modify password...": "Modifier le mot de passe...", "New Email": "Nouvel e-mail", "New Password": "Nouveau mot de passe", diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index 20e48ba2..eb19f1ed 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -657,6 +657,7 @@ "Link": "リンク", "Location": "場所", "Location - Tooltip": "場所 → ツールチップ", + "Managed accounts": "Managed accounts", "Modify password...": "パスワードを変更...", "New Email": "新しいメール", "New Password": "新しいパスワード", diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index 0cb27650..dc3c24de 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -657,6 +657,7 @@ "Link": "Link", "Location": "Location", "Location - Tooltip": "Location - Tooltip", + "Managed accounts": "Managed accounts", "Modify password...": "Modify password...", "New Email": "New Email", "New Password": "New Password", diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index c8111d0f..e35b47c5 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -657,6 +657,7 @@ "Link": "Ссылка", "Location": "Местоположение", "Location - Tooltip": "Расположение - Подсказка", + "Managed accounts": "Managed accounts", "Modify password...": "Изменить пароль...", "New Email": "Новое письмо", "New Password": "Новый пароль", diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index aa76a0eb..ea2f1edc 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -657,6 +657,7 @@ "Link": "绑定", "Location": "城市", "Location - Tooltip": "居住地址所在的城市", + "Managed accounts": "托管账户", "Modify password...": "编辑密码...", "New Email": "新邮箱", "New Password": "新密码",