Compare commits

...

19 Commits

Author SHA1 Message Date
Yaodong Yu
0639564d27 fix: check group name cannot be same as organization name (#2090) 2023-07-19 11:37:28 +08:00
Yang Luo
6c647818ca feat: add "Sender number" input for Twilio SMS provider 2023-07-18 22:46:56 +08:00
Yaodong Yu
8bc73d17aa feat: fix bug that themeEditor can not load saved theme data (#2085) 2023-07-17 22:57:55 +08:00
Yang Luo
1f37c80177 feat: refactor code to add getStorageProvider() 2023-07-17 15:59:37 +08:00
Yaodong Yu
7924fca403 fix: hidden bug of "like" query (#2082) 2023-07-16 17:11:32 +08:00
Yang Luo
bd06996bab Improve CorsFilter for login API 2023-07-15 19:29:48 +08:00
Yang Luo
19ab168b12 Fix panic in func (c *ApiController) GetUser() if no user exists in DB 2023-07-14 20:57:59 +08:00
UsherFall
854a74b73e feat: fix the error when user uploads avatar to minio (https) (#2078)
* fix: Error reported when user uploads avatar to minio (https)

* Update provider.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-14 15:58:30 +08:00
yehong
beefb0b432 fix: fix event-stream streaming output in prod mode (#2076) 2023-07-14 11:59:26 +08:00
Yang Luo
d8969e6652 Support EnableSigninSession after SAML login 2023-07-14 11:27:18 +08:00
Yang Luo
666ff48837 Use id param in /sync-ldap-users API 2023-07-13 00:14:18 +08:00
Yang Luo
0a0c1b4788 Fix "Groups is immutable" bug when updating a user 2023-07-13 00:03:18 +08:00
Yang Luo
438c999e11 Add password mask to /get-ldaps and /get-ldap APIs 2023-07-12 23:21:47 +08:00
Yang Luo
a193ceb33d Fix bug in TestDeployStaticFiles() 2023-07-12 23:11:02 +08:00
Yang Luo
caec1d1bac Only consider x509 certs in /.well-known/jwks API 2023-07-12 22:39:39 +08:00
Denis Plynskiy
0d48da24dc feat: fix wrong rowKey for tables (#2070) 2023-07-12 21:12:36 +08:00
Yaodong Yu
de9eeaa1ef fix: init groups modify rule with admin (#2054) 2023-07-11 09:49:49 +08:00
Baihhh
ae6e35ee73 feat: fix bug that the password input disappears in login window (#2051)
Signed-off-by: baihhh <2542274498@qq.com>
2023-07-08 23:46:31 +08:00
Yaodong Yu
a58df645bf fix: fix state after mfa is enabled (#2050) 2023-07-08 22:35:31 +08:00
27 changed files with 149 additions and 44 deletions

View File

@@ -129,7 +129,7 @@ func QueryAnswerStream(authToken string, question string, writer io.Writer, buil
fmt.Printf("%s", data) fmt.Printf("%s", data)
// Write the streamed data as Server-Sent Events // Write the streamed data as Server-Sent Events
if _, err = fmt.Fprintf(writer, "data: %s\n\n", data); err != nil { if _, err = fmt.Fprintf(writer, "event: message\ndata: %s\n\n", data); err != nil {
return err return err
} }
flusher.Flush() flusher.Flush()

View File

@@ -368,9 +368,11 @@ func (c *ApiController) GetAccount() {
return return
} }
user.Permissions = object.GetMaskedPermissions(user.Permissions) if user != nil {
user.Roles = object.GetMaskedRoles(user.Roles) user.Permissions = object.GetMaskedPermissions(user.Permissions)
user.MultiFactorAuths = object.GetAllMfaProps(user, true) user.Roles = object.GetMaskedRoles(user.Roles)
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
}
organization, err := object.GetMaskedOrganization(object.GetOrganizationByUser(user)) organization, err := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
if err != nil { if err != nil {

View File

@@ -123,6 +123,11 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return return
} }
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}} resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}}
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in
c.SetSessionUsername(userId)
}
} else if form.Type == ResponseTypeCas { } else if form.Type == ResponseTypeCas {
// not oauth but CAS SSO protocol // not oauth but CAS SSO protocol
service := c.Input().Get("service") service := c.Input().Get("service")
@@ -135,11 +140,11 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
resp.Data = st resp.Data = st
} }
} }
if application.EnableSigninSession || application.HasPromptPage() { if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in // The prompt page needs the user to be signed in
c.SetSessionUsername(userId) c.SetSessionUsername(userId)
} }
} else { } else {
resp = wrapErrorResponse(fmt.Errorf("unknown response type: %s", form.Type)) resp = wrapErrorResponse(fmt.Errorf("unknown response type: %s", form.Type))
} }

View File

@@ -100,7 +100,7 @@ func (c *ApiController) GetLdapUsers() {
func (c *ApiController) GetLdaps() { func (c *ApiController) GetLdaps() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
c.ResponseOk(object.GetLdaps(owner)) c.ResponseOk(object.GetMaskedLdaps(object.GetLdaps(owner)))
} }
// GetLdap // GetLdap
@@ -116,7 +116,7 @@ func (c *ApiController) GetLdap() {
} }
_, name := util.GetOwnerAndNameFromId(id) _, name := util.GetOwnerAndNameFromId(id)
c.ResponseOk(object.GetLdap(name)) c.ResponseOk(object.GetMaskedLdap(object.GetLdap(name)))
} }
// AddLdap // AddLdap
@@ -226,8 +226,9 @@ func (c *ApiController) DeleteLdap() {
// @Title SyncLdapUsers // @Title SyncLdapUsers
// @router /sync-ldap-users [post] // @router /sync-ldap-users [post]
func (c *ApiController) SyncLdapUsers() { func (c *ApiController) SyncLdapUsers() {
owner := c.Input().Get("owner") id := c.Input().Get("id")
ldapId := c.Input().Get("ldapId")
owner, ldapId := util.GetOwnerAndNameFromId(id)
var users []object.LdapUser var users []object.LdapUser
err := json.Unmarshal(c.Ctx.Input.RequestBody, &users) err := json.Unmarshal(c.Ctx.Input.RequestBody, &users)
if err != nil { if err != nil {

View File

@@ -198,7 +198,10 @@ func (c *ApiController) GetUser() {
return return
} }
user.MultiFactorAuths = object.GetAllMfaProps(user, true) if user != nil {
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
}
err = object.ExtendUserWithRolesAndPermissions(user) err = object.ExtendUserWithRolesAndPermissions(user)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())

View File

@@ -25,6 +25,12 @@ import (
) )
func TestDeployStaticFiles(t *testing.T) { func TestDeployStaticFiles(t *testing.T) {
provider := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss")) object.InitConfig()
provider, err := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss"))
if err != nil {
panic(err)
}
deployStaticFiles(provider) deployStaticFiles(provider)
} }

View File

@@ -107,6 +107,11 @@ func UpdateGroup(id string, group *Group) (bool, error) {
return false, err return false, err
} }
err = checkGroupName(group.Name)
if err != nil {
return false, err
}
if name != group.Name { if name != group.Name {
err := GroupChangeTrigger(name, group.Name) err := GroupChangeTrigger(name, group.Name)
if err != nil { if err != nil {
@@ -123,6 +128,11 @@ func UpdateGroup(id string, group *Group) (bool, error) {
} }
func AddGroup(group *Group) (bool, error) { func AddGroup(group *Group) (bool, error) {
err := checkGroupName(group.Name)
if err != nil {
return false, err
}
affected, err := adapter.Engine.Insert(group) affected, err := adapter.Engine.Insert(group)
if err != nil { if err != nil {
return false, err return false, err
@@ -168,6 +178,17 @@ func DeleteGroup(group *Group) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func checkGroupName(name string) error {
exist, err := adapter.Engine.Exist(&Organization{Owner: "admin", Name: name})
if err != nil {
return err
}
if exist {
return errors.New("group name can't be same as the organization name")
}
return nil
}
func (group *Group) GetId() string { func (group *Group) GetId() string {
return fmt.Sprintf("%s/%s", group.Owner, group.Name) return fmt.Sprintf("%s/%s", group.Owner, group.Name)
} }
@@ -225,7 +246,7 @@ func GetGroupUserCount(groupName string, field, value string) (int64, error) {
func GetPaginationGroupUsers(groupName string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) { func GetPaginationGroupUsers(groupName string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
users := []*User{} users := []*User{}
session := adapter.Engine.Table("user"). session := adapter.Engine.Table("user").
Where(builder.Like{"`groups`", groupName}) Where(builder.Like{"`groups`", groupName + "\""})
if offset != -1 && limit != -1 { if offset != -1 && limit != -1 {
session.Limit(limit, offset) session.Limit(limit, offset)
@@ -255,7 +276,7 @@ func GetPaginationGroupUsers(groupName string, offset, limit int, field, value,
func GetGroupUsers(groupName string) ([]*User, error) { func GetGroupUsers(groupName string) ([]*User, error) {
users := []*User{} users := []*User{}
err := adapter.Engine.Table("user"). err := adapter.Engine.Table("user").
Where(builder.Like{"`groups`", groupName}). Where(builder.Like{"`groups`", groupName + "\""}).
Find(&users) Find(&users)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -61,7 +61,7 @@ func getBuiltInAccountItems() []*AccountItem {
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"}, {Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"}, {Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Groups", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"}, {Name: "Groups", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, {Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},

View File

@@ -103,6 +103,37 @@ func GetLdap(id string) (*Ldap, error) {
} }
} }
func GetMaskedLdap(ldap *Ldap, errs ...error) (*Ldap, error) {
if len(errs) > 0 && errs[0] != nil {
return nil, errs[0]
}
if ldap == nil {
return nil, nil
}
if ldap.Password != "" {
ldap.Password = "***"
}
return ldap, nil
}
func GetMaskedLdaps(ldaps []*Ldap, errs ...error) ([]*Ldap, error) {
if len(errs) > 0 && errs[0] != nil {
return nil, errs[0]
}
var err error
for _, ldap := range ldaps {
ldap, err = GetMaskedLdap(ldap)
if err != nil {
return nil, err
}
}
return ldaps, nil
}
func UpdateLdap(ldap *Ldap) (bool, error) { func UpdateLdap(ldap *Ldap) (bool, error) {
if l, err := GetLdap(ldap.Id); err != nil { if l, err := GetLdap(ldap.Id); err != nil {
return false, nil return false, nil

View File

@@ -123,6 +123,10 @@ func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
// link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html // link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
// or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key // or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
for _, cert := range certs { for _, cert := range certs {
if cert.Type != "x509" {
continue
}
certPemBlock := []byte(cert.Certificate) certPemBlock := []byte(cert.Certificate)
certDerBlock, _ := pem.Decode(certPemBlock) certDerBlock, _ := pem.Decode(certPemBlock)
x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes) x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes)

View File

@@ -229,7 +229,7 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
session = session.Omit("client_secret2") session = session.Omit("client_secret2")
} }
if provider.Type != "Keycloak" { if provider.Type == "Tencent Cloud COS" {
provider.Endpoint = util.GetEndPoint(provider.Endpoint) provider.Endpoint = util.GetEndPoint(provider.Endpoint)
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint) provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
} }
@@ -243,7 +243,7 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
} }
func AddProvider(provider *Provider) (bool, error) { func AddProvider(provider *Provider) (bool, error) {
if provider.Type != "Keycloak" { if provider.Type == "Tencent Cloud COS" {
provider.Endpoint = util.GetEndPoint(provider.Endpoint) provider.Endpoint = util.GetEndPoint(provider.Endpoint)
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint) provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
} }

View File

@@ -42,7 +42,11 @@ func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
return err return err
} }
if provider.Type == sender.Aliyun { if provider.Type == sender.Twilio {
if provider.AppId != "" {
phoneNumbers = append([]string{provider.AppId}, phoneNumbers...)
}
} else if provider.Type == sender.Aliyun {
for i, number := range phoneNumbers { for i, number := range phoneNumbers {
phoneNumbers[i] = strings.TrimPrefix(number, "+86") phoneNumbers[i] = strings.TrimPrefix(number, "+86")
} }

View File

@@ -25,6 +25,7 @@ import (
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/storage" "github.com/casdoor/casdoor/storage"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/casdoor/oss"
) )
var isCloudIntranet bool var isCloudIntranet bool
@@ -102,11 +103,11 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
return fileUrl, objectKey return fileUrl, objectKey
} }
func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer, lang string) (string, string, error) { func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
endpoint := getProviderEndpoint(provider) endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint) storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if storageProvider == nil { if storageProvider == nil {
return "", "", fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type) return nil, fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
} }
if provider.Domain == "" { if provider.Domain == "" {
@@ -114,9 +115,18 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
UpdateProvider(provider.GetId(), provider) UpdateProvider(provider.GetId(), provider)
} }
return storageProvider, nil
}
func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer, lang string) (string, string, error) {
storageProvider, err := getStorageProvider(provider, lang)
if err != nil {
return "", "", err
}
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true) fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
_, err := storageProvider.Put(objectKey, fileBuffer) _, err = storageProvider.Put(objectKey, fileBuffer)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@@ -154,15 +164,9 @@ func DeleteFile(provider *Provider, objectKey string, lang string) error {
return fmt.Errorf(i18n.Translate(lang, "storage:The objectKey: %s is not allowed"), objectKey) return fmt.Errorf(i18n.Translate(lang, "storage:The objectKey: %s is not allowed"), objectKey)
} }
endpoint := getProviderEndpoint(provider) storageProvider, err := getStorageProvider(provider, lang)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint) if err != nil {
if storageProvider == nil { return err
return fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
}
if provider.Domain == "" {
provider.Domain = storageProvider.GetEndpoint()
UpdateProvider(provider.GetId(), provider)
} }
return storageProvider.Delete(objectKey) return storageProvider.Delete(objectKey)

View File

@@ -293,7 +293,13 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)
} }
if oldUser.Groups == nil {
oldUser.Groups = []string{}
}
oldUserGroupsJson, _ := json.Marshal(oldUser.Groups) oldUserGroupsJson, _ := json.Marshal(oldUser.Groups)
if newUser.Groups == nil {
newUser.Groups = []string{}
}
newUserGroupsJson, _ := json.Marshal(newUser.Groups) newUserGroupsJson, _ := json.Marshal(newUser.Groups)
if string(oldUserGroupsJson) != string(newUserGroupsJson) { if string(oldUserGroupsJson) != string(newUserGroupsJson) {
item := GetAccountItemByName("Groups", organization) item := GetAccountItemByName("Groups", organization)

View File

@@ -33,6 +33,13 @@ func CorsFilter(ctx *context.Context) {
origin := ctx.Input.Header(headerOrigin) origin := ctx.Input.Header(headerOrigin)
originConf := conf.GetConfigString("origin") originConf := conf.GetConfigString("origin")
if ctx.Request.Method == "POST" && ctx.Request.RequestURI == "/api/login/oauth/access_token" {
ctx.Output.Header(headerAllowOrigin, origin)
ctx.Output.Header(headerAllowMethods, "POST, GET, OPTIONS, DELETE")
ctx.Output.Header(headerAllowHeaders, "Content-Type, Authorization")
return
}
if origin != "" && originConf != "" && origin != originConf { if origin != "" && originConf != "" && origin != originConf {
ok, err := object.IsOriginAllowed(origin) ok, err := object.IsOriginAllowed(origin)
if err != nil { if err != nil {

View File

@@ -130,10 +130,8 @@ class App extends Component {
this.setState({ this.setState({
requiredEnableMfa: requiredEnableMfa, requiredEnableMfa: requiredEnableMfa,
}); });
}
if (this.state.requiredEnableMfa !== prevState.requiredEnableMfa || this.state.account !== prevState.account) { if (requiredEnableMfa === true) {
if (this.state.requiredEnableMfa === true) {
const mfaType = Setting.getMfaItemsByRules(this.state.account, this.state.account?.organization, [MfaRuleRequired]) const mfaType = Setting.getMfaItemsByRules(this.state.account, this.state.account?.organization, [MfaRuleRequired])
.find((item) => item.rule === MfaRuleRequired)?.name; .find((item) => item.rule === MfaRuleRequired)?.name;
if (mfaType !== undefined) { if (mfaType !== undefined) {
@@ -144,7 +142,6 @@ class App extends Component {
} }
updateMenuKey() { updateMenuKey() {
// eslint-disable-next-line no-restricted-globals
const uri = location.pathname; const uri = location.pathname;
this.setState({ this.setState({
uri: uri, uri: uri,
@@ -636,7 +633,7 @@ class App extends Component {
<Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} /> <Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} /> <Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} /> <Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
<Route exact path="/mfa/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} onfinish={result => this.setState({requiredEnableMfa: result})} {...props} />)} /> <Route exact path="/mfa/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} onfinish={() => this.setState({requiredEnableMfa: false})} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} /> <Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} /> <Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")} <Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}

View File

@@ -158,6 +158,7 @@ class CertEditPage extends React.Component {
{ {
[ [
{id: "x509", name: "x509"}, {id: "x509", name: "x509"},
{id: "Payment", name: "Payment"},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) ].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
} }
</Select> </Select>

View File

@@ -151,6 +151,7 @@ class CertListPage extends BaseListPage {
filterMultiple: false, filterMultiple: false,
filters: [ filters: [
{text: "x509", value: "x509"}, {text: "x509", value: "x509"},
{text: "Payment", value: "Payment"},
], ],
width: "110px", width: "110px",
sorter: true, sorter: true,
@@ -213,7 +214,7 @@ class CertListPage extends BaseListPage {
return ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={certs} rowKey="name" size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={certs} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => ( title={() => (
<div> <div>
{i18next.t("general:Certs")}&nbsp;&nbsp;&nbsp;&nbsp; {i18next.t("general:Certs")}&nbsp;&nbsp;&nbsp;&nbsp;

View File

@@ -41,7 +41,7 @@ class EntryPage extends React.Component {
renderHomeIfLoggedIn(component) { renderHomeIfLoggedIn(component) {
if (this.props.account !== null && this.props.account !== undefined) { if (this.props.account !== null && this.props.account !== undefined) {
return <Redirect to="/" />; return <Redirect to={{pathname: "/", state: {from: "/login"}}} />;
} else { } else {
return component; return component;
} }

View File

@@ -74,7 +74,7 @@ class OrganizationListPage extends BaseListPage {
{name: "Ranking", visible: true, viewRule: "Public", modifyRule: "Admin"}, {name: "Ranking", visible: true, viewRule: "Public", modifyRule: "Admin"},
{name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"}, {name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"},
{name: "API key", label: i18next.t("general:API key"), modifyRule: "Self"}, {name: "API key", label: i18next.t("general:API key"), modifyRule: "Self"},
{name: "Groups", visible: true, viewRule: "Public", modifyRule: "Immutable"}, {name: "Groups", visible: true, viewRule: "Public", modifyRule: "Admin"},
{name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"}, {name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"},
{name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"}, {name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"},
{name: "3rd-party logins", visible: true, viewRule: "Self", modifyRule: "Self"}, {name: "3rd-party logins", visible: true, viewRule: "Self", modifyRule: "Self"},

View File

@@ -196,7 +196,7 @@ class PlanListPage extends BaseListPage {
return ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={plans} rowKey="name" size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={plans} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => ( title={() => (
<div> <div>
{i18next.t("general:Plans")}&nbsp;&nbsp;&nbsp;&nbsp; {i18next.t("general:Plans")}&nbsp;&nbsp;&nbsp;&nbsp;

View File

@@ -165,7 +165,7 @@ class PricingListPage extends BaseListPage {
return ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={pricings} rowKey="name" size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={pricings} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => ( title={() => (
<div> <div>
{i18next.t("general:Pricings")}&nbsp;&nbsp;&nbsp;&nbsp; {i18next.t("general:Pricings")}&nbsp;&nbsp;&nbsp;&nbsp;

View File

@@ -236,7 +236,10 @@ class ProviderEditPage extends React.Component {
tooltip = i18next.t("provider:Agent ID - Tooltip"); tooltip = i18next.t("provider:Agent ID - Tooltip");
} }
} else if (provider.category === "SMS") { } else if (provider.category === "SMS") {
if (provider.type === "Tencent Cloud SMS") { if (provider.type === "Twilio SMS") {
text = i18next.t("provider:Sender number");
tooltip = i18next.t("provider:Sender number - Tooltip");
} else if (provider.type === "Tencent Cloud SMS") {
text = i18next.t("provider:App ID"); text = i18next.t("provider:App ID");
tooltip = i18next.t("provider:App ID - Tooltip"); tooltip = i18next.t("provider:App ID - Tooltip");
} else if (provider.type === "Volc Engine SMS") { } else if (provider.type === "Volc Engine SMS") {
@@ -674,6 +677,7 @@ class ProviderEditPage extends React.Component {
) : null} ) : null}
</div> </div>
) : null} ) : null}
{this.getAppIdRow(this.state.provider)}
{ {
this.state.provider.category === "Email" ? ( this.state.provider.category === "Email" ? (
<React.Fragment> <React.Fragment>
@@ -919,7 +923,6 @@ class ProviderEditPage extends React.Component {
</Row> </Row>
) : null ) : null
} }
{this.getAppIdRow(this.state.provider)}
<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("provider:Provider URL"), i18next.t("provider:Provider URL - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Provider URL"), i18next.t("provider:Provider URL - Tooltip"))} :

View File

@@ -215,7 +215,7 @@ class SubscriptionListPage extends BaseListPage {
return ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={subscriptions} rowKey="name" size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={subscriptions} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => ( title={() => (
<div> <div>
{i18next.t("general:Subscriptions")}&nbsp;&nbsp;&nbsp;&nbsp; {i18next.t("general:Subscriptions")}&nbsp;&nbsp;&nbsp;&nbsp;

View File

@@ -82,6 +82,10 @@ class LoginPage extends React.Component {
} }
componentDidUpdate(prevProps, prevState, snapshot) { componentDidUpdate(prevProps, prevState, snapshot) {
if (prevState.loginMethod === undefined && this.state.loginMethod === undefined) {
const application = this.getApplicationObj();
this.setState({loginMethod: this.getDefaultLoginMethod(application)});
}
if (prevProps.application !== this.props.application) { if (prevProps.application !== this.props.application) {
this.setState({loginMethod: this.getDefaultLoginMethod(this.props.application)}); this.setState({loginMethod: this.getDefaultLoginMethod(this.props.application)});

View File

@@ -190,7 +190,7 @@ class MfaSetupPage extends React.Component {
<MfaEnableForm user={this.getUser()} mfaType={this.state.mfaType} recoveryCodes={this.state.mfaProps.recoveryCodes} <MfaEnableForm user={this.getUser()} mfaType={this.state.mfaType} recoveryCodes={this.state.mfaProps.recoveryCodes}
onSuccess={() => { onSuccess={() => {
Setting.showMessage("success", i18next.t("general:Enabled successfully")); Setting.showMessage("success", i18next.t("general:Enabled successfully"));
this.props.onfinish(true); this.props.onfinish();
if (localStorage.getItem("mfaRedirectUrl") !== null) { if (localStorage.getItem("mfaRedirectUrl") !== null) {
Setting.goToLink(localStorage.getItem("mfaRedirectUrl")); Setting.goToLink(localStorage.getItem("mfaRedirectUrl"));
localStorage.removeItem("mfaRedirectUrl"); localStorage.removeItem("mfaRedirectUrl");

View File

@@ -17,7 +17,7 @@ import ThemePicker from "./ThemePicker";
import ColorPicker, {GREEN_COLOR, PINK_COLOR} from "./ColorPicker"; import ColorPicker, {GREEN_COLOR, PINK_COLOR} from "./ColorPicker";
import RadiusPicker from "./RadiusPicker"; import RadiusPicker from "./RadiusPicker";
import * as React from "react"; import * as React from "react";
import {useEffect} from "react"; import {useEffect, useLayoutEffect} from "react";
import {Content} from "antd/es/layout/layout"; import {Content} from "antd/es/layout/layout";
import i18next from "i18next"; import i18next from "i18next";
import * as Conf from "../../Conf"; import * as Conf from "../../Conf";
@@ -58,6 +58,11 @@ export default function ThemeEditor(props) {
}, [isLight, isCompact]); }, [isLight, isCompact]);
useEffect(() => { useEffect(() => {
onThemeChange(null, themeData);
form.setFieldsValue(themeData);
}, []);
useLayoutEffect(() => {
const mergedData = Object.assign(Object.assign(Object.assign({}, Conf.ThemeDefault), {themeType}), ThemesInfo[themeType]); const mergedData = Object.assign(Object.assign(Object.assign({}, Conf.ThemeDefault), {themeType}), ThemesInfo[themeType]);
onThemeChange(null, mergedData); onThemeChange(null, mergedData);
form.setFieldsValue(mergedData); form.setFieldsValue(mergedData);