mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-10 10:15:19 +08:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
812c44e070 | |||
78e45d07cf | |||
0856977b92 | |||
a44a4b0300 | |||
4b29dd8c41 | |||
165e2e33e3 | |||
d13a307ad5 | |||
27bd771fed | |||
9f3ee275a8 | |||
fcda64ad7d | |||
d815bf92bd | |||
7867060b71 | |||
8890d1d7c7 | |||
6e6a0a074a | |||
cff3007992 | |||
fe448cbcf4 | |||
2ab25df950 | |||
b895926754 | |||
5bb7a4153f | |||
b7cd598ee8 | |||
b10fb97c92 | |||
b337b908ea | |||
ba9d1e2388 | |||
29ec1d2d9c | |||
84a03f6c8e | |||
56ff06bbea | |||
7e756b8ee2 | |||
19ba37e0c2 | |||
b98ce19211 | |||
37d1a73c0c | |||
727877cf54 | |||
939b416717 | |||
f115843fbb | |||
aa6a4dc74f | |||
462a82a3d5 |
@ -32,7 +32,9 @@ func InitAuthz() {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
a, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName()+conf.GetConfigString("dbName"), "casbin_rule", tableNamePrefix, true)
|
driverName := conf.GetConfigString("driverName")
|
||||||
|
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
|
||||||
|
a, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, "casbin_rule", tableNamePrefix, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -85,6 +87,8 @@ p, *, *, POST, /api/logout, *, *
|
|||||||
p, *, *, GET, /api/logout, *, *
|
p, *, *, GET, /api/logout, *, *
|
||||||
p, *, *, GET, /api/get-account, *, *
|
p, *, *, GET, /api/get-account, *, *
|
||||||
p, *, *, GET, /api/userinfo, *, *
|
p, *, *, GET, /api/userinfo, *, *
|
||||||
|
p, *, *, POST, /api/webhook, *, *
|
||||||
|
p, *, *, GET, /api/get-webhook-event, *, *
|
||||||
p, *, *, *, /api/login/oauth, *, *
|
p, *, *, *, /api/login/oauth, *, *
|
||||||
p, *, *, GET, /api/get-application, *, *
|
p, *, *, GET, /api/get-application, *, *
|
||||||
p, *, *, GET, /api/get-organization-applications, *, *
|
p, *, *, GET, /api/get-organization-applications, *, *
|
||||||
|
@ -31,6 +31,8 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
|
|||||||
return NewAliyunCaptchaProvider()
|
return NewAliyunCaptchaProvider()
|
||||||
} else if captchaType == "GEETEST" {
|
} else if captchaType == "GEETEST" {
|
||||||
return NewGEETESTCaptchaProvider()
|
return NewGEETESTCaptchaProvider()
|
||||||
|
} else if captchaType == "Cloudflare Turnstile" {
|
||||||
|
return NewCloudflareTurnstileProvider()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
66
captcha/turnstile.go
Normal file
66
captcha/turnstile.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CloudflareTurnstileVerifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
|
||||||
|
|
||||||
|
type CloudflareTurnstileProvider struct{}
|
||||||
|
|
||||||
|
func NewCloudflareTurnstileProvider() *CloudflareTurnstileProvider {
|
||||||
|
captcha := &CloudflareTurnstileProvider{}
|
||||||
|
return captcha
|
||||||
|
}
|
||||||
|
|
||||||
|
func (captcha *CloudflareTurnstileProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
|
||||||
|
reqData := url.Values{
|
||||||
|
"secret": {clientSecret},
|
||||||
|
"response": {token},
|
||||||
|
}
|
||||||
|
resp, err := http.PostForm(CloudflareTurnstileVerifyUrl, reqData)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type captchaResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
ErrorCodes []string `json:"error-codes"`
|
||||||
|
}
|
||||||
|
captchaResp := &captchaResponse{}
|
||||||
|
err = json.Unmarshal(body, captchaResp)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(captchaResp.ErrorCodes) > 0 {
|
||||||
|
return false, errors.New(strings.Join(captchaResp.ErrorCodes, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
return captchaResp.Success, nil
|
||||||
|
}
|
@ -21,3 +21,4 @@ isDemoMode = false
|
|||||||
batchSize = 100
|
batchSize = 100
|
||||||
ldapServerPort = 389
|
ldapServerPort = 389
|
||||||
languages = en,zh,es,fr,de,ja,ko,ru
|
languages = en,zh,es,fr,de,ja,ko,ru
|
||||||
|
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
|
||||||
|
35
conf/conf.go
35
conf/conf.go
@ -15,6 +15,7 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -24,6 +25,15 @@ import (
|
|||||||
"github.com/beego/beego"
|
"github.com/beego/beego"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Quota struct {
|
||||||
|
Organization int `json:"organization"`
|
||||||
|
User int `json:"user"`
|
||||||
|
Application int `json:"application"`
|
||||||
|
Provider int `json:"provider"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var quota = &Quota{-1, -1, -1, -1}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// this array contains the beego configuration items that may be modified via env
|
// this array contains the beego configuration items that may be modified via env
|
||||||
presetConfigItems := []string{"httpport", "appname"}
|
presetConfigItems := []string{"httpport", "appname"}
|
||||||
@ -35,6 +45,17 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
initQuota()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initQuota() {
|
||||||
|
res := beego.AppConfig.String("quota")
|
||||||
|
if res != "" {
|
||||||
|
err := json.Unmarshal([]byte(res), quota)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfigString(key string) string {
|
func GetConfigString(key string) string {
|
||||||
@ -95,3 +116,17 @@ func GetConfigBatchSize() int {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetConfigQuota() *Quota {
|
||||||
|
return quota
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfigRealDataSourceName(driverName string) string {
|
||||||
|
var dataSourceName string
|
||||||
|
if driverName != "mysql" {
|
||||||
|
dataSourceName = GetConfigDataSourceName()
|
||||||
|
} else {
|
||||||
|
dataSourceName = GetConfigDataSourceName() + GetConfigString("dbName")
|
||||||
|
}
|
||||||
|
return dataSourceName
|
||||||
|
}
|
||||||
|
@ -93,3 +93,19 @@ func TestGetConfBool(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetConfigQuota(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
description string
|
||||||
|
expected *Quota
|
||||||
|
}{
|
||||||
|
{"default", &Quota{-1, -1, -1, -1}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := beego.LoadAppConfig("ini", "app.conf")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
for _, scenery := range scenarios {
|
||||||
|
quota := GetConfigQuota()
|
||||||
|
assert.Equal(t, scenery.expected, quota)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -46,7 +46,7 @@ func (c *ApiController) GetApplications() {
|
|||||||
if organization == "" {
|
if organization == "" {
|
||||||
applications = object.GetApplications(owner)
|
applications = object.GetApplications(owner)
|
||||||
} else {
|
} else {
|
||||||
applications = object.GetApplicationsByOrganizationName(owner, organization)
|
applications = object.GetOrganizationApplications(owner, organization)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
||||||
@ -103,17 +103,31 @@ func (c *ApiController) GetUserApplication() {
|
|||||||
// @router /get-organization-applications [get]
|
// @router /get-organization-applications [get]
|
||||||
func (c *ApiController) GetOrganizationApplications() {
|
func (c *ApiController) GetOrganizationApplications() {
|
||||||
userId := c.GetSessionUsername()
|
userId := c.GetSessionUsername()
|
||||||
owner := c.Input().Get("owner")
|
|
||||||
organization := c.Input().Get("organization")
|
organization := c.Input().Get("organization")
|
||||||
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
|
page := c.Input().Get("p")
|
||||||
|
field := c.Input().Get("field")
|
||||||
|
value := c.Input().Get("value")
|
||||||
|
sortField := c.Input().Get("sortField")
|
||||||
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
|
||||||
if organization == "" {
|
if organization == "" {
|
||||||
c.ResponseError(c.T("ParameterErr.OrgMissingErr"))
|
c.ResponseError(c.T("ParameterErr.OrgMissingErr"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
applications := object.GetApplicationsByOrganizationName(owner, organization)
|
if limit == "" || page == "" {
|
||||||
|
var applications []*object.Application
|
||||||
|
applications = object.GetOrganizationApplications(owner, organization)
|
||||||
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetOrganizationApplicationCount(owner, organization, field, value)))
|
||||||
|
applications := object.GetMaskedApplications(object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder), userId)
|
||||||
|
c.ResponseOk(applications, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateApplication
|
// UpdateApplication
|
||||||
@ -153,6 +167,12 @@ func (c *ApiController) AddApplication() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count := object.GetApplicationCount("", "", "")
|
||||||
|
if err := checkQuotaForApplication(count); err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.AddApplication(&application))
|
c.Data["json"] = wrapActionResponse(object.AddApplication(&application))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,16 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/captcha"
|
"github.com/casdoor/casdoor/captcha"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/idp"
|
"github.com/casdoor/casdoor/idp"
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
@ -33,6 +35,11 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
wechatScanType string
|
||||||
|
lock sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
func codeToResponse(code *object.Code) *Response {
|
func codeToResponse(code *object.Code) *Response {
|
||||||
if code.Code == "" {
|
if code.Code == "" {
|
||||||
return &Response{Status: "error", Msg: code.Message, Data: code.Code}
|
return &Response{Status: "error", Msg: code.Message, Data: code.Code}
|
||||||
@ -300,7 +307,7 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", application.Organization))
|
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", application.Organization))
|
||||||
provider := object.GetProvider(fmt.Sprintf("admin/%s", form.Provider))
|
provider := object.GetProvider(util.GetId("admin", form.Provider))
|
||||||
providerItem := application.GetProviderItem(provider.Name)
|
providerItem := application.GetProviderItem(provider.Name)
|
||||||
if !providerItem.IsProviderVisible() {
|
if !providerItem.IsProviderVisible() {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotEnabled"), provider.Name))
|
c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotEnabled"), provider.Name))
|
||||||
@ -531,3 +538,46 @@ func (c *ApiController) HandleSamlLogin() {
|
|||||||
slice[4], relayState, samlResponse)
|
slice[4], relayState, samlResponse)
|
||||||
c.Redirect(targetUrl, 303)
|
c.Redirect(targetUrl, 303)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleOfficialAccountEvent ...
|
||||||
|
// @Tag HandleOfficialAccountEvent API
|
||||||
|
// @Title HandleOfficialAccountEvent
|
||||||
|
// @router /api/webhook [POST]
|
||||||
|
func (c *ApiController) HandleOfficialAccountEvent() {
|
||||||
|
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
}
|
||||||
|
var data struct {
|
||||||
|
MsgType string `xml:"MsgType"`
|
||||||
|
Event string `xml:"Event"`
|
||||||
|
EventKey string `xml:"EventKey"`
|
||||||
|
}
|
||||||
|
err = xml.Unmarshal(respBytes, &data)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
}
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
if data.EventKey != "" {
|
||||||
|
wechatScanType = data.Event
|
||||||
|
c.Ctx.WriteString("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWebhookEventType ...
|
||||||
|
// @Tag GetWebhookEventType API
|
||||||
|
// @Title GetWebhookEventType
|
||||||
|
// @router /api/get-webhook-event [GET]
|
||||||
|
func (c *ApiController) GetWebhookEventType() {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
resp := &Response{
|
||||||
|
Status: "ok",
|
||||||
|
Msg: "",
|
||||||
|
Data: wechatScanType,
|
||||||
|
}
|
||||||
|
c.Data["json"] = resp
|
||||||
|
wechatScanType = ""
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/beego/beego/utils/pagination"
|
"github.com/beego/beego/utils/pagination"
|
||||||
|
xormadapter "github.com/casbin/xorm-adapter/v3"
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
)
|
)
|
||||||
@ -89,6 +90,69 @@ func (c *ApiController) SyncPolicies() {
|
|||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
adapter := object.GetCasbinAdapter(id)
|
adapter := object.GetCasbinAdapter(id)
|
||||||
|
|
||||||
c.Data["json"] = object.SyncPolicies(adapter)
|
policies, err := object.SyncPolicies(adapter)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = policies
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) UpdatePolicy() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
adapter := object.GetCasbinAdapter(id)
|
||||||
|
var policies []xormadapter.CasbinRule
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policies)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := object.UpdatePolicy(util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]), adapter)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = wrapActionResponse(affected)
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) AddPolicy() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
adapter := object.GetCasbinAdapter(id)
|
||||||
|
var policy xormadapter.CasbinRule
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := object.AddPolicy(util.CasbinToSlice(policy), adapter)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = wrapActionResponse(affected)
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) RemovePolicy() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
adapter := object.GetCasbinAdapter(id)
|
||||||
|
var policy xormadapter.CasbinRule
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := object.RemovePolicy(util.CasbinToSlice(policy), adapter)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = wrapActionResponse(affected)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *ApiController) Enforce() {
|
func (c *ApiController) Enforce() {
|
||||||
userId := c.GetSessionUsername()
|
|
||||||
if userId == "" {
|
|
||||||
c.ResponseError(c.T("EnforcerErr.SignInFirst"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var permissionRule object.PermissionRule
|
var permissionRule object.PermissionRule
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRule)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -34,17 +28,11 @@ func (c *ApiController) Enforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.Enforce(userId, &permissionRule)
|
c.Data["json"] = object.Enforce(&permissionRule)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) BatchEnforce() {
|
func (c *ApiController) BatchEnforce() {
|
||||||
userId := c.GetSessionUsername()
|
|
||||||
if userId == "" {
|
|
||||||
c.ResponseError(c.T("EnforcerErr.SignInFirst"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var permissionRules []object.PermissionRule
|
var permissionRules []object.PermissionRule
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRules)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -52,7 +40,7 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.BatchEnforce(userId, permissionRules)
|
c.Data["json"] = object.BatchEnforce(permissionRules)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,12 @@ func (c *ApiController) AddOrganization() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count := object.GetOrganizationCount("", "", "")
|
||||||
|
if err := checkQuotaForOrganization(count); err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization))
|
c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,20 @@ func (c *ApiController) GetPermissionsBySubmitter() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPermissionsByRole
|
||||||
|
// @Title GetPermissionsByRole
|
||||||
|
// @Tag Permission API
|
||||||
|
// @Description get permissions by role
|
||||||
|
// @Param id query string true "The id of the role"
|
||||||
|
// @Success 200 {array} object.Permission The Response object
|
||||||
|
// @router /get-permissions-by-role [get]
|
||||||
|
func (c *ApiController) GetPermissionsByRole() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
permissions := object.GetPermissionsByRole(id)
|
||||||
|
c.ResponseOk(permissions, len(permissions))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// GetPermission
|
// GetPermission
|
||||||
// @Title GetPermission
|
// @Title GetPermission
|
||||||
// @Tag Permission API
|
// @Tag Permission API
|
||||||
|
@ -81,7 +81,6 @@ func (c *ApiController) GetGlobalProviders() {
|
|||||||
// @router /get-provider [get]
|
// @router /get-provider [get]
|
||||||
func (c *ApiController) GetProvider() {
|
func (c *ApiController) GetProvider() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id))
|
c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
@ -123,6 +122,12 @@ func (c *ApiController) AddProvider() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count := object.GetProviderCount("", "", "")
|
||||||
|
if err := checkQuotaForProvider(count); err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.AddProvider(&provider))
|
c.Data["json"] = wrapActionResponse(object.AddProvider(&provider))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,15 @@ func (c *ApiController) GetResources() {
|
|||||||
value := c.Input().Get("value")
|
value := c.Input().Get("value")
|
||||||
sortField := c.Input().Get("sortField")
|
sortField := c.Input().Get("sortField")
|
||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
|
||||||
|
userObj, ok := c.RequireSignedInUser()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if userObj.IsAdmin {
|
||||||
|
user = ""
|
||||||
|
}
|
||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
c.Data["json"] = object.GetResources(owner, user)
|
c.Data["json"] = object.GetResources(owner, user)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
@ -156,7 +165,7 @@ func (c *ApiController) UploadResource() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, user, ok := c.GetProviderFromContext("Storage")
|
provider, _, ok := c.GetProviderFromContext("Storage")
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -171,6 +180,20 @@ func (c *ApiController) UploadResource() {
|
|||||||
fileType, _ = util.GetOwnerAndNameFromId(mimeType)
|
fileType, _ = util.GetOwnerAndNameFromId(mimeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tag != "avatar" && tag != "termsOfUse" {
|
||||||
|
ext := filepath.Ext(filepath.Base(fullFilePath))
|
||||||
|
index := len(fullFilePath) - len(ext)
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
_, objectKey := object.GetUploadFileUrl(provider, fullFilePath, true)
|
||||||
|
if object.GetResourceCount(owner, username, "name", objectKey) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// duplicated fullFilePath found, change it
|
||||||
|
fullFilePath = fullFilePath[:index] + fmt.Sprintf("-%d", i) + ext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileUrl, objectKey, err := object.UploadFileSafe(provider, fullFilePath, fileBuffer)
|
fileUrl, objectKey, err := object.UploadFileSafe(provider, fullFilePath, fileBuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@ -202,13 +225,11 @@ func (c *ApiController) UploadResource() {
|
|||||||
|
|
||||||
switch tag {
|
switch tag {
|
||||||
case "avatar":
|
case "avatar":
|
||||||
if user == nil {
|
user := object.GetUserNoCheck(util.GetId(owner, username))
|
||||||
user = object.GetUserNoCheck(username)
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
c.ResponseError(c.T("ResourceErr.UserIsNil"))
|
c.ResponseError(c.T("ResourceErr.UserIsNil"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
user.Avatar = fileUrl
|
user.Avatar = fileUrl
|
||||||
object.UpdateUser(user.GetId(), user, []string{"avatar"}, false)
|
object.UpdateUser(user.GetId(), user, []string{"avatar"}, false)
|
||||||
|
@ -60,7 +60,7 @@ func (c *ApiController) SendEmail() {
|
|||||||
var provider *object.Provider
|
var provider *object.Provider
|
||||||
if emailForm.Provider != "" {
|
if emailForm.Provider != "" {
|
||||||
// called by frontend's TestEmailWidget, provider name is set by frontend
|
// called by frontend's TestEmailWidget, provider name is set by frontend
|
||||||
provider = object.GetProvider(fmt.Sprintf("admin/%s", emailForm.Provider))
|
provider = object.GetProvider(util.GetId("admin", emailForm.Provider))
|
||||||
} else {
|
} else {
|
||||||
// called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider
|
// called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider
|
||||||
var ok bool
|
var ok bool
|
||||||
|
@ -183,6 +183,12 @@ func (c *ApiController) AddUser() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count := object.GetUserCount("", "", "")
|
||||||
|
if err := checkQuotaForUser(count); err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
|
msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(msg)
|
||||||
|
@ -17,6 +17,7 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/i18n"
|
"github.com/casdoor/casdoor/i18n"
|
||||||
@ -56,7 +57,7 @@ func (c *ApiController) T(error string) string {
|
|||||||
// GetAcceptLanguage ...
|
// GetAcceptLanguage ...
|
||||||
func (c *ApiController) GetAcceptLanguage() string {
|
func (c *ApiController) GetAcceptLanguage() string {
|
||||||
lang := c.Ctx.Request.Header.Get("Accept-Language")
|
lang := c.Ctx.Request.Header.Get("Accept-Language")
|
||||||
if lang == "" {
|
if lang == "" || !strings.Contains(conf.GetConfigString("languages"), lang[0:2]) {
|
||||||
lang = "en"
|
lang = "en"
|
||||||
}
|
}
|
||||||
return lang[0:2]
|
return lang[0:2]
|
||||||
@ -125,7 +126,7 @@ func getInitScore() (int, error) {
|
|||||||
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, *object.User, bool) {
|
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, *object.User, bool) {
|
||||||
providerName := c.Input().Get("provider")
|
providerName := c.Input().Get("provider")
|
||||||
if providerName != "" {
|
if providerName != "" {
|
||||||
provider := object.GetProvider(util.GetId(providerName))
|
provider := object.GetProvider(util.GetId("admin", providerName))
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
c.ResponseError(c.T("ProviderErr.ProviderNotFound"), providerName)
|
c.ResponseError(c.T("ProviderErr.ProviderNotFound"), providerName)
|
||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
@ -152,3 +153,47 @@ func (c *ApiController) GetProviderFromContext(category string) (*object.Provide
|
|||||||
|
|
||||||
return provider, user, true
|
return provider, user, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkQuotaForApplication(count int) error {
|
||||||
|
quota := conf.GetConfigQuota().Application
|
||||||
|
if quota == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if count >= quota {
|
||||||
|
return fmt.Errorf("application quota is exceeded")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkQuotaForOrganization(count int) error {
|
||||||
|
quota := conf.GetConfigQuota().Organization
|
||||||
|
if quota == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if count >= quota {
|
||||||
|
return fmt.Errorf("organization quota is exceeded")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkQuotaForProvider(count int) error {
|
||||||
|
quota := conf.GetConfigQuota().Provider
|
||||||
|
if quota == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if count >= quota {
|
||||||
|
return fmt.Errorf("provider quota is exceeded")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkQuotaForUser(count int) error {
|
||||||
|
quota := conf.GetConfigQuota().User
|
||||||
|
if quota == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if count >= quota {
|
||||||
|
return fmt.Errorf("user quota is exceeded")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -92,6 +92,10 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
user := c.getCurrentUser()
|
user := c.getCurrentUser()
|
||||||
application := object.GetApplication(applicationId)
|
application := object.GetApplication(applicationId)
|
||||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization))
|
organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization))
|
||||||
|
if organization == nil {
|
||||||
|
c.ResponseError(c.T("OrgErr.DoNotExist"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil {
|
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil {
|
||||||
c.ResponseError(c.T("LoginErr.LoginFirst"))
|
c.ResponseError(c.T("LoginErr.LoginFirst"))
|
||||||
@ -114,6 +118,12 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userByEmail := object.GetUserByEmail(organization.Name, dest)
|
||||||
|
if userByEmail == nil {
|
||||||
|
c.ResponseError(c.T("UserErr.DoNotExistSignUp"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
provider := application.GetEmailProvider()
|
provider := application.GetEmailProvider()
|
||||||
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest)
|
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest)
|
||||||
case "phone":
|
case "phone":
|
||||||
@ -124,8 +134,10 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
c.ResponseError(c.T("PhoneErr.NumberInvalid"))
|
c.ResponseError(c.T("PhoneErr.NumberInvalid"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if organization == nil {
|
|
||||||
c.ResponseError(c.T("OrgErr.DoNotExist"))
|
userByPhone := object.GetUserByPhone(organization.Name, dest)
|
||||||
|
if userByPhone == nil {
|
||||||
|
c.ResponseError(c.T("UserErr.DoNotExistSignUp"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,11 @@ func (c *ApiController) WebAuthnSigninBegin() {
|
|||||||
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExistInOrg"), userOwner, userName))
|
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExistInOrg"), userOwner, userName))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(user.WebauthnCredentials) == 0 {
|
||||||
|
c.ResponseError(c.T("UserErr.NoWebAuthnCredential"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
options, sessionData, err := webauthnObj.BeginLogin(user)
|
options, sessionData, err := webauthnObj.BeginLogin(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
|
@ -21,9 +21,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDeployStaticFiles(t *testing.T) {
|
func TestDeployStaticFiles(t *testing.T) {
|
||||||
provider := object.GetProvider("admin/provider_storage_aliyun_oss")
|
provider := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss"))
|
||||||
deployStaticFiles(provider)
|
deployStaticFiles(provider)
|
||||||
}
|
}
|
||||||
|
5
go.mod
5
go.mod
@ -23,6 +23,7 @@ require (
|
|||||||
github.com/go-pay/gopay v1.5.72
|
github.com/go-pay/gopay v1.5.72
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/go-cmp v0.5.8 // indirect
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||||
@ -36,6 +37,7 @@ require (
|
|||||||
github.com/russellhaering/goxmldsig v1.1.1
|
github.com/russellhaering/goxmldsig v1.1.1
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
github.com/tealeg/xlsx v1.0.5
|
github.com/tealeg/xlsx v1.0.5
|
||||||
@ -51,6 +53,7 @@ require (
|
|||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||||
|
xorm.io/builder v0.3.12 // indirect
|
||||||
xorm.io/core v0.7.2
|
xorm.io/core v0.7.2
|
||||||
xorm.io/xorm v1.0.4
|
xorm.io/xorm v1.0.5
|
||||||
)
|
)
|
||||||
|
12
go.sum
12
go.sum
@ -213,8 +213,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
@ -394,6 +395,8 @@ github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKz
|
|||||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
@ -787,10 +790,11 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
|
||||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
|
xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM=
|
||||||
|
xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
||||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||||
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
||||||
xorm.io/xorm v1.0.4 h1:UBXA4I3NhiyjXfPqxXUkS2t5hMta9SSPATeMMaZg9oA=
|
xorm.io/xorm v1.0.5 h1:LRr5PfOUb4ODPR63YwbowkNDwcolT2LnkwP/TUaMaB0=
|
||||||
xorm.io/xorm v1.0.4/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
||||||
|
@ -83,7 +83,7 @@ func parseToData() *I18nData {
|
|||||||
|
|
||||||
data := I18nData{}
|
data := I18nData{}
|
||||||
for _, word := range allWords {
|
for _, word := range allWords {
|
||||||
tokens := strings.Split(word, ":")
|
tokens := strings.SplitN(word, ":", 2)
|
||||||
namespace := tokens[0]
|
namespace := tokens[0]
|
||||||
key := tokens[1]
|
key := tokens[1]
|
||||||
|
|
||||||
|
@ -134,4 +134,5 @@ NameTooLang = Username is too long (maximum is 39 characters).
|
|||||||
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
||||||
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
||||||
InvalidInformation = Invalid information
|
InvalidInformation = Invalid information
|
||||||
|
NoWebAuthnCredential = Found no credentials for this user
|
||||||
|
|
||||||
|
@ -134,4 +134,5 @@ NameTooLang = Username is too long (maximum is 39 characters).
|
|||||||
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
||||||
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
||||||
InvalidInformation = Invalid information
|
InvalidInformation = Invalid information
|
||||||
|
NoWebAuthnCredential = Found no credentials for this user
|
||||||
|
|
||||||
|
@ -134,4 +134,5 @@ NameTooLang = Username is too long (maximum is 39 characters).
|
|||||||
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
||||||
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
||||||
InvalidInformation = Invalid information
|
InvalidInformation = Invalid information
|
||||||
|
NoWebAuthnCredential = Found no credentials for this user
|
||||||
|
|
||||||
|
@ -134,4 +134,5 @@ NameTooLang = Username is too long (maximum is 39 characters).
|
|||||||
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
||||||
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
||||||
InvalidInformation = Invalid information
|
InvalidInformation = Invalid information
|
||||||
|
NoWebAuthnCredential = Found no credentials for this user
|
||||||
|
|
||||||
|
@ -134,4 +134,5 @@ NameTooLang = Username is too long (maximum is 39 characters).
|
|||||||
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
||||||
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
||||||
InvalidInformation = Invalid information
|
InvalidInformation = Invalid information
|
||||||
|
NoWebAuthnCredential = Found no credentials for this user
|
||||||
|
|
||||||
|
@ -134,4 +134,5 @@ NameTooLang = Username is too long (maximum is 39 characters).
|
|||||||
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
||||||
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
||||||
InvalidInformation = Invalid information
|
InvalidInformation = Invalid information
|
||||||
|
NoWebAuthnCredential = Found no credentials for this user
|
||||||
|
|
||||||
|
@ -134,4 +134,5 @@ NameTooLang = Username is too long (maximum is 39 characters).
|
|||||||
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
NameFormatErr = The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.
|
||||||
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
PasswordLessThanSixCharacters = Password must have at least 6 characters
|
||||||
InvalidInformation = Invalid information
|
InvalidInformation = Invalid information
|
||||||
|
NoWebAuthnCredential = Found no credentials for this user
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[ApplicationErr]
|
[ApplicationErr]
|
||||||
AppNotFound = 应用 %%s 未找到
|
AppNotFound = 应用 %s 未找到
|
||||||
AppNotFoundForUserID = 找不到该用户的应用程序 %s
|
AppNotFoundForUserID = 找不到该用户的应用程序 %s
|
||||||
GrantTypeNotSupport = 此应用中不支持此授权类型
|
GrantTypeNotSupport = 此应用中不支持此授权类型
|
||||||
HasNoProviders = 该应用无提供商
|
HasNoProviders = 该应用无提供商
|
||||||
@ -25,7 +25,7 @@ EmptyErr = 邮箱不可为空
|
|||||||
EmailInvalid = 无效邮箱
|
EmailInvalid = 无效邮箱
|
||||||
EmailCheckResult = Email: %s
|
EmailCheckResult = Email: %s
|
||||||
EmptyParam = 邮件参数为空: %v
|
EmptyParam = 邮件参数为空: %v
|
||||||
InvalidReceivers = 无效的邮箱接收者: %%s
|
InvalidReceivers = 无效的邮箱接收者: %s
|
||||||
UnableGetModifyRule = 无法得到Email修改规则
|
UnableGetModifyRule = 无法得到Email修改规则
|
||||||
|
|
||||||
[EnforcerErr]
|
[EnforcerErr]
|
||||||
@ -131,6 +131,7 @@ NameFormatErr = 用户名只能包含字母数字字符、下划线或连字符
|
|||||||
PasswordLessThanSixCharacters = 密码至少为6字符
|
PasswordLessThanSixCharacters = 密码至少为6字符
|
||||||
DoNotExistSignUp = 用户不存在,请先注册
|
DoNotExistSignUp = 用户不存在,请先注册
|
||||||
InvalidInformation = 无效信息
|
InvalidInformation = 无效信息
|
||||||
|
NoWebAuthnCredential = 该用户没有WebAuthn凭据
|
||||||
|
|
||||||
[StorageErr]
|
[StorageErr]
|
||||||
ObjectKeyNotAllowed = object key :%s 不被允许
|
ObjectKeyNotAllowed = object key :%s 不被允许
|
||||||
|
12
i18n/util.go
12
i18n/util.go
@ -17,7 +17,6 @@ package i18n
|
|||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -27,10 +26,7 @@ import (
|
|||||||
//go:embed languages/*.ini
|
//go:embed languages/*.ini
|
||||||
var f embed.FS
|
var f embed.FS
|
||||||
|
|
||||||
var (
|
var langMapConfig = make(map[string]*ini.File)
|
||||||
langMapConfig = make(map[string]*ini.File)
|
|
||||||
isNotFirstLoad = make(map[string]bool)
|
|
||||||
)
|
|
||||||
|
|
||||||
func getI18nFilePath(language string) string {
|
func getI18nFilePath(language string) string {
|
||||||
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
|
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
|
||||||
@ -77,16 +73,14 @@ func applyData(data1 *I18nData, data2 *I18nData) {
|
|||||||
func Translate(lang string, error string) string {
|
func Translate(lang string, error string) string {
|
||||||
parts := strings.Split(error, ".")
|
parts := strings.Split(error, ".")
|
||||||
if !strings.Contains(error, ".") || len(parts) != 2 {
|
if !strings.Contains(error, ".") || len(parts) != 2 {
|
||||||
log.Println("Invalid Error Name")
|
return "Translate Error: " + error
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if isNotFirstLoad[lang] {
|
if langMapConfig[lang] != nil {
|
||||||
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
|
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
|
||||||
} else {
|
} else {
|
||||||
file, _ := f.ReadFile("languages/locale_" + lang + ".ini")
|
file, _ := f.ReadFile("languages/locale_" + lang + ".ini")
|
||||||
langMapConfig[lang], _ = ini.Load(file)
|
langMapConfig[lang], _ = ini.Load(file)
|
||||||
isNotFirstLoad[lang] = true
|
|
||||||
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
|
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,17 @@ package idp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/skip2/go-qrcode"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -191,3 +194,54 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
}
|
}
|
||||||
return &userInfo, nil
|
return &userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
||||||
|
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
||||||
|
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
||||||
|
client := new(http.Client)
|
||||||
|
resp, err := client.Do(request)
|
||||||
|
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var data struct {
|
||||||
|
ExpireIn int `json:"expires_in"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(respBytes, &data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return data.AccessToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWechatOfficialAccountQRCode(clientId string, clientSecret string) (string, error) {
|
||||||
|
accessToken, err := GetWechatOfficialAccountAccessToken(clientId, clientSecret)
|
||||||
|
client := new(http.Client)
|
||||||
|
params := "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"test\"}}}"
|
||||||
|
bodyData := bytes.NewReader([]byte(params))
|
||||||
|
qrCodeUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s", accessToken)
|
||||||
|
requeset, err := http.NewRequest("POST", qrCodeUrl, bodyData)
|
||||||
|
resp, err := client.Do(requeset)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var data struct {
|
||||||
|
Ticket string `json:"ticket"`
|
||||||
|
ExpireSeconds int `json:"expire_seconds"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(respBytes, &data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var png []byte
|
||||||
|
png, err = qrcode.Encode(data.URL, qrcode.Medium, 256)
|
||||||
|
base64Image := base64.StdEncoding.EncodeToString(png)
|
||||||
|
return base64Image, nil
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/idp"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"xorm.io/core"
|
"xorm.io/core"
|
||||||
)
|
)
|
||||||
@ -84,6 +85,16 @@ func GetApplicationCount(owner, field, value string) int {
|
|||||||
return int(count)
|
return int(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetOrganizationApplicationCount(owner, Organization, field, value string) int {
|
||||||
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
|
count, err := session.Count(&Application{Organization: Organization})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
func GetApplications(owner string) []*Application {
|
func GetApplications(owner string) []*Application {
|
||||||
applications := []*Application{}
|
applications := []*Application{}
|
||||||
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner})
|
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner})
|
||||||
@ -94,8 +105,18 @@ func GetApplications(owner string) []*Application {
|
|||||||
return applications
|
return applications
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPaginationApplications(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Application {
|
func GetOrganizationApplications(owner string, organization string) []*Application {
|
||||||
applications := []*Application{}
|
applications := []*Application{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return applications
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationApplications(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Application {
|
||||||
|
var applications []*Application
|
||||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&applications)
|
err := session.Find(&applications)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -105,9 +126,10 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
|
|||||||
return applications
|
return applications
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApplicationsByOrganizationName(owner string, organization string) []*Application {
|
func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Application {
|
||||||
applications := []*Application{}
|
applications := []*Application{}
|
||||||
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization})
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
|
err := session.Find(&applications, &Application{Owner: owner, Organization: organization})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -119,9 +141,11 @@ func getProviderMap(owner string) map[string]*Provider {
|
|||||||
providers := GetProviders(owner)
|
providers := GetProviders(owner)
|
||||||
m := map[string]*Provider{}
|
m := map[string]*Provider{}
|
||||||
for _, provider := range providers {
|
for _, provider := range providers {
|
||||||
//if provider.Category != "OAuth" {
|
// Get QRCode only once
|
||||||
// continue
|
if provider.Type == "WeChat" && provider.DisableSsl == true && provider.Content == "" {
|
||||||
//}
|
provider.Content, _ = idp.GetWechatOfficialAccountQRCode(provider.ClientId2, provider.ClientSecret2)
|
||||||
|
UpdateProvider(provider.Owner+"/"+provider.Name, provider)
|
||||||
|
}
|
||||||
|
|
||||||
m[provider.Name] = GetMaskedProvider(provider)
|
m[provider.Name] = GetMaskedProvider(provider)
|
||||||
}
|
}
|
||||||
@ -129,7 +153,7 @@ func getProviderMap(owner string) map[string]*Provider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func extendApplicationWithProviders(application *Application) {
|
func extendApplicationWithProviders(application *Application) {
|
||||||
m := getProviderMap(application.Owner)
|
m := getProviderMap(application.Organization)
|
||||||
for _, providerItem := range application.Providers {
|
for _, providerItem := range application.Providers {
|
||||||
if provider, ok := m[providerItem.Name]; ok {
|
if provider, ok := m[providerItem.Name]; ok {
|
||||||
providerItem.Provider = provider
|
providerItem.Provider = provider
|
||||||
@ -378,7 +402,7 @@ func IsAllowOrigin(origin string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getApplicationMap(organization string) map[string]*Application {
|
func getApplicationMap(organization string) map[string]*Application {
|
||||||
applications := GetApplicationsByOrganizationName("admin", organization)
|
applications := GetOrganizationApplications("admin", organization)
|
||||||
|
|
||||||
applicationMap := make(map[string]*Application)
|
applicationMap := make(map[string]*Application)
|
||||||
for _, application := range applications {
|
for _, application := range applications {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
func (application *Application) GetProviderByCategory(category string) *Provider {
|
func (application *Application) GetProviderByCategory(category string) *Provider {
|
||||||
providers := GetProviders(application.Owner)
|
providers := GetProviders(application.Organization)
|
||||||
m := map[string]*Provider{}
|
m := map[string]*Provider{}
|
||||||
for _, provider := range providers {
|
for _, provider := range providers {
|
||||||
if provider.Category != category {
|
if provider.Category != category {
|
||||||
|
@ -60,7 +60,7 @@ func getPermanentAvatarUrl(organization string, username string, url string, upl
|
|||||||
}
|
}
|
||||||
|
|
||||||
fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username)
|
fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username)
|
||||||
uploadedFileUrl, _ := getUploadFileUrl(defaultStorageProvider, fullFilePath, false)
|
uploadedFileUrl, _ := GetUploadFileUrl(defaultStorageProvider, fullFilePath, false)
|
||||||
|
|
||||||
if upload {
|
if upload {
|
||||||
DownloadAndUpload(url, fullFilePath)
|
DownloadAndUpload(url, fullFilePath)
|
||||||
|
@ -148,34 +148,7 @@ func (casbinAdapter *CasbinAdapter) getTable() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func safeReturn(policy []string, i int) string {
|
func initEnforcer(modelObj *Model, casbinAdapter *CasbinAdapter) (*casbin.Enforcer, error) {
|
||||||
if len(policy) > i {
|
|
||||||
return policy[i]
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func matrixToCasbinRules(pType string, policies [][]string) []*xormadapter.CasbinRule {
|
|
||||||
res := []*xormadapter.CasbinRule{}
|
|
||||||
|
|
||||||
for _, policy := range policies {
|
|
||||||
line := xormadapter.CasbinRule{
|
|
||||||
Ptype: pType,
|
|
||||||
V0: safeReturn(policy, 0),
|
|
||||||
V1: safeReturn(policy, 1),
|
|
||||||
V2: safeReturn(policy, 2),
|
|
||||||
V3: safeReturn(policy, 3),
|
|
||||||
V4: safeReturn(policy, 4),
|
|
||||||
V5: safeReturn(policy, 5),
|
|
||||||
}
|
|
||||||
res = append(res, &line)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
|
|
||||||
// init Adapter
|
// init Adapter
|
||||||
if casbinAdapter.Adapter == nil {
|
if casbinAdapter.Adapter == nil {
|
||||||
var dataSourceName string
|
var dataSourceName string
|
||||||
@ -191,20 +164,60 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
|
|||||||
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
|
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
|
||||||
}
|
}
|
||||||
|
|
||||||
casbinAdapter.Adapter, _ = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "")
|
var err error
|
||||||
|
casbinAdapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// init Model
|
// init Model
|
||||||
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
|
||||||
m, err := model.NewModelFromString(modelObj.ModelText)
|
m, err := model.NewModelFromString(modelObj.ModelText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// init Enforcer
|
// init Enforcer
|
||||||
enforcer, err := casbin.NewEnforcer(m, casbinAdapter.Adapter)
|
enforcer, err := casbin.NewEnforcer(m, casbinAdapter.Adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return enforcer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeReturn(policy []string, i int) string {
|
||||||
|
if len(policy) > i {
|
||||||
|
return policy[i]
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func matrixToCasbinRules(Ptype string, policies [][]string) []*xormadapter.CasbinRule {
|
||||||
|
res := []*xormadapter.CasbinRule{}
|
||||||
|
|
||||||
|
for _, policy := range policies {
|
||||||
|
line := xormadapter.CasbinRule{
|
||||||
|
Ptype: Ptype,
|
||||||
|
V0: safeReturn(policy, 0),
|
||||||
|
V1: safeReturn(policy, 1),
|
||||||
|
V2: safeReturn(policy, 2),
|
||||||
|
V3: safeReturn(policy, 3),
|
||||||
|
V4: safeReturn(policy, 4),
|
||||||
|
V5: safeReturn(policy, 5),
|
||||||
|
}
|
||||||
|
res = append(res, &line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func SyncPolicies(casbinAdapter *CasbinAdapter) ([]*xormadapter.CasbinRule, error) {
|
||||||
|
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||||
|
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
policies := matrixToCasbinRules("p", enforcer.GetPolicy())
|
policies := matrixToCasbinRules("p", enforcer.GetPolicy())
|
||||||
@ -212,5 +225,48 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
|
|||||||
policies = append(policies, matrixToCasbinRules("g", enforcer.GetGroupingPolicy())...)
|
policies = append(policies, matrixToCasbinRules("g", enforcer.GetGroupingPolicy())...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return policies
|
return policies, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePolicy(oldPolicy, newPolicy []string, casbinAdapter *CasbinAdapter) (bool, error) {
|
||||||
|
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||||
|
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := enforcer.UpdatePolicy(oldPolicy, newPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
return affected, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddPolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) {
|
||||||
|
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||||
|
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := enforcer.AddPolicy(policy)
|
||||||
|
if err != nil {
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
return affected, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemovePolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) {
|
||||||
|
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||||
|
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := enforcer.RemovePolicy(policy)
|
||||||
|
if err != nil {
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected, nil
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ func initBuiltInOrganization() bool {
|
|||||||
PhonePrefix: "86",
|
PhonePrefix: "86",
|
||||||
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
||||||
Tags: []string{},
|
Tags: []string{},
|
||||||
|
Languages: []string{"en", "zh", "es", "fr", "de", "ja", "ko", "ru"},
|
||||||
AccountItems: []*AccountItem{
|
AccountItems: []*AccountItem{
|
||||||
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||||
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||||
@ -157,7 +158,7 @@ func initBuiltInApplication() {
|
|||||||
},
|
},
|
||||||
RedirectUris: []string{},
|
RedirectUris: []string{},
|
||||||
ExpireInHours: 168,
|
ExpireInHours: 168,
|
||||||
FormOffset: 8,
|
FormOffset: 2,
|
||||||
}
|
}
|
||||||
AddApplication(application)
|
AddApplication(application)
|
||||||
}
|
}
|
||||||
@ -221,7 +222,7 @@ func initBuiltInLdap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initBuiltInProvider() {
|
func initBuiltInProvider() {
|
||||||
provider := GetProvider("admin/provider_captcha_default")
|
provider := GetProvider(util.GetId("admin", "provider_captcha_default"))
|
||||||
if provider != nil {
|
if provider != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ func initDefinedLdap(ldap *Ldap) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initDefinedProvider(provider *Provider) {
|
func initDefinedProvider(provider *Provider) {
|
||||||
existed := GetProvider(provider.GetId())
|
existed := GetProvider(util.GetId("admin", provider.Name))
|
||||||
if existed != nil {
|
if existed != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ type Organization struct {
|
|||||||
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
|
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
|
||||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||||
|
Languages []string `xorm:"varchar(255)" json:"languages"`
|
||||||
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
||||||
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
||||||
IsProfilePublic bool `json:"isProfilePublic"`
|
IsProfilePublic bool `json:"isProfilePublic"`
|
||||||
@ -222,7 +223,12 @@ func GetDefaultApplication(id string) (*Application, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if organization.DefaultApplication != "" {
|
if organization.DefaultApplication != "" {
|
||||||
return getApplication("admin", organization.DefaultApplication), fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
|
defaultApplication := getApplication("admin", organization.DefaultApplication)
|
||||||
|
if defaultApplication == nil {
|
||||||
|
return nil, fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
|
||||||
|
} else {
|
||||||
|
return defaultApplication, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applications := []*Application{}
|
applications := []*Application{}
|
||||||
|
@ -111,7 +111,27 @@ func GetPermission(id string) *Permission {
|
|||||||
return getPermission(owner, name)
|
return getPermission(owner, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkPermissionValid verifies if the permission is valid
|
||||||
|
func checkPermissionValid(permission *Permission) {
|
||||||
|
enforcer := getEnforcer(permission)
|
||||||
|
enforcer.EnableAutoSave(false)
|
||||||
|
policies, groupingPolicies := getPolicies(permission)
|
||||||
|
|
||||||
|
if len(groupingPolicies) > 0 {
|
||||||
|
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := enforcer.AddPolicies(policies)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func UpdatePermission(id string, permission *Permission) bool {
|
func UpdatePermission(id string, permission *Permission) bool {
|
||||||
|
checkPermissionValid(permission)
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
oldPermission := getPermission(owner, name)
|
oldPermission := getPermission(owner, name)
|
||||||
if oldPermission == nil {
|
if oldPermission == nil {
|
||||||
|
@ -29,7 +29,9 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
|
|||||||
tableName = permission.Adapter
|
tableName = permission.Adapter
|
||||||
}
|
}
|
||||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName()+conf.GetConfigString("dbName"), tableName, tableNamePrefix, true)
|
driverName := conf.GetConfigString("driverName")
|
||||||
|
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
|
||||||
|
adapter, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, tableName, tableNamePrefix, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -63,22 +65,27 @@ m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load Policy with a specific Permission
|
||||||
|
enforcer.LoadFilteredPolicy(xormadapter.Filter{
|
||||||
|
V5: []string{permission.Owner + "/" + permission.Name},
|
||||||
|
})
|
||||||
return enforcer
|
return enforcer
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPolicies(permission *Permission) ([][]string, [][]string) {
|
func getPolicies(permission *Permission) ([][]string, [][]string) {
|
||||||
var policies [][]string
|
var policies [][]string
|
||||||
var groupingPolicies [][]string
|
var groupingPolicies [][]string
|
||||||
|
permissionId := permission.Owner + "/" + permission.Name
|
||||||
domainExist := len(permission.Domains) > 0
|
domainExist := len(permission.Domains) > 0
|
||||||
for _, user := range permission.Users {
|
for _, user := range permission.Users {
|
||||||
for _, resource := range permission.Resources {
|
for _, resource := range permission.Resources {
|
||||||
for _, action := range permission.Actions {
|
for _, action := range permission.Actions {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
policies = append(policies, []string{user, domain, resource, strings.ToLower(action)})
|
policies = append(policies, []string{user, domain, resource, strings.ToLower(action), "", permissionId})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
policies = append(policies, []string{user, resource, strings.ToLower(action)})
|
policies = append(policies, []string{user, resource, strings.ToLower(action), "", "", permissionId})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,29 +95,29 @@ func getPolicies(permission *Permission) ([][]string, [][]string) {
|
|||||||
for _, subUser := range roleObj.Users {
|
for _, subUser := range roleObj.Users {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role})
|
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role, "", "", permissionId})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subUser, role})
|
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, subRole := range roleObj.Roles {
|
for _, subRole := range roleObj.Roles {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role})
|
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role, "", "", permissionId})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subRole, role})
|
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, resource := range permission.Resources {
|
for _, resource := range permission.Resources {
|
||||||
for _, action := range permission.Actions {
|
for _, action := range permission.Actions {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
policies = append(policies, []string{role, domain, resource, strings.ToLower(action)})
|
policies = append(policies, []string{role, domain, resource, strings.ToLower(action), "", permissionId})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
policies = append(policies, []string{role, resource, strings.ToLower(action)})
|
policies = append(policies, []string{role, resource, strings.ToLower(action), "", "", permissionId})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,20 +159,29 @@ func removePolicies(permission *Permission) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Enforce(userId string, permissionRule *PermissionRule) bool {
|
func Enforce(permissionRule *PermissionRule) bool {
|
||||||
permission := GetPermission(permissionRule.Id)
|
permission := GetPermission(permissionRule.Id)
|
||||||
enforcer := getEnforcer(permission)
|
enforcer := getEnforcer(permission)
|
||||||
allow, err := enforcer.Enforce(userId, permissionRule.V1, permissionRule.V2)
|
|
||||||
|
request := []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2}
|
||||||
|
if permissionRule.V3 != "" {
|
||||||
|
request = append(request, permissionRule.V3)
|
||||||
|
}
|
||||||
|
allow, err := enforcer.Enforce(request...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return allow
|
return allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func BatchEnforce(userId string, permissionRules []PermissionRule) []bool {
|
func BatchEnforce(permissionRules []PermissionRule) []bool {
|
||||||
var requests [][]interface{}
|
var requests [][]interface{}
|
||||||
for _, permissionRule := range permissionRules {
|
for _, permissionRule := range permissionRules {
|
||||||
requests = append(requests, []interface{}{userId, permissionRule.V1, permissionRule.V2})
|
if permissionRule.V3 != "" {
|
||||||
|
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2, permissionRule.V3})
|
||||||
|
} else {
|
||||||
|
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
permission := GetPermission(permissionRules[0].Id)
|
permission := GetPermission(permissionRules[0].Id)
|
||||||
enforcer := getEnforcer(permission)
|
enforcer := getEnforcer(permission)
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk unique" 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"`
|
||||||
@ -46,9 +46,9 @@ type Provider struct {
|
|||||||
|
|
||||||
Host string `xorm:"varchar(100)" json:"host"`
|
Host string `xorm:"varchar(100)" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
DisableSsl bool `json:"disableSsl"`
|
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode
|
||||||
Title string `xorm:"varchar(100)" json:"title"`
|
Title string `xorm:"varchar(100)" json:"title"`
|
||||||
Content string `xorm:"varchar(1000)" json:"content"`
|
Content string `xorm:"varchar(1000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
|
||||||
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
||||||
|
|
||||||
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
||||||
@ -93,8 +93,8 @@ func GetMaskedProviders(providers []*Provider) []*Provider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetProviderCount(owner, field, value string) int {
|
func GetProviderCount(owner, field, value string) int {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession("", -1, -1, field, value, "", "")
|
||||||
count, err := session.Count(&Provider{})
|
count, err := session.Where("owner = ? or owner = ? ", "admin", owner).Count(&Provider{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ func GetGlobalProviderCount(field, value string) int {
|
|||||||
|
|
||||||
func GetProviders(owner string) []*Provider {
|
func GetProviders(owner string) []*Provider {
|
||||||
providers := []*Provider{}
|
providers := []*Provider{}
|
||||||
err := adapter.Engine.Desc("created_time").Find(&providers, &Provider{Owner: owner})
|
err := adapter.Engine.Where("owner = ? or owner = ? ", "admin", owner).Desc("created_time").Find(&providers, &Provider{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -133,9 +133,9 @@ func GetGlobalProviders() []*Provider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Provider {
|
func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Provider {
|
||||||
var providers []*Provider
|
providers := []*Provider{}
|
||||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&providers)
|
err := session.Where("owner = ? or owner = ? ", "admin", owner).Find(&providers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ func GetPaginationProviders(owner string, offset, limit int, field, value, sortF
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetPaginationGlobalProviders(offset, limit int, field, value, sortField, sortOrder string) []*Provider {
|
func GetPaginationGlobalProviders(offset, limit int, field, value, sortField, sortOrder string) []*Provider {
|
||||||
var providers []*Provider
|
providers := []*Provider{}
|
||||||
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&providers)
|
err := session.Find(&providers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -159,7 +159,7 @@ func getProvider(owner string, name string) *Provider {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := Provider{Owner: owner, Name: name}
|
provider := Provider{Name: name}
|
||||||
existed, err := adapter.Engine.Get(&provider)
|
existed, err := adapter.Engine.Get(&provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
type ProviderItem struct {
|
type ProviderItem struct {
|
||||||
|
Owner string `json:"owner"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
CanSignUp bool `json:"canSignUp"`
|
CanSignUp bool `json:"canSignUp"`
|
||||||
CanSignIn bool `json:"canSignIn"`
|
CanSignIn bool `json:"canSignIn"`
|
||||||
CanUnlink bool `json:"canUnlink"`
|
CanUnlink bool `json:"canUnlink"`
|
||||||
|
@ -54,7 +54,7 @@ func escapePath(path string) string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
||||||
escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath))
|
escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath))
|
||||||
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
|
|||||||
UpdateProvider(provider.GetId(), provider)
|
UpdateProvider(provider.GetId(), provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -678,7 +678,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
|
|||||||
ErrorDescription: "the application does not support wechat mini program",
|
ErrorDescription: "the application does not support wechat mini program",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
provider := GetProvider(util.GetId(mpProvider.Name))
|
provider := GetProvider(util.GetId("admin", mpProvider.Name))
|
||||||
mpIdp := idp.NewWeChatMiniProgramIdProvider(provider.ClientId, provider.ClientSecret)
|
mpIdp := idp.NewWeChatMiniProgramIdProvider(provider.ClientId, provider.ClientSecret)
|
||||||
session, err := mpIdp.GetSessionByCode(code)
|
session, err := mpIdp.GetSessionByCode(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
type Claims struct {
|
type Claims struct {
|
||||||
*User
|
*User
|
||||||
|
TokenType string `json:"tokenType,omitempty"`
|
||||||
Nonce string `json:"nonce,omitempty"`
|
Nonce string `json:"nonce,omitempty"`
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
Scope string `json:"scope,omitempty"`
|
Scope string `json:"scope,omitempty"`
|
||||||
@ -37,6 +38,7 @@ type UserShort struct {
|
|||||||
|
|
||||||
type ClaimsShort struct {
|
type ClaimsShort struct {
|
||||||
*UserShort
|
*UserShort
|
||||||
|
TokenType string `json:"tokenType,omitempty"`
|
||||||
Nonce string `json:"nonce,omitempty"`
|
Nonce string `json:"nonce,omitempty"`
|
||||||
Scope string `json:"scope,omitempty"`
|
Scope string `json:"scope,omitempty"`
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
@ -53,6 +55,7 @@ func getShortUser(user *User) *UserShort {
|
|||||||
func getShortClaims(claims Claims) ClaimsShort {
|
func getShortClaims(claims Claims) ClaimsShort {
|
||||||
res := ClaimsShort{
|
res := ClaimsShort{
|
||||||
UserShort: getShortUser(claims.User),
|
UserShort: getShortUser(claims.User),
|
||||||
|
TokenType: claims.TokenType,
|
||||||
Nonce: claims.Nonce,
|
Nonce: claims.Nonce,
|
||||||
Scope: claims.Scope,
|
Scope: claims.Scope,
|
||||||
RegisteredClaims: claims.RegisteredClaims,
|
RegisteredClaims: claims.RegisteredClaims,
|
||||||
@ -73,6 +76,7 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
|||||||
|
|
||||||
claims := Claims{
|
claims := Claims{
|
||||||
User: user,
|
User: user,
|
||||||
|
TokenType: "access-token",
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
// FIXME: A workaround for custom claim by reusing `tag` in user info
|
// FIXME: A workaround for custom claim by reusing `tag` in user info
|
||||||
Tag: user.Tag,
|
Tag: user.Tag,
|
||||||
@ -97,10 +101,12 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
|||||||
|
|
||||||
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
|
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
|
||||||
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||||
|
claimsShort.TokenType = "refresh-token"
|
||||||
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
|
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
|
||||||
} else {
|
} else {
|
||||||
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||||
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||||
|
claims.TokenType = "refresh-token"
|
||||||
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,38 +41,7 @@ type VerificationRecord struct {
|
|||||||
IsUsed bool
|
IsUsed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
func IsAllowSend(user *User, remoteAddr, recordType string) error {
|
||||||
if provider == nil {
|
|
||||||
return fmt.Errorf("please set an Email provider first")
|
|
||||||
}
|
|
||||||
|
|
||||||
sender := organization.DisplayName
|
|
||||||
title := provider.Title
|
|
||||||
code := getRandomCode(6)
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
|
||||||
content := fmt.Sprintf(provider.Content, code)
|
|
||||||
|
|
||||||
if err := SendEmail(provider, title, content, dest, sender); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
|
||||||
if provider == nil {
|
|
||||||
return errors.New("please set a SMS provider first")
|
|
||||||
}
|
|
||||||
|
|
||||||
code := getRandomCode(6)
|
|
||||||
if err := SendSms(provider, code, dest); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordType, dest, code string) error {
|
|
||||||
var record VerificationRecord
|
var record VerificationRecord
|
||||||
record.RemoteAddr = remoteAddr
|
record.RemoteAddr = remoteAddr
|
||||||
record.Type = recordType
|
record.Type = recordType
|
||||||
@ -89,6 +58,63 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
|
|||||||
return errors.New("you can only send one code in 60s")
|
return errors.New("you can only send one code in 60s")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
||||||
|
if provider == nil {
|
||||||
|
return fmt.Errorf("please set an Email provider first")
|
||||||
|
}
|
||||||
|
|
||||||
|
sender := organization.DisplayName
|
||||||
|
title := provider.Title
|
||||||
|
code := getRandomCode(6)
|
||||||
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
|
content := fmt.Sprintf(provider.Content, code)
|
||||||
|
|
||||||
|
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := SendEmail(provider, title, content, dest, sender); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
||||||
|
if provider == nil {
|
||||||
|
return errors.New("please set a SMS provider first")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
code := getRandomCode(6)
|
||||||
|
if err := SendSms(provider, code, dest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordType, dest, code string) error {
|
||||||
|
var record VerificationRecord
|
||||||
|
record.RemoteAddr = remoteAddr
|
||||||
|
record.Type = recordType
|
||||||
|
if user != nil {
|
||||||
|
record.User = user.GetId()
|
||||||
|
}
|
||||||
record.Owner = provider.Owner
|
record.Owner = provider.Owner
|
||||||
record.Name = util.GenerateId()
|
record.Name = util.GenerateId()
|
||||||
record.CreatedTime = util.GetCurrentTime()
|
record.CreatedTime = util.GetCurrentTime()
|
||||||
@ -99,10 +125,10 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
|
|||||||
|
|
||||||
record.Receiver = dest
|
record.Receiver = dest
|
||||||
record.Code = code
|
record.Code = code
|
||||||
record.Time = now
|
record.Time = time.Now().Unix()
|
||||||
record.IsUsed = false
|
record.IsUsed = false
|
||||||
|
|
||||||
_, err = adapter.Engine.Insert(record)
|
_, err := adapter.Engine.Insert(record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ type Webhook struct {
|
|||||||
Method string `xorm:"varchar(100)" json:"method"`
|
Method string `xorm:"varchar(100)" json:"method"`
|
||||||
ContentType string `xorm:"varchar(100)" json:"contentType"`
|
ContentType string `xorm:"varchar(100)" json:"contentType"`
|
||||||
Headers []*Header `xorm:"mediumtext" json:"headers"`
|
Headers []*Header `xorm:"mediumtext" json:"headers"`
|
||||||
Events []string `xorm:"varchar(100)" json:"events"`
|
Events []string `xorm:"varchar(1000)" json:"events"`
|
||||||
IsUserExtended bool `json:"isUserExtended"`
|
IsUserExtended bool `json:"isUserExtended"`
|
||||||
IsEnabled bool `json:"isEnabled"`
|
IsEnabled bool `json:"isEnabled"`
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,8 @@ func initAPI() {
|
|||||||
beego.Router("/api/get-saml-login", &controllers.ApiController{}, "GET:GetSamlLogin")
|
beego.Router("/api/get-saml-login", &controllers.ApiController{}, "GET:GetSamlLogin")
|
||||||
beego.Router("/api/acs", &controllers.ApiController{}, "POST:HandleSamlLogin")
|
beego.Router("/api/acs", &controllers.ApiController{}, "POST:HandleSamlLogin")
|
||||||
beego.Router("/api/saml/metadata", &controllers.ApiController{}, "GET:GetSamlMeta")
|
beego.Router("/api/saml/metadata", &controllers.ApiController{}, "GET:GetSamlMeta")
|
||||||
|
beego.Router("/api/webhook", &controllers.ApiController{}, "POST:HandleOfficialAccountEvent")
|
||||||
|
beego.Router("/api/get-webhook-event", &controllers.ApiController{}, "GET:GetWebhookEventType")
|
||||||
|
|
||||||
beego.Router("/api/get-organizations", &controllers.ApiController{}, "GET:GetOrganizations")
|
beego.Router("/api/get-organizations", &controllers.ApiController{}, "GET:GetOrganizations")
|
||||||
beego.Router("/api/get-organization", &controllers.ApiController{}, "GET:GetOrganization")
|
beego.Router("/api/get-organization", &controllers.ApiController{}, "GET:GetOrganization")
|
||||||
@ -80,6 +82,7 @@ func initAPI() {
|
|||||||
|
|
||||||
beego.Router("/api/get-permissions", &controllers.ApiController{}, "GET:GetPermissions")
|
beego.Router("/api/get-permissions", &controllers.ApiController{}, "GET:GetPermissions")
|
||||||
beego.Router("/api/get-permissions-by-submitter", &controllers.ApiController{}, "GET:GetPermissionsBySubmitter")
|
beego.Router("/api/get-permissions-by-submitter", &controllers.ApiController{}, "GET:GetPermissionsBySubmitter")
|
||||||
|
beego.Router("/api/get-permissions-by-role", &controllers.ApiController{}, "GET:GetPermissionsByRole")
|
||||||
beego.Router("/api/get-permission", &controllers.ApiController{}, "GET:GetPermission")
|
beego.Router("/api/get-permission", &controllers.ApiController{}, "GET:GetPermission")
|
||||||
beego.Router("/api/update-permission", &controllers.ApiController{}, "POST:UpdatePermission")
|
beego.Router("/api/update-permission", &controllers.ApiController{}, "POST:UpdatePermission")
|
||||||
beego.Router("/api/add-permission", &controllers.ApiController{}, "POST:AddPermission")
|
beego.Router("/api/add-permission", &controllers.ApiController{}, "POST:AddPermission")
|
||||||
@ -103,6 +106,9 @@ func initAPI() {
|
|||||||
beego.Router("/api/add-adapter", &controllers.ApiController{}, "POST:AddCasbinAdapter")
|
beego.Router("/api/add-adapter", &controllers.ApiController{}, "POST:AddCasbinAdapter")
|
||||||
beego.Router("/api/delete-adapter", &controllers.ApiController{}, "POST:DeleteCasbinAdapter")
|
beego.Router("/api/delete-adapter", &controllers.ApiController{}, "POST:DeleteCasbinAdapter")
|
||||||
beego.Router("/api/sync-policies", &controllers.ApiController{}, "GET:SyncPolicies")
|
beego.Router("/api/sync-policies", &controllers.ApiController{}, "GET:SyncPolicies")
|
||||||
|
beego.Router("/api/update-policy", &controllers.ApiController{}, "POST:UpdatePolicy")
|
||||||
|
beego.Router("/api/add-policy", &controllers.ApiController{}, "POST:AddPolicy")
|
||||||
|
beego.Router("/api/remove-policy", &controllers.ApiController{}, "POST:RemovePolicy")
|
||||||
|
|
||||||
beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword")
|
beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword")
|
||||||
beego.Router("/api/check-user-password", &controllers.ApiController{}, "POST:CheckUserPassword")
|
beego.Router("/api/check-user-password", &controllers.ApiController{}, "POST:CheckUserPassword")
|
||||||
|
@ -123,8 +123,8 @@ func GenerateSimpleTimeId() string {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetId(name string) string {
|
func GetId(owner, name string) string {
|
||||||
return fmt.Sprintf("admin/%s", name)
|
return fmt.Sprintf("%s/%s", owner, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMd5Hash(text string) string {
|
func GetMd5Hash(text string) string {
|
||||||
|
@ -137,16 +137,16 @@ func TestGenerateId(t *testing.T) {
|
|||||||
func TestGetId(t *testing.T) {
|
func TestGetId(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
description string
|
description string
|
||||||
input string
|
input []string
|
||||||
expected interface{}
|
expected interface{}
|
||||||
}{
|
}{
|
||||||
{"Scenery one", "casdoor", "admin/casdoor"},
|
{"Scenery one", []string{"admin", "casdoor"}, "admin/casdoor"},
|
||||||
{"Scenery two", "casbin", "admin/casbin"},
|
{"Scenery two", []string{"admin", "casbin"}, "admin/casbin"},
|
||||||
{"Scenery three", "lorem ipsum", "admin/lorem ipsum"},
|
{"Scenery three", []string{"test", "lorem ipsum"}, "test/lorem ipsum"},
|
||||||
}
|
}
|
||||||
for _, scenery := range scenarios {
|
for _, scenery := range scenarios {
|
||||||
t.Run(scenery.description, func(t *testing.T) {
|
t.Run(scenery.description, func(t *testing.T) {
|
||||||
actual := GetId(scenery.input)
|
actual := GetId(scenery.input[0], scenery.input[1])
|
||||||
assert.Equal(t, scenery.expected, actual, "This not is a valid MD5")
|
assert.Equal(t, scenery.expected, actual, "This not is a valid MD5")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
29
util/struct.go
Normal file
29
util/struct.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import xormadapter "github.com/casbin/xorm-adapter/v3"
|
||||||
|
|
||||||
|
func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
|
||||||
|
s := []string{
|
||||||
|
casbinRule.V0,
|
||||||
|
casbinRule.V1,
|
||||||
|
casbinRule.V2,
|
||||||
|
casbinRule.V3,
|
||||||
|
casbinRule.V4,
|
||||||
|
casbinRule.V5,
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^9.3.2",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
"antd": "^4.22.8",
|
"antd": "5.0.3",
|
||||||
"codemirror": "^5.61.1",
|
"codemirror": "^5.61.1",
|
||||||
"copy-to-clipboard": "^3.3.1",
|
"copy-to-clipboard": "^3.3.1",
|
||||||
"core-js": "^3.25.0",
|
"core-js": "^3.25.0",
|
||||||
|
@ -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, InputNumber, Row, Select, Switch, Table, Tooltip} from "antd";
|
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
|
||||||
import * as AdapterBackend from "./backend/AdapterBackend";
|
import * as AdapterBackend from "./backend/AdapterBackend";
|
||||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
@ -21,7 +21,7 @@ import i18next from "i18next";
|
|||||||
|
|
||||||
import "codemirror/lib/codemirror.css";
|
import "codemirror/lib/codemirror.css";
|
||||||
import * as ModelBackend from "./backend/ModelBackend";
|
import * as ModelBackend from "./backend/ModelBackend";
|
||||||
import {EditOutlined, MinusOutlined} from "@ant-design/icons";
|
import PolicyTable from "./common/PoliciyTable";
|
||||||
require("codemirror/theme/material-darker.css");
|
require("codemirror/theme/material-darker.css");
|
||||||
require("codemirror/mode/javascript/javascript");
|
require("codemirror/mode/javascript/javascript");
|
||||||
|
|
||||||
@ -32,12 +32,11 @@ class AdapterEditPage extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
classes: props,
|
classes: props,
|
||||||
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
adapterName: props.match.params.adapterName,
|
adapterName: props.match.params.adapterName,
|
||||||
adapter: null,
|
adapter: null,
|
||||||
organizations: [],
|
organizations: [],
|
||||||
models: [],
|
models: [],
|
||||||
policyLists: [],
|
|
||||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -48,7 +47,7 @@ class AdapterEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAdapter() {
|
getAdapter() {
|
||||||
AdapterBackend.getAdapter(this.state.organizationName, this.state.adapterName)
|
AdapterBackend.getAdapter(this.state.owner, this.state.adapterName)
|
||||||
.then((adapter) => {
|
.then((adapter) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
@ -93,93 +92,6 @@ class AdapterEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
synPolicies() {
|
|
||||||
this.setState({loading: true});
|
|
||||||
AdapterBackend.syncPolicies(this.state.adapter.owner, this.state.adapter.name)
|
|
||||||
.then((res) => {
|
|
||||||
this.setState({loading: false, policyLists: res});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
this.setState({loading: false});
|
|
||||||
Setting.showMessage("error", `Adapter failed to get policies: ${error}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTable(table) {
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: "Rule Type",
|
|
||||||
dataIndex: "PType",
|
|
||||||
key: "PType",
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "V0",
|
|
||||||
dataIndex: "V0",
|
|
||||||
key: "V0",
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "V1",
|
|
||||||
dataIndex: "V1",
|
|
||||||
key: "V1",
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "V2",
|
|
||||||
dataIndex: "V2",
|
|
||||||
key: "V2",
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "V3",
|
|
||||||
dataIndex: "V3",
|
|
||||||
key: "V3",
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "V4",
|
|
||||||
dataIndex: "V4",
|
|
||||||
key: "V4",
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "V5",
|
|
||||||
dataIndex: "V5",
|
|
||||||
key: "V5",
|
|
||||||
width: "100px",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Option",
|
|
||||||
key: "option",
|
|
||||||
width: "100px",
|
|
||||||
render: (text, record, index) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Tooltip placement="topLeft" title="Edit">
|
|
||||||
<Button style={{marginRight: "0.5rem"}} icon={<EditOutlined />} size="small" />
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip placement="topLeft" title="Delete">
|
|
||||||
<Button icon={<MinusOutlined />} size="small" />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Table
|
|
||||||
pagination={{
|
|
||||||
defaultPageSize: 10,
|
|
||||||
}}
|
|
||||||
columns={columns} dataSource={table} rowKey="name" size="middle" bordered
|
|
||||||
loading={this.state.loading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderAdapter() {
|
renderAdapter() {
|
||||||
return (
|
return (
|
||||||
<Card size="small" title={
|
<Card size="small" title={
|
||||||
@ -329,19 +241,8 @@ class AdapterEditPage extends React.Component {
|
|||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
|
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={2}>
|
|
||||||
<Button type="primary" onClick={() => {this.synPolicies();}}>
|
|
||||||
{i18next.t("adapter:Sync")}
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: "20px"}} >
|
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
|
||||||
</Col>
|
|
||||||
<Col span={22}>
|
<Col span={22}>
|
||||||
{
|
<PolicyTable owner={this.state.owner} name={this.state.adapterName} mode={this.state.mode} />
|
||||||
this.renderTable(this.state.policyLists)
|
|
||||||
}
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
@ -362,8 +263,8 @@ class AdapterEditPage extends React.Component {
|
|||||||
const adapter = Setting.deepCopy(this.state.adapter);
|
const adapter = Setting.deepCopy(this.state.adapter);
|
||||||
AdapterBackend.updateAdapter(this.state.adapter.owner, this.state.adapterName, adapter)
|
AdapterBackend.updateAdapter(this.state.adapter.owner, this.state.adapterName, adapter)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
adapterName: this.state.adapter.name,
|
adapterName: this.state.adapter.name,
|
||||||
});
|
});
|
||||||
@ -371,25 +272,29 @@ class AdapterEditPage extends React.Component {
|
|||||||
if (willExist) {
|
if (willExist) {
|
||||||
this.props.history.push("/adapters");
|
this.props.history.push("/adapters");
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(`/adapters/${this.state.adapter.name}`);
|
this.props.history.push(`/adapters/${this.state.owner}/${this.state.adapter.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateAdapterField("name", this.state.adapterName);
|
this.updateAdapterField("name", this.state.adapterName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAdapter() {
|
deleteAdapter() {
|
||||||
AdapterBackend.deleteAdapter(this.state.adapter)
|
AdapterBackend.deleteAdapter(this.state.adapter)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/adapters");
|
this.props.history.push("/adapters");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `adapter failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,26 +45,33 @@ class AdapterListPage extends BaseListPage {
|
|||||||
const newAdapter = this.newAdapter();
|
const newAdapter = this.newAdapter();
|
||||||
AdapterBackend.addAdapter(newAdapter)
|
AdapterBackend.addAdapter(newAdapter)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.props.history.push({pathname: `/adapters/${newAdapter.owner}/${newAdapter.name}`, mode: "add"});
|
if (res.status === "ok") {
|
||||||
|
this.props.history.push({pathname: `/adapters/${newAdapter.organization}/${newAdapter.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Adapter failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAdapter(i) {
|
deleteAdapter(i) {
|
||||||
AdapterBackend.deleteAdapter(this.state.data[i])
|
AdapterBackend.deleteAdapter(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Adapter deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Adapter failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +208,7 @@ class AdapterListPage extends BaseListPage {
|
|||||||
title={`Sure to delete adapter: ${record.name} ?`}
|
title={`Sure to delete adapter: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteAdapter(index)}
|
onConfirm={() => this.deleteAdapter(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
255
web/src/App.js
255
web/src/App.js
@ -17,7 +17,7 @@ import "./App.less";
|
|||||||
import {Helmet} from "react-helmet";
|
import {Helmet} from "react-helmet";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import {BarsOutlined, DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
import {BarsOutlined, DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
||||||
import {Avatar, BackTop, Button, Card, Drawer, Dropdown, Layout, Menu, Result} from "antd";
|
import {Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
|
||||||
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
||||||
import OrganizationListPage from "./OrganizationListPage";
|
import OrganizationListPage from "./OrganizationListPage";
|
||||||
import OrganizationEditPage from "./OrganizationEditPage";
|
import OrganizationEditPage from "./OrganizationEditPage";
|
||||||
@ -295,23 +295,16 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderRightDropdown() {
|
renderRightDropdown() {
|
||||||
const menu = (
|
const items = [];
|
||||||
<Menu onClick={this.handleRightDropdownClick.bind(this)}>
|
items.push(Setting.getItem(<><SettingOutlined /> {i18next.t("account:My Account")}</>,
|
||||||
<Menu.Item key="/account">
|
"/account"
|
||||||
<SettingOutlined />
|
));
|
||||||
|
items.push(Setting.getItem(<><LogoutOutlined /> {i18next.t("account:Logout")}</>,
|
||||||
{i18next.t("account:My Account")}
|
"/logout"));
|
||||||
</Menu.Item>
|
const onClick = this.handleRightDropdownClick.bind(this);
|
||||||
<Menu.Item key="/logout">
|
|
||||||
<LogoutOutlined />
|
|
||||||
|
|
||||||
{i18next.t("account:Logout")}
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown key="/rightDropDown" overlay={menu} className="rightDropDown">
|
<Dropdown key="/rightDropDown" menu={{items, onClick}} className="rightDropDown">
|
||||||
<div className="ant-dropdown-link" style={{float: "right", cursor: "pointer"}}>
|
<div className="ant-dropdown-link" style={{float: "right", cursor: "pointer"}}>
|
||||||
|
|
||||||
|
|
||||||
@ -363,156 +356,89 @@ class App extends Component {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
res.push(
|
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
||||||
<Menu.Item key="/">
|
|
||||||
<Link to="/">
|
|
||||||
{i18next.t("general:Home")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Setting.isAdminUser(this.state.account)) {
|
if (Setting.isAdminUser(this.state.account)) {
|
||||||
res.push(
|
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
||||||
<Menu.Item key="/organizations">
|
"/organizations"));
|
||||||
<Link to="/organizations">
|
|
||||||
{i18next.t("general:Organizations")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||||
res.push(
|
res.push(Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>,
|
||||||
<Menu.Item key="/users">
|
"/users"
|
||||||
<Link to="/users">
|
));
|
||||||
{i18next.t("general:Users")}
|
|
||||||
</Link>
|
res.push(Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>,
|
||||||
</Menu.Item>
|
"/roles"
|
||||||
);
|
));
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/roles">
|
res.push(Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>,
|
||||||
<Link to="/roles">
|
"/permissions"
|
||||||
{i18next.t("general:Roles")}
|
));
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/permissions">
|
|
||||||
<Link to="/permissions">
|
|
||||||
{i18next.t("general:Permissions")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Setting.isAdminUser(this.state.account)) {
|
if (Setting.isAdminUser(this.state.account)) {
|
||||||
res.push(
|
res.push(Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>,
|
||||||
<Menu.Item key="/models">
|
"/models"
|
||||||
<Link to="/models">
|
));
|
||||||
{i18next.t("general:Models")}
|
|
||||||
</Link>
|
res.push(Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>,
|
||||||
</Menu.Item>
|
"/adapters"
|
||||||
);
|
));
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/adapters">
|
|
||||||
<Link to="/adapters">
|
|
||||||
{i18next.t("general:Adapters")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/applications">
|
|
||||||
<Link to="/applications">
|
|
||||||
{i18next.t("general:Applications")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||||
res.push(
|
res.push(Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>,
|
||||||
<Menu.Item key="/providers">
|
"/applications"
|
||||||
<Link to="/providers">
|
));
|
||||||
{i18next.t("general:Providers")}
|
|
||||||
</Link>
|
res.push(Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>,
|
||||||
</Menu.Item>
|
"/providers"
|
||||||
);
|
));
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/resources">
|
res.push(Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>,
|
||||||
<Link to="/resources">
|
"/resources"
|
||||||
{i18next.t("general:Resources")}
|
));
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
res.push(Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>,
|
||||||
);
|
"/records"
|
||||||
res.push(
|
));
|
||||||
<Menu.Item key="/records">
|
|
||||||
<Link to="/records">
|
|
||||||
{i18next.t("general:Records")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Setting.isAdminUser(this.state.account)) {
|
if (Setting.isAdminUser(this.state.account)) {
|
||||||
res.push(
|
res.push(Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>,
|
||||||
<Menu.Item key="/tokens">
|
"/tokens"
|
||||||
<Link to="/tokens">
|
));
|
||||||
{i18next.t("general:Tokens")}
|
|
||||||
</Link>
|
res.push(Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>,
|
||||||
</Menu.Item>
|
"/webhooks"
|
||||||
);
|
));
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/webhooks">
|
res.push(Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>,
|
||||||
<Link to="/webhooks">
|
"/syncers"
|
||||||
{i18next.t("general:Webhooks")}
|
));
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
res.push(Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>,
|
||||||
);
|
"/certs"
|
||||||
res.push(
|
));
|
||||||
<Menu.Item key="/syncers">
|
|
||||||
<Link to="/syncers">
|
|
||||||
{i18next.t("general:Syncers")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/certs">
|
|
||||||
<Link to="/certs">
|
|
||||||
{i18next.t("general:Certs")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Conf.EnableExtraPages) {
|
if (Conf.EnableExtraPages) {
|
||||||
res.push(
|
res.push(Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>,
|
||||||
<Menu.Item key="/products">
|
"/products"
|
||||||
<Link to="/products">
|
));
|
||||||
{i18next.t("general:Products")}
|
|
||||||
</Link>
|
res.push(Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>,
|
||||||
</Menu.Item>
|
"/payments"
|
||||||
);
|
));
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/payments">
|
res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:SysInfo")}</Link>,
|
||||||
<Link to="/payments">
|
"/sysinfo"
|
||||||
{i18next.t("general:Payments")}
|
));
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="/sysinfo">
|
|
||||||
<Link to="/sysinfo">
|
|
||||||
{i18next.t("general:SysInfo")}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
res.push(
|
res.push(Setting.getItem(<a target="_blank" rel="noreferrer"
|
||||||
<Menu.Item key="/swagger">
|
href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>,
|
||||||
<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>
|
"/swagger"
|
||||||
{i18next.t("general:Swagger")}
|
));
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -565,10 +491,9 @@ class App extends Component {
|
|||||||
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/providers/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/applications/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
|
||||||
{/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/}
|
{/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/}
|
||||||
<Route exact path="/ldap/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/ldap/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
|
||||||
@ -625,18 +550,16 @@ class App extends Component {
|
|||||||
<div>
|
<div>
|
||||||
<Menu
|
<Menu
|
||||||
// theme="dark"
|
// theme="dark"
|
||||||
|
items={this.renderMenu()}
|
||||||
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"}
|
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"}
|
||||||
selectedKeys={[`${this.state.selectedMenuKey}`]}
|
selectedKeys={[`${this.state.selectedMenuKey}`]}
|
||||||
style={{lineHeight: "64px", position: "absolute", left: "145px", right: "200px"}}
|
style={{lineHeight: "64px", position: "absolute", left: "145px", right: "200px"}}
|
||||||
>
|
>
|
||||||
{
|
|
||||||
this.renderMenu()
|
|
||||||
}
|
|
||||||
</Menu>
|
</Menu>
|
||||||
{
|
{
|
||||||
this.renderAccount()
|
this.renderAccount()
|
||||||
}
|
}
|
||||||
<SelectLanguageBox />
|
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
|
||||||
</div>
|
</div>
|
||||||
</Header>
|
</Header>
|
||||||
<Layout style={{backgroundColor: "#f5f5f5", alignItems: "stretch"}}>
|
<Layout style={{backgroundColor: "#f5f5f5", alignItems: "stretch"}}>
|
||||||
@ -663,14 +586,12 @@ class App extends Component {
|
|||||||
<Drawer title={i18next.t("general:Close")} placement="left" visible={this.state.menuVisible} onClose={this.onClose}>
|
<Drawer title={i18next.t("general:Close")} placement="left" visible={this.state.menuVisible} onClose={this.onClose}>
|
||||||
<Menu
|
<Menu
|
||||||
// theme="dark"
|
// theme="dark"
|
||||||
|
items={this.renderMenu()}
|
||||||
mode={(Setting.isMobile()) ? "inline" : "horizontal"}
|
mode={(Setting.isMobile()) ? "inline" : "horizontal"}
|
||||||
selectedKeys={[`${this.state.selectedMenuKey}`]}
|
selectedKeys={[`${this.state.selectedMenuKey}`]}
|
||||||
style={{lineHeight: "64px"}}
|
style={{lineHeight: "64px"}}
|
||||||
onClick={this.onClose}
|
onClick={this.onClose}
|
||||||
>
|
>
|
||||||
{
|
|
||||||
this.renderMenu()
|
|
||||||
}
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
|
<Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
|
||||||
@ -680,7 +601,7 @@ class App extends Component {
|
|||||||
{
|
{
|
||||||
this.renderAccount()
|
this.renderAccount()
|
||||||
}
|
}
|
||||||
<SelectLanguageBox />
|
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
|
||||||
</div>
|
</div>
|
||||||
</Header>
|
</Header>
|
||||||
{
|
{
|
||||||
@ -756,7 +677,7 @@ class App extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="parent-area">
|
<div id="parent-area">
|
||||||
<BackTop />
|
<FloatButton.BackTop />
|
||||||
<CustomGithubCorner />
|
<CustomGithubCorner />
|
||||||
<div id="content-wrap" style={{flexDirection: "column"}}>
|
<div id="content-wrap" style={{flexDirection: "column"}}>
|
||||||
{
|
{
|
||||||
@ -777,9 +698,16 @@ class App extends Component {
|
|||||||
<Helmet>
|
<Helmet>
|
||||||
<link rel="icon" href={"https://cdn.casdoor.com/static/favicon.png"} />
|
<link rel="icon" href={"https://cdn.casdoor.com/static/favicon.png"} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
<ConfigProvider theme={{
|
||||||
|
token: {
|
||||||
|
colorPrimary: "rgb(89,54,213)",
|
||||||
|
colorInfo: "rgb(89,54,213)",
|
||||||
|
},
|
||||||
|
}}>
|
||||||
{
|
{
|
||||||
this.renderPage()
|
this.renderPage()
|
||||||
}
|
}
|
||||||
|
</ConfigProvider>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -791,9 +719,16 @@ class App extends Component {
|
|||||||
<title>{organization.displayName}</title>
|
<title>{organization.displayName}</title>
|
||||||
<link rel="icon" href={organization.favicon} />
|
<link rel="icon" href={organization.favicon} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
<ConfigProvider theme={{
|
||||||
|
token: {
|
||||||
|
colorPrimary: "rgb(89,54,213)",
|
||||||
|
colorInfo: "rgb(89,54,213)",
|
||||||
|
},
|
||||||
|
}}>
|
||||||
{
|
{
|
||||||
this.renderPage()
|
this.renderPage()
|
||||||
}
|
}
|
||||||
|
</ConfigProvider>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/* stylelint-disable at-rule-name-case */
|
/* stylelint-disable at-rule-name-case */
|
||||||
/* stylelint-disable selector-class-pattern */
|
/* stylelint-disable selector-class-pattern */
|
||||||
@import "~antd/dist/antd.less";
|
|
||||||
|
|
||||||
@StaticBaseUrl: "https://cdn.casbin.org";
|
@StaticBaseUrl: "https://cdn.casbin.org";
|
||||||
|
|
||||||
@ -32,6 +31,11 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
#parent-area {
|
#parent-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -56,13 +60,7 @@
|
|||||||
#footer {
|
#footer {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 70px; /* Footer height */
|
padding: 24px 50px;
|
||||||
}
|
|
||||||
|
|
||||||
#language-box-corner {
|
|
||||||
position: absolute;
|
|
||||||
top: 75px;
|
|
||||||
right: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.language-box {
|
.language-box {
|
||||||
|
@ -49,6 +49,9 @@ const template = `<style>
|
|||||||
}
|
}
|
||||||
</style>`;
|
</style>`;
|
||||||
|
|
||||||
|
const previewGrid = Setting.isMobile() ? 22 : 11;
|
||||||
|
const previewWidth = Setting.isMobile() ? "110%" : "90%";
|
||||||
|
|
||||||
const sideTemplate = `<style>
|
const sideTemplate = `<style>
|
||||||
.left-model{
|
.left-model{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -91,7 +94,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
classes: props,
|
classes: props,
|
||||||
owner: props.account.owner,
|
owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
applicationName: props.match.params.applicationName,
|
applicationName: props.match.params.applicationName,
|
||||||
application: null,
|
application: null,
|
||||||
organizations: [],
|
organizations: [],
|
||||||
@ -142,21 +145,11 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getProviders() {
|
getProviders() {
|
||||||
if (Setting.isAdminUser(this.props.account)) {
|
ProviderBackend.getProviders(this.state.owner).then((res => {
|
||||||
ProviderBackend.getGlobalProviders()
|
|
||||||
.then((res) => {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
providers: res,
|
providers: res,
|
||||||
});
|
});
|
||||||
});
|
}));
|
||||||
} else {
|
|
||||||
ProviderBackend.getProviders(this.state.owner)
|
|
||||||
.then((res) => {
|
|
||||||
this.setState({
|
|
||||||
providers: res,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSamlMetadata() {
|
getSamlMetadata() {
|
||||||
@ -198,7 +191,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
Setting.showMessage("success", i18next.t("application:File uploaded successfully"));
|
Setting.showMessage("success", i18next.t("application:File uploaded successfully"));
|
||||||
this.updateApplicationField("termsOfUse", res.data);
|
this.updateApplicationField("termsOfUse", res.data);
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setState({uploading: false});
|
this.setState({uploading: false});
|
||||||
@ -287,7 +280,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.application.organization} onChange={(value => {this.updateApplicationField("organization", value);})}>
|
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.application.organization} onChange={(value => {this.updateApplicationField("organization", value);})}>
|
||||||
{
|
{
|
||||||
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
|
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
|
||||||
}
|
}
|
||||||
@ -730,7 +723,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Col span={11}>
|
<Col span={previewGrid}>
|
||||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}${signUpUrl}`);
|
copy(`${window.location.origin}${signUpUrl}`);
|
||||||
Setting.showMessage("success", i18next.t("application:Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
Setting.showMessage("success", i18next.t("application:Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
||||||
@ -739,7 +732,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{i18next.t("application:Copy signup page URL")}
|
{i18next.t("application:Copy signup page URL")}
|
||||||
</Button>
|
</Button>
|
||||||
<br />
|
<br />
|
||||||
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
|
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
|
||||||
{
|
{
|
||||||
this.state.application.enablePassword ? (
|
this.state.application.enablePassword ? (
|
||||||
<SignupPage application={this.state.application} />
|
<SignupPage application={this.state.application} />
|
||||||
@ -750,8 +743,8 @@ class ApplicationEditPage extends React.Component {
|
|||||||
<div style={maskStyle} />
|
<div style={maskStyle} />
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={11}>
|
<Col span={previewGrid}>
|
||||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px", marginTop: Setting.isMobile() ? "15px" : "0"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}${signInUrl}`);
|
copy(`${window.location.origin}${signInUrl}`);
|
||||||
Setting.showMessage("success", i18next.t("application:Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
Setting.showMessage("success", i18next.t("application:Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
||||||
}}
|
}}
|
||||||
@ -759,7 +752,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{i18next.t("application:Copy signin page URL")}
|
{i18next.t("application:Copy signin page URL")}
|
||||||
</Button>
|
</Button>
|
||||||
<br />
|
<br />
|
||||||
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
|
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
|
||||||
<LoginPage type={"login"} mode={"signin"} application={this.state.application} />
|
<LoginPage type={"login"} mode={"signin"} application={this.state.application} />
|
||||||
<div style={maskStyle} />
|
<div style={maskStyle} />
|
||||||
</div>
|
</div>
|
||||||
@ -772,7 +765,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
const promptUrl = `/prompt/${this.state.application.name}`;
|
const promptUrl = `/prompt/${this.state.application.name}`;
|
||||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||||
return (
|
return (
|
||||||
<Col span={11}>
|
<Col span={previewGrid}>
|
||||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}${promptUrl}`);
|
copy(`${window.location.origin}${promptUrl}`);
|
||||||
Setting.showMessage("success", i18next.t("application:Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
Setting.showMessage("success", i18next.t("application:Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
||||||
@ -781,7 +774,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{i18next.t("application:Copy prompt page URL")}
|
{i18next.t("application:Copy prompt page URL")}
|
||||||
</Button>
|
</Button>
|
||||||
<br />
|
<br />
|
||||||
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", flexDirection: "column", flex: "auto"}}>
|
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", flexDirection: "column", flex: "auto"}}>
|
||||||
<PromptPage application={this.state.application} account={this.props.account} />
|
<PromptPage application={this.state.application} account={this.props.account} />
|
||||||
<div style={maskStyle} />
|
<div style={maskStyle} />
|
||||||
</div>
|
</div>
|
||||||
@ -791,10 +784,10 @@ class ApplicationEditPage extends React.Component {
|
|||||||
|
|
||||||
submitApplicationEdit(willExist) {
|
submitApplicationEdit(willExist) {
|
||||||
const application = Setting.deepCopy(this.state.application);
|
const application = Setting.deepCopy(this.state.application);
|
||||||
ApplicationBackend.updateApplication(this.state.application.owner, this.state.applicationName, application)
|
ApplicationBackend.updateApplication("admin", this.state.applicationName, application)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
applicationName: this.state.application.name,
|
applicationName: this.state.application.name,
|
||||||
});
|
});
|
||||||
@ -802,25 +795,29 @@ class ApplicationEditPage extends React.Component {
|
|||||||
if (willExist) {
|
if (willExist) {
|
||||||
this.props.history.push("/applications");
|
this.props.history.push("/applications");
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(`/applications/${this.state.application.name}`);
|
this.props.history.push(`/applications/${this.state.application.organization}/${this.state.application.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateApplicationField("name", this.state.applicationName);
|
this.updateApplicationField("name", this.state.applicationName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteApplication() {
|
deleteApplication() {
|
||||||
ApplicationBackend.deleteApplication(this.state.application)
|
ApplicationBackend.deleteApplication(this.state.application)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/applications");
|
this.props.history.push("/applications");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Application failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,11 +23,28 @@ import i18next from "i18next";
|
|||||||
import BaseListPage from "./BaseListPage";
|
import BaseListPage from "./BaseListPage";
|
||||||
|
|
||||||
class ApplicationListPage extends BaseListPage {
|
class ApplicationListPage extends BaseListPage {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
classes: props,
|
||||||
|
organizationName: props.account.owner,
|
||||||
|
data: [],
|
||||||
|
pagination: {
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
searchText: "",
|
||||||
|
searchedColumn: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
newApplication() {
|
newApplication() {
|
||||||
const randomName = Setting.getRandomName();
|
const randomName = Setting.getRandomName();
|
||||||
return {
|
return {
|
||||||
owner: "admin", // this.props.account.applicationname,
|
owner: "admin", // this.props.account.applicationName,
|
||||||
name: `application_${randomName}`,
|
name: `application_${randomName}`,
|
||||||
|
organization: this.state.organizationName,
|
||||||
createdTime: moment().format(),
|
createdTime: moment().format(),
|
||||||
displayName: `New Application - ${randomName}`,
|
displayName: `New Application - ${randomName}`,
|
||||||
logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
|
logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
|
||||||
@ -53,7 +70,7 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
redirectUris: ["http://localhost:9000/callback"],
|
redirectUris: ["http://localhost:9000/callback"],
|
||||||
tokenFormat: "JWT",
|
tokenFormat: "JWT",
|
||||||
expireInHours: 24 * 7,
|
expireInHours: 24 * 7,
|
||||||
formOffset: 8,
|
formOffset: 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,26 +78,33 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
const newApplication = this.newApplication();
|
const newApplication = this.newApplication();
|
||||||
ApplicationBackend.addApplication(newApplication)
|
ApplicationBackend.addApplication(newApplication)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.props.history.push({pathname: `/applications/${newApplication.name}`, mode: "add"});
|
if (res.status === "ok") {
|
||||||
|
this.props.history.push({pathname: `/applications/${newApplication.organization}/${newApplication.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Application failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteApplication(i) {
|
deleteApplication(i) {
|
||||||
ApplicationBackend.deleteApplication(this.state.data[i])
|
ApplicationBackend.deleteApplication(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Application deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Application failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +120,7 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
...this.getColumnSearchProps("name"),
|
...this.getColumnSearchProps("name"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/applications/${text}`}>
|
<Link to={`/applications/${record.organization}/${text}`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -213,13 +237,13 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/applications/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/applications/${record.organization}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={`Sure to delete application: ${record.name} ?`}
|
title={`Sure to delete application: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteApplication(index)}
|
onConfirm={() => this.deleteApplication(index)}
|
||||||
disabled={record.name === "app-built-in"}
|
disabled={record.name === "app-built-in"}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} disabled={record.name === "app-built-in"} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} disabled={record.name === "app-built-in"} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -254,7 +278,8 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
const field = params.searchedColumn, value = params.searchText;
|
const field = params.searchedColumn, value = params.searchText;
|
||||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
(Setting.isAdminUser(this.props.account) ? ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) :
|
||||||
|
ApplicationBackend.getApplicationsByOrganization("admin", this.state.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -87,7 +87,7 @@ class BaseListPage extends React.Component {
|
|||||||
record[dataIndex]
|
record[dataIndex]
|
||||||
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
|
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
|
||||||
: "",
|
: "",
|
||||||
onFilterDropdownVisibleChange: visible => {
|
onFilterDropdownOpenChange: visible => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
setTimeout(() => this.searchInput.select(), 100);
|
setTimeout(() => this.searchInput.select(), 100);
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@ class CertEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderCert() {
|
renderCert() {
|
||||||
|
const editorWidth = Setting.isMobile() ? 22 : 9;
|
||||||
return (
|
return (
|
||||||
<Card size="small" title={
|
<Card size="small" title={
|
||||||
<div>
|
<div>
|
||||||
@ -166,7 +167,7 @@ class CertEditPage extends React.Component {
|
|||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("cert:Certificate"), i18next.t("cert:Certificate - Tooltip"))} :
|
{Setting.getLabel(i18next.t("cert:Certificate"), i18next.t("cert:Certificate - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={9} >
|
<Col span={editorWidth} >
|
||||||
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
|
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
|
||||||
copy(this.state.cert.certificate);
|
copy(this.state.cert.certificate);
|
||||||
Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
|
||||||
@ -189,7 +190,7 @@ class CertEditPage extends React.Component {
|
|||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("cert:Private key"), i18next.t("cert:Private key - Tooltip"))} :
|
{Setting.getLabel(i18next.t("cert:Private key"), i18next.t("cert:Private key - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={9} >
|
<Col span={editorWidth} >
|
||||||
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
|
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
|
||||||
copy(this.state.cert.privateKey);
|
copy(this.state.cert.privateKey);
|
||||||
Setting.showMessage("success", i18next.t("cert:Private key copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("cert:Private key copied to clipboard successfully"));
|
||||||
@ -217,8 +218,8 @@ class CertEditPage extends React.Component {
|
|||||||
const cert = Setting.deepCopy(this.state.cert);
|
const cert = Setting.deepCopy(this.state.cert);
|
||||||
CertBackend.updateCert(this.state.cert.owner, this.state.certName, cert)
|
CertBackend.updateCert(this.state.cert.owner, this.state.certName, cert)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
certName: this.state.cert.name,
|
certName: this.state.cert.name,
|
||||||
});
|
});
|
||||||
@ -229,22 +230,26 @@ class CertEditPage extends React.Component {
|
|||||||
this.props.history.push(`/certs/${this.state.cert.name}`);
|
this.props.history.push(`/certs/${this.state.cert.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateCertField("name", this.state.certName);
|
this.updateCertField("name", this.state.certName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCert() {
|
deleteCert() {
|
||||||
CertBackend.deleteCert(this.state.cert)
|
CertBackend.deleteCert(this.state.cert)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/certs");
|
this.props.history.push("/certs");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Cert failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,26 +43,33 @@ class CertListPage extends BaseListPage {
|
|||||||
const newCert = this.newCert();
|
const newCert = this.newCert();
|
||||||
CertBackend.addCert(newCert)
|
CertBackend.addCert(newCert)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/certs/${newCert.name}`, mode: "add"});
|
this.props.history.push({pathname: `/certs/${newCert.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Cert failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCert(i) {
|
deleteCert(i) {
|
||||||
CertBackend.deleteCert(this.state.data[i])
|
CertBackend.deleteCert(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Cert deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Cert failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +172,7 @@ class CertListPage extends BaseListPage {
|
|||||||
title={`Sure to delete cert: ${record.name} ?`}
|
title={`Sure to delete cert: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteCert(index)}
|
onConfirm={() => this.deleteCert(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -30,6 +30,7 @@ export const CropperDiv = (props) => {
|
|||||||
const {title} = props;
|
const {title} = props;
|
||||||
const {user} = props;
|
const {user} = props;
|
||||||
const {buttonText} = props;
|
const {buttonText} = props;
|
||||||
|
const {organization} = props;
|
||||||
let uploadButton;
|
let uploadButton;
|
||||||
|
|
||||||
const onChange = (e) => {
|
const onChange = (e) => {
|
||||||
@ -92,9 +93,8 @@ export const CropperDiv = (props) => {
|
|||||||
|
|
||||||
const getOptions = (data) => {
|
const getOptions = (data) => {
|
||||||
const options = [];
|
const options = [];
|
||||||
if (props.account.organization.defaultAvatar !== null) {
|
options.push({value: organization?.defaultAvatar});
|
||||||
options.push({value: props.account.organization.defaultAvatar});
|
|
||||||
}
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
if (data[i].fileType === "image") {
|
if (data[i].fileType === "image") {
|
||||||
const url = `${data[i].url}`;
|
const url = `${data[i].url}`;
|
||||||
@ -125,7 +125,7 @@ export const CropperDiv = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
ResourceBackend.getResources(props.account.owner, props.account.name, "", "", "", "", "", "")
|
ResourceBackend.getResources(user.owner, user.name, "", "", "", "", "", "")
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setOptions(getOptions(res));
|
setOptions(getOptions(res));
|
||||||
@ -140,7 +140,7 @@ export const CropperDiv = (props) => {
|
|||||||
<Modal
|
<Modal
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
title={title}
|
title={title}
|
||||||
visible={visible}
|
open={visible}
|
||||||
okText={i18next.t("user:Upload a photo")}
|
okText={i18next.t("user:Upload a photo")}
|
||||||
confirmLoading={confirmLoading}
|
confirmLoading={confirmLoading}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
|
@ -144,7 +144,7 @@ class LdapListPage extends React.Component {
|
|||||||
onConfirm={() => this.deleteLdap(index)}
|
onConfirm={() => this.deleteLdap(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}}
|
<Button style={{marginBottom: "10px"}}
|
||||||
type="danger">{i18next.t("general:Delete")}</Button>
|
type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -81,7 +81,7 @@ class LdapTable extends React.Component {
|
|||||||
table = Setting.deleteRow(table, i);
|
table = Setting.deleteRow(table, i);
|
||||||
this.updateTable(table);
|
this.updateTable(table);
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -162,7 +162,7 @@ class LdapTable extends React.Component {
|
|||||||
onConfirm={() => this.deleteRow(table, index)}
|
onConfirm={() => this.deleteRow(table, index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}}
|
<Button style={{marginBottom: "10px"}}
|
||||||
type="danger">{i18next.t("general:Delete")}</Button>
|
type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -158,8 +158,8 @@ class ModelEditPage extends React.Component {
|
|||||||
const model = Setting.deepCopy(this.state.model);
|
const model = Setting.deepCopy(this.state.model);
|
||||||
ModelBackend.updateModel(this.state.organizationName, this.state.modelName, model)
|
ModelBackend.updateModel(this.state.organizationName, this.state.modelName, model)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
modelName: this.state.model.name,
|
modelName: this.state.model.name,
|
||||||
});
|
});
|
||||||
@ -170,22 +170,26 @@ class ModelEditPage extends React.Component {
|
|||||||
this.props.history.push(`/models/${this.state.model.owner}/${this.state.model.name}`);
|
this.props.history.push(`/models/${this.state.model.owner}/${this.state.model.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateModelField("name", this.state.modelName);
|
this.updateModelField("name", this.state.modelName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteModel() {
|
deleteModel() {
|
||||||
ModelBackend.deleteModel(this.state.model)
|
ModelBackend.deleteModel(this.state.model)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/models");
|
this.props.history.push("/models");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Model failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,26 +38,33 @@ class ModelListPage extends BaseListPage {
|
|||||||
const newModel = this.newModel();
|
const newModel = this.newModel();
|
||||||
ModelBackend.addModel(newModel)
|
ModelBackend.addModel(newModel)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/models/${newModel.owner}/${newModel.name}`, mode: "add"});
|
this.props.history.push({pathname: `/models/${newModel.owner}/${newModel.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Model failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteModel(i) {
|
deleteModel(i) {
|
||||||
ModelBackend.deleteModel(this.state.data[i])
|
ModelBackend.deleteModel(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Model deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Model failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +146,7 @@ class ModelListPage extends BaseListPage {
|
|||||||
title={`Sure to delete model: ${record.name} ?`}
|
title={`Sure to delete model: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteModel(index)}
|
onConfirm={() => this.deleteModel(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -255,6 +255,31 @@ class OrganizationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Languages"), i18next.t("general:Languages - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} mode="tags" style={{width: "100%"}}
|
||||||
|
value={this.state.organization.languages}
|
||||||
|
onChange={(value => {
|
||||||
|
this.updateOrganizationField("languages", value);
|
||||||
|
})} >
|
||||||
|
{
|
||||||
|
[
|
||||||
|
{value: "en", label: "English"},
|
||||||
|
{value: "zh", label: "简体中文"},
|
||||||
|
{value: "es", label: "Español"},
|
||||||
|
{value: "fr", label: "Français"},
|
||||||
|
{value: "de", label: "Deutsch"},
|
||||||
|
{value: "ja", label: "日本語"},
|
||||||
|
{value: "ko", label: "한국어"},
|
||||||
|
{value: "ru", label: "Русский"},
|
||||||
|
].map((item, index) => <Option key={index} value={item.value}>{item.label}</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||||
{Setting.getLabel(i18next.t("organization:Soft deletion"), i18next.t("organization:Soft deletion - Tooltip"))} :
|
{Setting.getLabel(i18next.t("organization:Soft deletion"), i18next.t("organization:Soft deletion - Tooltip"))} :
|
||||||
@ -310,8 +335,8 @@ class OrganizationEditPage extends React.Component {
|
|||||||
const organization = Setting.deepCopy(this.state.organization);
|
const organization = Setting.deepCopy(this.state.organization);
|
||||||
OrganizationBackend.updateOrganization(this.state.organization.owner, this.state.organizationName, organization)
|
OrganizationBackend.updateOrganization(this.state.organization.owner, this.state.organizationName, organization)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
organizationName: this.state.organization.name,
|
organizationName: this.state.organization.name,
|
||||||
});
|
});
|
||||||
@ -322,22 +347,26 @@ class OrganizationEditPage extends React.Component {
|
|||||||
this.props.history.push(`/organizations/${this.state.organization.name}`);
|
this.props.history.push(`/organizations/${this.state.organization.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateOrganizationField("name", this.state.organizationName);
|
this.updateOrganizationField("name", this.state.organizationName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteOrganization() {
|
deleteOrganization() {
|
||||||
OrganizationBackend.deleteOrganization(this.state.organization)
|
OrganizationBackend.deleteOrganization(this.state.organization)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/organizations");
|
this.props.history.push("/organizations");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
|
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
|
||||||
defaultApplication: "",
|
defaultApplication: "",
|
||||||
tags: [],
|
tags: [],
|
||||||
|
languages: ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"],
|
||||||
masterPassword: "",
|
masterPassword: "",
|
||||||
enableSoftDeletion: false,
|
enableSoftDeletion: false,
|
||||||
isProfilePublic: true,
|
isProfilePublic: true,
|
||||||
@ -74,26 +75,33 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
const newOrganization = this.newOrganization();
|
const newOrganization = this.newOrganization();
|
||||||
OrganizationBackend.addOrganization(newOrganization)
|
OrganizationBackend.addOrganization(newOrganization)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/organizations/${newOrganization.name}`, mode: "add"});
|
this.props.history.push({pathname: `/organizations/${newOrganization.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Organization failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteOrganization(i) {
|
deleteOrganization(i) {
|
||||||
OrganizationBackend.deleteOrganization(this.state.data[i])
|
OrganizationBackend.deleteOrganization(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Organization deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Organization failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +231,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
onConfirm={() => this.deleteOrganization(index)}
|
onConfirm={() => this.deleteOrganization(index)}
|
||||||
disabled={record.name === "built-in"}
|
disabled={record.name === "built-in"}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} disabled={record.name === "built-in"} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} disabled={record.name === "built-in"} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -64,7 +64,7 @@ export const PasswordModal = (props) => {
|
|||||||
<Modal
|
<Modal
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
title={i18next.t("user:Password")}
|
title={i18next.t("user:Password")}
|
||||||
visible={visible}
|
open={visible}
|
||||||
okText={i18next.t("user:Set Password")}
|
okText={i18next.t("user:Set Password")}
|
||||||
cancelText={i18next.t("user:Cancel")}
|
cancelText={i18next.t("user:Cancel")}
|
||||||
confirmLoading={confirmLoading}
|
confirmLoading={confirmLoading}
|
||||||
|
@ -78,7 +78,7 @@ class PaymentEditPage extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
isInvoiceLoading: false,
|
isInvoiceLoading: false,
|
||||||
});
|
});
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully invoiced");
|
Setting.showMessage("success", "Successfully invoiced");
|
||||||
Setting.openLinkSafe(res.data);
|
Setting.openLinkSafe(res.data);
|
||||||
this.getPayment();
|
this.getPayment();
|
||||||
@ -90,7 +90,7 @@ class PaymentEditPage extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
isInvoiceLoading: false,
|
isInvoiceLoading: false,
|
||||||
});
|
});
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ class PaymentEditPage extends React.Component {
|
|||||||
{" " + i18next.t("payment:Confirm your invoice information")}
|
{" " + i18next.t("payment:Confirm your invoice information")}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
visible={this.state.isModalVisible}
|
open={this.state.isModalVisible}
|
||||||
onOk={handleIssueInvoice}
|
onOk={handleIssueInvoice}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
okText={i18next.t("payment:Issue Invoice")}
|
okText={i18next.t("payment:Issue Invoice")}
|
||||||
@ -443,8 +443,8 @@ class PaymentEditPage extends React.Component {
|
|||||||
const payment = Setting.deepCopy(this.state.payment);
|
const payment = Setting.deepCopy(this.state.payment);
|
||||||
PaymentBackend.updatePayment(this.state.payment.owner, this.state.paymentName, payment)
|
PaymentBackend.updatePayment(this.state.payment.owner, this.state.paymentName, payment)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
paymentName: this.state.payment.name,
|
paymentName: this.state.payment.name,
|
||||||
});
|
});
|
||||||
@ -455,22 +455,26 @@ class PaymentEditPage extends React.Component {
|
|||||||
this.props.history.push(`/payments/${this.state.payment.name}`);
|
this.props.history.push(`/payments/${this.state.payment.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updatePaymentField("name", this.state.paymentName);
|
this.updatePaymentField("name", this.state.paymentName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePayment() {
|
deletePayment() {
|
||||||
PaymentBackend.deletePayment(this.state.payment)
|
PaymentBackend.deletePayment(this.state.payment)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/payments");
|
this.props.history.push("/payments");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Payment failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,26 +51,34 @@ class PaymentListPage extends BaseListPage {
|
|||||||
const newPayment = this.newPayment();
|
const newPayment = this.newPayment();
|
||||||
PaymentBackend.addPayment(newPayment)
|
PaymentBackend.addPayment(newPayment)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/payments/${newPayment.name}`, mode: "add"});
|
this.props.history.push({pathname: `/payments/${newPayment.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Payment failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePayment(i) {
|
deletePayment(i) {
|
||||||
PaymentBackend.deletePayment(this.state.data[i])
|
PaymentBackend.deletePayment(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Payment deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Payment failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +226,7 @@ class PaymentListPage extends BaseListPage {
|
|||||||
title={`Sure to delete payment: ${record.name} ?`}
|
title={`Sure to delete payment: ${record.name} ?`}
|
||||||
onConfirm={() => this.deletePayment(index)}
|
onConfirm={() => this.deletePayment(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -417,8 +417,8 @@ class PermissionEditPage extends React.Component {
|
|||||||
const permission = Setting.deepCopy(this.state.permission);
|
const permission = Setting.deepCopy(this.state.permission);
|
||||||
PermissionBackend.updatePermission(this.state.organizationName, this.state.permissionName, permission)
|
PermissionBackend.updatePermission(this.state.organizationName, this.state.permissionName, permission)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
permissionName: this.state.permission.name,
|
permissionName: this.state.permission.name,
|
||||||
});
|
});
|
||||||
@ -429,22 +429,26 @@ class PermissionEditPage extends React.Component {
|
|||||||
this.props.history.push(`/permissions/${this.state.permission.owner}/${this.state.permission.name}`);
|
this.props.history.push(`/permissions/${this.state.permission.owner}/${this.state.permission.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updatePermissionField("name", this.state.permissionName);
|
this.updatePermissionField("name", this.state.permissionName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePermission() {
|
deletePermission() {
|
||||||
PermissionBackend.deletePermission(this.state.permission)
|
PermissionBackend.deletePermission(this.state.permission)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/permissions");
|
this.props.history.push("/permissions");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Permission failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class PermissionListPage extends BaseListPage {
|
|||||||
name: `permission_${randomName}`,
|
name: `permission_${randomName}`,
|
||||||
createdTime: moment().format(),
|
createdTime: moment().format(),
|
||||||
displayName: `New Permission - ${randomName}`,
|
displayName: `New Permission - ${randomName}`,
|
||||||
users: [this.props.account.name],
|
users: [`${this.props.account.owner}/${this.props.account.name}`],
|
||||||
roles: [],
|
roles: [],
|
||||||
domains: [],
|
domains: [],
|
||||||
resourceType: "Application",
|
resourceType: "Application",
|
||||||
@ -48,30 +48,33 @@ class PermissionListPage extends BaseListPage {
|
|||||||
const newPermission = this.newPermission();
|
const newPermission = this.newPermission();
|
||||||
PermissionBackend.addPermission(newPermission)
|
PermissionBackend.addPermission(newPermission)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg !== "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("error", res.msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.history.push({pathname: `/permissions/${newPermission.owner}/${newPermission.name}`, mode: "add"});
|
this.props.history.push({pathname: `/permissions/${newPermission.owner}/${newPermission.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Permission failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePermission(i) {
|
deletePermission(i) {
|
||||||
PermissionBackend.deletePermission(this.state.data[i])
|
PermissionBackend.deletePermission(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Permission deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Permission failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +304,7 @@ class PermissionListPage extends BaseListPage {
|
|||||||
title={`Sure to delete permission: ${record.name} ?`}
|
title={`Sure to delete permission: ${record.name} ?`}
|
||||||
onConfirm={() => this.deletePermission(index)}
|
onConfirm={() => this.deletePermission(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -81,11 +81,11 @@ class ProductBuyPage extends React.Component {
|
|||||||
|
|
||||||
ProductBackend.buyProduct(this.state.product.owner, this.state.productName, provider.name)
|
ProductBackend.buyProduct(this.state.product.owner, this.state.productName, provider.name)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
const payUrl = res.data;
|
const payUrl = res.data;
|
||||||
Setting.goToLink(payUrl);
|
Setting.goToLink(payUrl);
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isPlacingOrder: false,
|
isPlacingOrder: false,
|
||||||
@ -93,7 +93,7 @@ class ProductBuyPage extends React.Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,8 +271,8 @@ class ProductEditPage extends React.Component {
|
|||||||
const product = Setting.deepCopy(this.state.product);
|
const product = Setting.deepCopy(this.state.product);
|
||||||
ProductBackend.updateProduct(this.state.product.owner, this.state.productName, product)
|
ProductBackend.updateProduct(this.state.product.owner, this.state.productName, product)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
productName: this.state.product.name,
|
productName: this.state.product.name,
|
||||||
});
|
});
|
||||||
@ -283,22 +283,26 @@ class ProductEditPage extends React.Component {
|
|||||||
this.props.history.push(`/products/${this.state.product.name}`);
|
this.props.history.push(`/products/${this.state.product.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateProductField("name", this.state.productName);
|
this.updateProductField("name", this.state.productName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteProduct() {
|
deleteProduct() {
|
||||||
ProductBackend.deleteProduct(this.state.product)
|
ProductBackend.deleteProduct(this.state.product)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/products");
|
this.props.history.push("/products");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Product failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,26 +45,33 @@ class ProductListPage extends BaseListPage {
|
|||||||
const newProduct = this.newProduct();
|
const newProduct = this.newProduct();
|
||||||
ProductBackend.addProduct(newProduct)
|
ProductBackend.addProduct(newProduct)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/products/${newProduct.name}`, mode: "add"});
|
this.props.history.push({pathname: `/products/${newProduct.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Product failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteProduct(i) {
|
deleteProduct(i) {
|
||||||
ProductBackend.deleteProduct(this.state.data[i])
|
ProductBackend.deleteProduct(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Product deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Product failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +243,7 @@ class ProductListPage extends BaseListPage {
|
|||||||
title={`Sure to delete product: ${record.name} ?`}
|
title={`Sure to delete product: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteProduct(index)}
|
onConfirm={() => this.deleteProduct(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -22,6 +22,7 @@ import {authConfig} from "./auth/Auth";
|
|||||||
import * as ProviderEditTestEmail from "./TestEmailWidget";
|
import * as ProviderEditTestEmail from "./TestEmailWidget";
|
||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
import {CaptchaPreview} from "./common/CaptchaPreview";
|
import {CaptchaPreview} from "./common/CaptchaPreview";
|
||||||
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
const {TextArea} = Input;
|
const {TextArea} = Input;
|
||||||
@ -34,11 +35,13 @@ class ProviderEditPage extends React.Component {
|
|||||||
providerName: props.match.params.providerName,
|
providerName: props.match.params.providerName,
|
||||||
owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
provider: null,
|
provider: null,
|
||||||
|
organizations: [],
|
||||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
|
this.getOrganizations();
|
||||||
this.getProvider();
|
this.getProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +54,17 @@ class ProviderEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOrganizations() {
|
||||||
|
if (Setting.isAdminUser(this.props.account)) {
|
||||||
|
OrganizationBackend.getOrganizations("admin")
|
||||||
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
organizations: res.msg === undefined ? res : [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parseProviderField(key, value) {
|
parseProviderField(key, value) {
|
||||||
if (["port"].includes(key)) {
|
if (["port"].includes(key)) {
|
||||||
value = Setting.myParseInt(value);
|
value = Setting.myParseInt(value);
|
||||||
@ -191,6 +205,19 @@ class ProviderEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.provider.owner} onChange={(value => {this.updateProviderField("owner", value);})}>
|
||||||
|
{Setting.isAdminUser(this.props.account) ? <Option key={"admin"} value={"admin"}>{i18next.t("provider:admin (share)")}</Option> : null}
|
||||||
|
{
|
||||||
|
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</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("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
|
||||||
@ -230,7 +257,9 @@ class ProviderEditPage extends React.Component {
|
|||||||
{id: "SAML", name: "SAML"},
|
{id: "SAML", name: "SAML"},
|
||||||
{id: "Payment", name: "Payment"},
|
{id: "Payment", name: "Payment"},
|
||||||
{id: "Captcha", name: "Captcha"},
|
{id: "Captcha", name: "Captcha"},
|
||||||
].map((providerCategory, index) => <Option key={index} value={providerCategory.id}>{providerCategory.name}</Option>)
|
]
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
.map((providerCategory, index) => <Option key={index} value={providerCategory.id}>{providerCategory.name}</Option>)
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
</Col>
|
</Col>
|
||||||
@ -253,7 +282,9 @@ class ProviderEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
})}>
|
})}>
|
||||||
{
|
{
|
||||||
Setting.getProviderTypeOptions(this.state.provider.category).map((providerType, index) => <Option key={index} value={providerType.id}>{providerType.name}</Option>)
|
Setting.getProviderTypeOptions(this.state.provider.category)
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
.map((providerType, index) => <Option key={index} value={providerType.id}>{providerType.name}</Option>)
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
</Col>
|
</Col>
|
||||||
@ -424,6 +455,20 @@ class ProviderEditPage extends React.Component {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
this.state.provider.type !== "WeChat" ? null : (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("provider:Enable QR code"), i18next.t("provider:Enable QR code - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={1} >
|
||||||
|
<Switch checked={this.state.provider.disableSsl} onChange={checked => {
|
||||||
|
this.updateProviderField("disableSsl", checked);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
this.state.provider.type !== "Adfs" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
this.state.provider.type !== "Adfs" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
@ -746,36 +791,41 @@ class ProviderEditPage extends React.Component {
|
|||||||
|
|
||||||
submitProviderEdit(willExist) {
|
submitProviderEdit(willExist) {
|
||||||
const provider = Setting.deepCopy(this.state.provider);
|
const provider = Setting.deepCopy(this.state.provider);
|
||||||
ProviderBackend.updateProvider(this.state.provider.owner, this.state.providerName, provider)
|
ProviderBackend.updateProvider(this.state.owner, this.state.providerName, provider)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
|
owner: this.state.provider.owner,
|
||||||
providerName: this.state.provider.name,
|
providerName: this.state.provider.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (willExist) {
|
if (willExist) {
|
||||||
this.props.history.push("/providers");
|
this.props.history.push("/providers");
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(`/providers/${this.state.provider.name}`);
|
this.props.history.push(`/providers/${this.state.provider.owner}/${this.state.provider.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateProviderField("name", this.state.providerName);
|
this.updateProviderField("name", this.state.providerName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteProvider() {
|
deleteProvider() {
|
||||||
ProviderBackend.deleteProvider(this.state.provider)
|
ProviderBackend.deleteProvider(this.state.provider)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/providers");
|
this.props.history.push("/providers");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Provider failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class ProviderListPage extends BaseListPage {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
classes: props,
|
classes: props,
|
||||||
owner: Setting.isAdminUser(props.account) ? "admin" : props.account.organization.name,
|
owner: Setting.isAdminUser(props.account) ? "admin" : props.account.owner,
|
||||||
data: [],
|
data: [],
|
||||||
pagination: {
|
pagination: {
|
||||||
current: 1,
|
current: 1,
|
||||||
@ -61,26 +61,33 @@ class ProviderListPage extends BaseListPage {
|
|||||||
const newProvider = this.newProvider();
|
const newProvider = this.newProvider();
|
||||||
ProviderBackend.addProvider(newProvider)
|
ProviderBackend.addProvider(newProvider)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/providers/${newProvider.owner}/${newProvider.name}`, mode: "add"});
|
this.props.history.push({pathname: `/providers/${newProvider.owner}/${newProvider.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Provider failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteProvider(i) {
|
deleteProvider(i) {
|
||||||
ProviderBackend.deleteProvider(this.state.data[i])
|
ProviderBackend.deleteProvider(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Provider deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Provider failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,12 +103,20 @@ class ProviderListPage extends BaseListPage {
|
|||||||
...this.getColumnSearchProps("name"),
|
...this.getColumnSearchProps("name"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/providers/${text}`}>
|
<Link to={`/providers/${record.owner}/${text}`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Organization"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
width: "150px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("owner"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Created time"),
|
title: i18next.t("general:Created time"),
|
||||||
dataIndex: "createdTime",
|
dataIndex: "createdTime",
|
||||||
@ -192,12 +207,12 @@ class ProviderListPage extends BaseListPage {
|
|||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/providers/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/providers/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={`Sure to delete provider: ${record.name} ?`}
|
title={`Sure to delete provider: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteProvider(index)}
|
onConfirm={() => this.deleteProvider(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -71,7 +71,7 @@ export const ResetModal = (props) => {
|
|||||||
<Modal
|
<Modal
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
title={buttonText}
|
title={buttonText}
|
||||||
visible={visible}
|
open={visible}
|
||||||
okText={buttonText}
|
okText={buttonText}
|
||||||
cancelText={i18next.t("user:Cancel")}
|
cancelText={i18next.t("user:Cancel")}
|
||||||
confirmLoading={confirmLoading}
|
confirmLoading={confirmLoading}
|
||||||
|
@ -43,15 +43,18 @@ class ResourceListPage extends BaseListPage {
|
|||||||
deleteResource(i) {
|
deleteResource(i) {
|
||||||
ResourceBackend.deleteResource(this.state.data[i])
|
ResourceBackend.deleteResource(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Resource deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Resource failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +98,7 @@ class ResourceListPage extends BaseListPage {
|
|||||||
...this.getColumnSearchProps("provider"),
|
...this.getColumnSearchProps("provider"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/providers/${text}`}>
|
<Link to={`/providers/${record.owner}/${text}`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -207,16 +210,14 @@ class ResourceListPage extends BaseListPage {
|
|||||||
if (record.fileType === "image") {
|
if (record.fileType === "image") {
|
||||||
return (
|
return (
|
||||||
<a target="_blank" rel="noreferrer" href={record.url}>
|
<a target="_blank" rel="noreferrer" href={record.url}>
|
||||||
<img src={record.url} alt={record.name} width={100} />
|
<img src={record.url} alt={record.name} width={200} />
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
} else if (record.fileType === "video") {
|
} else if (record.fileType === "video") {
|
||||||
return (
|
return (
|
||||||
<div>
|
<video width={200} controls>
|
||||||
<video width={100} controls>
|
<source src={record.url} type="video/mp4" />
|
||||||
<source src={text} type="video/mp4" />
|
|
||||||
</video>
|
</video>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -256,7 +257,7 @@ class ResourceListPage extends BaseListPage {
|
|||||||
okText={i18next.t("user:OK")}
|
okText={i18next.t("user:OK")}
|
||||||
cancelText={i18next.t("user:Cancel")}
|
cancelText={i18next.t("user:Cancel")}
|
||||||
>
|
>
|
||||||
<Button type="danger">{i18next.t("general:Delete")}</Button>
|
<Button type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -196,8 +196,8 @@ class RoleEditPage extends React.Component {
|
|||||||
const role = Setting.deepCopy(this.state.role);
|
const role = Setting.deepCopy(this.state.role);
|
||||||
RoleBackend.updateRole(this.state.organizationName, this.state.roleName, role)
|
RoleBackend.updateRole(this.state.organizationName, this.state.roleName, role)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
roleName: this.state.role.name,
|
roleName: this.state.role.name,
|
||||||
});
|
});
|
||||||
@ -208,22 +208,26 @@ class RoleEditPage extends React.Component {
|
|||||||
this.props.history.push(`/roles/${this.state.role.owner}/${this.state.role.name}`);
|
this.props.history.push(`/roles/${this.state.role.owner}/${this.state.role.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateRoleField("name", this.state.roleName);
|
this.updateRoleField("name", this.state.roleName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteRole() {
|
deleteRole() {
|
||||||
RoleBackend.deleteRole(this.state.role)
|
RoleBackend.deleteRole(this.state.role)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/roles");
|
this.props.history.push("/roles");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Role failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,26 +40,33 @@ class RoleListPage extends BaseListPage {
|
|||||||
const newRole = this.newRole();
|
const newRole = this.newRole();
|
||||||
RoleBackend.addRole(newRole)
|
RoleBackend.addRole(newRole)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/roles/${newRole.owner}/${newRole.name}`, mode: "add"});
|
this.props.history.push({pathname: `/roles/${newRole.owner}/${newRole.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Role failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteRole(i) {
|
deleteRole(i) {
|
||||||
RoleBackend.deleteRole(this.state.data[i])
|
RoleBackend.deleteRole(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Role deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Role failed to delete: ${error}`);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +179,7 @@ class RoleListPage extends BaseListPage {
|
|||||||
title={`Sure to delete role: ${record.name} ?`}
|
title={`Sure to delete role: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteRole(index)}
|
onConfirm={() => this.deleteRole(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -28,28 +28,41 @@ class SelectLanguageBox extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
classes: props,
|
classes: props,
|
||||||
|
languages: props.languages ?? ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items = [
|
||||||
|
Setting.getItem("English", "en", flagIcon("US", "English")),
|
||||||
|
Setting.getItem("简体中文", "zh", flagIcon("CN", "简体中文")),
|
||||||
|
Setting.getItem("Español", "es", flagIcon("ES", "Español")),
|
||||||
|
Setting.getItem("Français", "fr", flagIcon("FR", "Français")),
|
||||||
|
Setting.getItem("Deutsch", "de", flagIcon("DE", "Deutsch")),
|
||||||
|
Setting.getItem("日本語", "ja", flagIcon("JP", "日本語")),
|
||||||
|
Setting.getItem("한국어", "ko", flagIcon("KR", "한국어")),
|
||||||
|
Setting.getItem("Русский", "ru", flagIcon("RU", "Русский")),
|
||||||
|
];
|
||||||
|
|
||||||
|
getOrganizationLanguages(languages) {
|
||||||
|
const select = [];
|
||||||
|
for (const language of languages) {
|
||||||
|
this.items.map((item, index) => item.key === language ? select.push(item) : null);
|
||||||
|
}
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const languageItems = this.getOrganizationLanguages(this.state.languages);
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu onClick={(e) => {
|
<Menu items={languageItems} onClick={(e) => {
|
||||||
Setting.changeLanguage(e.key);
|
Setting.setLanguage(e.key);
|
||||||
}}>
|
}}>
|
||||||
<Menu.Item key="en" icon={flagIcon("US", "English")}>English</Menu.Item>
|
|
||||||
<Menu.Item key="zh" icon={flagIcon("CN", "简体中文")}>简体中文</Menu.Item>
|
|
||||||
<Menu.Item key="es" icon={flagIcon("ES", "Español")}>Español</Menu.Item>
|
|
||||||
<Menu.Item key="fr" icon={flagIcon("FR", "Français")}>Français</Menu.Item>
|
|
||||||
<Menu.Item key="de" icon={flagIcon("DE", "Deutsch")}>Deutsch</Menu.Item>
|
|
||||||
<Menu.Item key="ja" icon={flagIcon("JP", "日本語")}>日本語</Menu.Item>
|
|
||||||
<Menu.Item key="ko" icon={flagIcon("KR", "한국어")}>한국어</Menu.Item>
|
|
||||||
<Menu.Item key="ru" icon={flagIcon("RU", "Русский")}>Русский</Menu.Item>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown overlay={menu} >
|
<Dropdown overlay={menu} >
|
||||||
<div className="language-box" id={this.props.id} style={this.props.style} />
|
<div className="language-box" style={{display: languageItems.length === 0 ? "none" : null, ...this.props.style}} />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,10 @@ export const OtherProviderInfo = {
|
|||||||
logo: `${StaticBaseUrl}/img/social_geetest.png`,
|
logo: `${StaticBaseUrl}/img/social_geetest.png`,
|
||||||
url: "https://www.geetest.com",
|
url: "https://www.geetest.com",
|
||||||
},
|
},
|
||||||
|
"Cloudflare Turnstile": {
|
||||||
|
logo: `${StaticBaseUrl}/img/social_cloudflare.png`,
|
||||||
|
url: "https://www.cloudflare.com/products/turnstile/",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -416,9 +420,7 @@ export function goToLinkSoft(ths, link) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function showMessage(type, text) {
|
export function showMessage(type, text) {
|
||||||
if (type === "") {
|
if (type === "success") {
|
||||||
return;
|
|
||||||
} else if (type === "success") {
|
|
||||||
message.success(text);
|
message.success(text);
|
||||||
} else if (type === "error") {
|
} else if (type === "error") {
|
||||||
message.error(text);
|
message.error(text);
|
||||||
@ -445,8 +447,8 @@ export function deepCopy(obj) {
|
|||||||
return Object.assign({}, obj);
|
return Object.assign({}, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addRow(array, row) {
|
export function addRow(array, row, position = "end") {
|
||||||
return [...array, row];
|
return position === "end" ? [...array, row] : [row, ...array];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prependRow(array, row) {
|
export function prependRow(array, row) {
|
||||||
@ -552,14 +554,10 @@ export function setLanguage(language) {
|
|||||||
i18next.changeLanguage(language);
|
i18next.changeLanguage(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changeLanguage(language) {
|
|
||||||
localStorage.setItem("language", language);
|
|
||||||
changeMomentLanguage(language);
|
|
||||||
i18next.changeLanguage(language);
|
|
||||||
// window.location.reload(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAcceptLanguage() {
|
export function getAcceptLanguage() {
|
||||||
|
if (i18next.language === null || i18next.language === "") {
|
||||||
|
return "en;q=0.9,en;q=0.8";
|
||||||
|
}
|
||||||
return i18next.language + ";q=0.9,en;q=0.8";
|
return i18next.language + ";q=0.9,en;q=0.8";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,6 +699,7 @@ export function getProviderTypeOptions(category) {
|
|||||||
{id: "hCaptcha", name: "hCaptcha"},
|
{id: "hCaptcha", name: "hCaptcha"},
|
||||||
{id: "Aliyun Captcha", name: "Aliyun Captcha"},
|
{id: "Aliyun Captcha", name: "Aliyun Captcha"},
|
||||||
{id: "GEETEST", name: "GEETEST"},
|
{id: "GEETEST", name: "GEETEST"},
|
||||||
|
{id: "Cloudflare Turnstile", name: "Cloudflare Turnstile"},
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -859,6 +858,15 @@ export function getLabel(text, tooltip) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getItem(label, key, icon, children, type) {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
label,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
}
|
||||||
function repeat(str, len) {
|
function repeat(str, len) {
|
||||||
while (str.length < len) {
|
while (str.length < len) {
|
||||||
str += str.substr(0, len - str.length);
|
str += str.substr(0, len - str.length);
|
||||||
|
@ -298,8 +298,8 @@ class SyncerEditPage extends React.Component {
|
|||||||
const syncer = Setting.deepCopy(this.state.syncer);
|
const syncer = Setting.deepCopy(this.state.syncer);
|
||||||
SyncerBackend.updateSyncer(this.state.syncer.owner, this.state.syncerName, syncer)
|
SyncerBackend.updateSyncer(this.state.syncer.owner, this.state.syncerName, syncer)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
syncerName: this.state.syncer.name,
|
syncerName: this.state.syncer.name,
|
||||||
});
|
});
|
||||||
@ -310,22 +310,26 @@ class SyncerEditPage extends React.Component {
|
|||||||
this.props.history.push(`/syncers/${this.state.syncer.name}`);
|
this.props.history.push(`/syncers/${this.state.syncer.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateSyncerField("name", this.state.syncerName);
|
this.updateSyncerField("name", this.state.syncerName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSyncer() {
|
deleteSyncer() {
|
||||||
SyncerBackend.deleteSyncer(this.state.syncer)
|
SyncerBackend.deleteSyncer(this.state.syncer)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/syncers");
|
this.props.history.push("/syncers");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Syncer failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,26 +49,33 @@ class SyncerListPage extends BaseListPage {
|
|||||||
const newSyncer = this.newSyncer();
|
const newSyncer = this.newSyncer();
|
||||||
SyncerBackend.addSyncer(newSyncer)
|
SyncerBackend.addSyncer(newSyncer)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/syncers/${newSyncer.name}`, mode: "add"});
|
this.props.history.push({pathname: `/syncers/${newSyncer.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Syncer failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSyncer(i) {
|
deleteSyncer(i) {
|
||||||
SyncerBackend.deleteSyncer(this.state.data[i])
|
SyncerBackend.deleteSyncer(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Syncer deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Syncer failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +236,7 @@ class SyncerListPage extends BaseListPage {
|
|||||||
title={`Sure to delete syncer: ${record.name} ?`}
|
title={`Sure to delete syncer: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteSyncer(index)}
|
onConfirm={() => this.deleteSyncer(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,32 +13,33 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
export function sendTestEmail(provider, email) {
|
export function sendTestEmail(provider, email) {
|
||||||
testEmailProvider(provider, email)
|
testEmailProvider(provider, email)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully send email");
|
Setting.showMessage("success", "Successfully send email");
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function connectSmtpServer(provider) {
|
export function connectSmtpServer(provider) {
|
||||||
testEmailProvider(provider)
|
testEmailProvider(provider)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully connecting smtp server");
|
Setting.showMessage("success", "Successfully connecting smtp server");
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,8 +167,8 @@ class TokenEditPage extends React.Component {
|
|||||||
const token = Setting.deepCopy(this.state.token);
|
const token = Setting.deepCopy(this.state.token);
|
||||||
TokenBackend.updateToken(this.state.token.owner, this.state.tokenName, token)
|
TokenBackend.updateToken(this.state.token.owner, this.state.tokenName, token)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
tokenName: this.state.token.name,
|
tokenName: this.state.token.name,
|
||||||
});
|
});
|
||||||
@ -179,22 +179,26 @@ class TokenEditPage extends React.Component {
|
|||||||
this.props.history.push(`/tokens/${this.state.token.name}`);
|
this.props.history.push(`/tokens/${this.state.token.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateTokenField("name", this.state.tokenName);
|
this.updateTokenField("name", this.state.tokenName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteToken() {
|
deleteToken() {
|
||||||
TokenBackend.deleteToken(this.state.token)
|
TokenBackend.deleteToken(this.state.token)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/tokens");
|
this.props.history.push("/tokens");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Token failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,26 +42,33 @@ class TokenListPage extends BaseListPage {
|
|||||||
const newToken = this.newToken();
|
const newToken = this.newToken();
|
||||||
TokenBackend.addToken(newToken)
|
TokenBackend.addToken(newToken)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/tokens/${newToken.name}`, mode: "add"});
|
this.props.history.push({pathname: `/tokens/${newToken.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Token failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteToken(i) {
|
deleteToken(i) {
|
||||||
TokenBackend.deleteToken(this.state.data[i])
|
TokenBackend.deleteToken(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Token deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Token failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +109,7 @@ class TokenListPage extends BaseListPage {
|
|||||||
...this.getColumnSearchProps("application"),
|
...this.getColumnSearchProps("application"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/applications/${text}`}>
|
<Link to={`/applications/${record.organization}/${text}`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -198,7 +205,7 @@ class TokenListPage extends BaseListPage {
|
|||||||
title={`Sure to delete token: ${record.name} ?`}
|
title={`Sure to delete token: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteToken(index)}
|
onConfirm={() => this.deleteToken(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -49,6 +49,7 @@ class UserEditPage extends React.Component {
|
|||||||
applications: [],
|
applications: [],
|
||||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||||
loading: true,
|
loading: true,
|
||||||
|
returnUrl: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ class UserEditPage extends React.Component {
|
|||||||
this.getOrganizations();
|
this.getOrganizations();
|
||||||
this.getApplicationsByOrganization(this.state.organizationName);
|
this.getApplicationsByOrganization(this.state.organizationName);
|
||||||
this.getUserApplication();
|
this.getUserApplication();
|
||||||
|
this.setReturnUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser() {
|
getUser() {
|
||||||
@ -100,9 +102,14 @@ class UserEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getReturnUrl() {
|
setReturnUrl() {
|
||||||
const searchParams = new URLSearchParams(this.props.location.search);
|
const searchParams = new URLSearchParams(this.props.location.search);
|
||||||
return searchParams.get("returnUrl");
|
const returnUrl = searchParams.get("returnUrl");
|
||||||
|
if (returnUrl !== null) {
|
||||||
|
this.setState({
|
||||||
|
returnUrl: returnUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseUserField(key, value) {
|
parseUserField(key, value) {
|
||||||
@ -242,7 +249,7 @@ class UserEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}}>
|
<Row style={{marginTop: "20px"}}>
|
||||||
<CropperDiv buttonText={`${i18next.t("user:Upload a photo")}...`} title={i18next.t("user:Upload a photo")} user={this.state.user} account={this.props.account} />
|
<CropperDiv buttonText={`${i18next.t("user:Upload a photo")}...`} title={i18next.t("user:Upload a photo")} user={this.state.user} organization={this.state.organizations.find(organization => organization.name === this.state.organizationName)} />
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@ -604,8 +611,8 @@ class UserEditPage extends React.Component {
|
|||||||
const user = Setting.deepCopy(this.state.user);
|
const user = Setting.deepCopy(this.state.user);
|
||||||
UserBackend.updateUser(this.state.organizationName, this.state.userName, user)
|
UserBackend.updateUser(this.state.organizationName, this.state.userName, user)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
organizationName: this.state.user.owner,
|
organizationName: this.state.user.owner,
|
||||||
userName: this.state.user.name,
|
userName: this.state.user.name,
|
||||||
@ -619,30 +626,33 @@ class UserEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (willExist) {
|
if (willExist) {
|
||||||
const returnUrl = this.getReturnUrl();
|
if (this.state.returnUrl) {
|
||||||
if (returnUrl) {
|
window.location.href = this.state.returnUrl;
|
||||||
window.location.href = returnUrl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateUserField("owner", this.state.organizationName);
|
this.updateUserField("owner", this.state.organizationName);
|
||||||
this.updateUserField("name", this.state.userName);
|
this.updateUserField("name", this.state.userName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteUser() {
|
deleteUser() {
|
||||||
UserBackend.deleteUser(this.state.user)
|
UserBackend.deleteUser(this.state.user)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/users");
|
this.props.history.push("/users");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `User failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,26 +72,33 @@ class UserListPage extends BaseListPage {
|
|||||||
const newUser = this.newUser();
|
const newUser = this.newUser();
|
||||||
UserBackend.addUser(newUser)
|
UserBackend.addUser(newUser)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/users/${newUser.owner}/${newUser.name}`, mode: "add"});
|
this.props.history.push({pathname: `/users/${newUser.owner}/${newUser.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `User failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteUser(i) {
|
deleteUser(i) {
|
||||||
UserBackend.deleteUser(this.state.data[i])
|
UserBackend.deleteUser(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "User deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `User failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +174,7 @@ class UserListPage extends BaseListPage {
|
|||||||
...this.getColumnSearchProps("signupApplication"),
|
...this.getColumnSearchProps("signupApplication"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/applications/${text}`}>
|
<Link to={`/applications/${record.owner}/${text}`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -346,7 +353,7 @@ class UserListPage extends BaseListPage {
|
|||||||
title={`Sure to delete user: ${record.name} ?`}
|
title={`Sure to delete user: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteUser(index)}
|
onConfirm={() => this.deleteUser(index)}
|
||||||
>
|
>
|
||||||
<Button disabled={disabled} style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button disabled={disabled} style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -26,7 +26,7 @@ class WebAuthnCredentialTable extends React.Component {
|
|||||||
|
|
||||||
registerWebAuthn() {
|
registerWebAuthn() {
|
||||||
UserWebauthnBackend.registerWebauthnCredential().then((res) => {
|
UserWebauthnBackend.registerWebauthnCredential().then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully added webauthn credentials");
|
Setting.showMessage("success", "Successfully added webauthn credentials");
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", res.msg);
|
||||||
@ -34,7 +34,7 @@ class WebAuthnCredentialTable extends React.Component {
|
|||||||
|
|
||||||
this.props.refresh();
|
this.props.refresh();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ class WebAuthnCredentialTable extends React.Component {
|
|||||||
width: "170px",
|
width: "170px",
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Button style={{marginTop: "5px", marginBottom: "5px", marginRight: "5px"}} type="danger" onClick={() => {this.deleteRow(this.props.table, index);}}>
|
<Button style={{marginTop: "5px", marginBottom: "5px", marginRight: "5px"}} type="primary" danger onClick={() => {this.deleteRow(this.props.table, index);}}>
|
||||||
{i18next.t("general:Delete")}
|
{i18next.t("general:Delete")}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
@ -244,7 +244,7 @@ class WebhookEditPage extends React.Component {
|
|||||||
}} >
|
}} >
|
||||||
{
|
{
|
||||||
(
|
(
|
||||||
["signup", "login", "logout", "update-user"].map((option, index) => {
|
["signup", "login", "logout", "add-user", "update-user", "add-organization", "update-organization", "add-provider", "update-provider"].map((option, index) => {
|
||||||
return (
|
return (
|
||||||
<Option key={option} value={option}>{option}</Option>
|
<Option key={option} value={option}>{option}</Option>
|
||||||
);
|
);
|
||||||
@ -296,8 +296,8 @@ class WebhookEditPage extends React.Component {
|
|||||||
const webhook = Setting.deepCopy(this.state.webhook);
|
const webhook = Setting.deepCopy(this.state.webhook);
|
||||||
WebhookBackend.updateWebhook(this.state.webhook.owner, this.state.webhookName, webhook)
|
WebhookBackend.updateWebhook(this.state.webhook.owner, this.state.webhookName, webhook)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", "Successfully saved");
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
webhookName: this.state.webhook.name,
|
webhookName: this.state.webhook.name,
|
||||||
});
|
});
|
||||||
@ -308,22 +308,26 @@ class WebhookEditPage extends React.Component {
|
|||||||
this.props.history.push(`/webhooks/${this.state.webhook.name}`);
|
this.props.history.push(`/webhooks/${this.state.webhook.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateWebhookField("name", this.state.webhookName);
|
this.updateWebhookField("name", this.state.webhookName);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteWebhook() {
|
deleteWebhook() {
|
||||||
WebhookBackend.deleteWebhook(this.state.webhook)
|
WebhookBackend.deleteWebhook(this.state.webhook)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push("/webhooks");
|
this.props.history.push("/webhooks");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Webhook failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,26 +42,33 @@ class WebhookListPage extends BaseListPage {
|
|||||||
const newWebhook = this.newWebhook();
|
const newWebhook = this.newWebhook();
|
||||||
WebhookBackend.addWebhook(newWebhook)
|
WebhookBackend.addWebhook(newWebhook)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/webhooks/${newWebhook.name}`, mode: "add"});
|
this.props.history.push({pathname: `/webhooks/${newWebhook.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Webhook failed to add: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteWebhook(i) {
|
deleteWebhook(i) {
|
||||||
WebhookBackend.deleteWebhook(this.state.data[i])
|
WebhookBackend.deleteWebhook(this.state.data[i])
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
Setting.showMessage("success", "Webhook deleted successfully");
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.setState({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Webhook failed to delete: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +201,7 @@ class WebhookListPage extends BaseListPage {
|
|||||||
title={`Sure to delete webhook: ${record.name} ?`}
|
title={`Sure to delete webhook: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteWebhook(index)}
|
onConfirm={() => this.deleteWebhook(index)}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -54,7 +54,7 @@ export function oAuthParamsToQuery(oAuthParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// code
|
// code
|
||||||
return `?clientId=${oAuthParams.clientId}&responseType=${oAuthParams.responseType}&redirectUri=${oAuthParams.redirectUri}&scope=${oAuthParams.scope}&state=${oAuthParams.state}&nonce=${oAuthParams.nonce}&code_challenge_method=${oAuthParams.challengeMethod}&code_challenge=${oAuthParams.codeChallenge}`;
|
return `?clientId=${oAuthParams.clientId}&responseType=${oAuthParams.responseType}&redirectUri=${encodeURIComponent(oAuthParams.redirectUri)}&scope=${oAuthParams.scope}&state=${oAuthParams.state}&nonce=${oAuthParams.nonce}&code_challenge_method=${oAuthParams.challengeMethod}&code_challenge=${oAuthParams.codeChallenge}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getApplicationLogin(oAuthParams) {
|
export function getApplicationLogin(oAuthParams) {
|
||||||
@ -130,3 +130,13 @@ export function loginWithSaml(values, param) {
|
|||||||
},
|
},
|
||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getWechatMessageEvent() {
|
||||||
|
return fetch(`${Setting.ServerUrl}/api/get-webhook-event`, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Accept-Language": Setting.getAcceptLanguage(),
|
||||||
|
},
|
||||||
|
}).then(res => res.json());
|
||||||
|
}
|
||||||
|
@ -126,7 +126,7 @@ class AuthCallback extends React.Component {
|
|||||||
// If service was not specified, Casdoor must display a message notifying the client that it has successfully initiated a single sign-on session.
|
// If service was not specified, Casdoor must display a message notifying the client that it has successfully initiated a single sign-on session.
|
||||||
msg += "Now you can visit apps protected by Casdoor.";
|
msg += "Now you can visit apps protected by Casdoor.";
|
||||||
}
|
}
|
||||||
Util.showMessage("success", msg);
|
Setting.showMessage("success", msg);
|
||||||
|
|
||||||
if (casService !== "") {
|
if (casService !== "") {
|
||||||
const st = res.data;
|
const st = res.data;
|
||||||
@ -135,7 +135,7 @@ class AuthCallback extends React.Component {
|
|||||||
window.location.href = newUrl.toString();
|
window.location.href = newUrl.toString();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
Setting.showMessage("error", `Failed to log in: ${res.msg}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -148,7 +148,7 @@ class AuthCallback extends React.Component {
|
|||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const responseType = this.getResponseType();
|
const responseType = this.getResponseType();
|
||||||
if (responseType === "login") {
|
if (responseType === "login") {
|
||||||
Util.showMessage("success", "Logged in successfully");
|
Setting.showMessage("success", "Logged in successfully");
|
||||||
// Setting.goToLinkSoft(this, "/");
|
// Setting.goToLinkSoft(this, "/");
|
||||||
|
|
||||||
const link = Setting.getFromLink();
|
const link = Setting.getFromLink();
|
||||||
@ -156,7 +156,7 @@ class AuthCallback extends React.Component {
|
|||||||
} else if (responseType === "code") {
|
} else if (responseType === "code") {
|
||||||
const code = res.data;
|
const code = res.data;
|
||||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||||
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
// Setting.showMessage("success", `Authorization code: ${res.data}`);
|
||||||
} else if (responseType === "token" || responseType === "id_token") {
|
} else if (responseType === "token" || responseType === "id_token") {
|
||||||
const token = res.data;
|
const token = res.data;
|
||||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
|
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
|
||||||
|
@ -61,10 +61,7 @@ class ForgetPage extends React.Component {
|
|||||||
if (this.state.applicationName !== undefined) {
|
if (this.state.applicationName !== undefined) {
|
||||||
this.getApplication();
|
this.getApplication();
|
||||||
} else {
|
} else {
|
||||||
Util.showMessage(
|
Setting.showMessage("error", i18next.t("forget:Unknown forget type: ") + this.state.type);
|
||||||
"error",
|
|
||||||
i18next.t("forget:Unknown forget type: ") + this.state.type
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +99,13 @@ class ForgetPage extends React.Component {
|
|||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const phone = res.data.phone;
|
const phone = res.data.phone;
|
||||||
const email = res.data.email;
|
const email = res.data.email;
|
||||||
|
const saveFields = () => {
|
||||||
|
if (this.state.isFixed) {
|
||||||
|
forms.step2.setFieldsValue({email: this.state.fixedContent});
|
||||||
|
this.setState({username: this.state.fixedContent});
|
||||||
|
}
|
||||||
|
this.setState({current: 1});
|
||||||
|
};
|
||||||
this.setState({phone: phone, email: email, username: res.data.name, name: res.data.name});
|
this.setState({phone: phone, email: email, username: res.data.name, name: res.data.name});
|
||||||
|
|
||||||
if (phone !== "" && email === "") {
|
if (phone !== "" && email === "") {
|
||||||
@ -116,19 +120,15 @@ class ForgetPage extends React.Component {
|
|||||||
|
|
||||||
switch (res.data2) {
|
switch (res.data2) {
|
||||||
case "email":
|
case "email":
|
||||||
this.setState({isFixed: true, fixedContent: email, verifyType: "email"});
|
this.setState({isFixed: true, fixedContent: email, verifyType: "email"}, () => {saveFields();});
|
||||||
break;
|
break;
|
||||||
case "phone":
|
case "phone":
|
||||||
this.setState({isFixed: true, fixedContent: phone, verifyType: "phone"});
|
this.setState({isFixed: true, fixedContent: phone, verifyType: "phone"}, () => {saveFields();});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
saveFields();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (this.state.isFixed) {
|
|
||||||
forms.step2.setFieldsValue({email: this.state.fixedContent});
|
|
||||||
this.setState({username: this.state.fixedContent});
|
|
||||||
}
|
|
||||||
this.setState({current: 1});
|
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
|
Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
|
||||||
}
|
}
|
||||||
@ -136,11 +136,7 @@ class ForgetPage extends React.Component {
|
|||||||
break;
|
break;
|
||||||
case "step2":
|
case "step2":
|
||||||
const oAuthParams = Util.getOAuthGetParameters();
|
const oAuthParams = Util.getOAuthGetParameters();
|
||||||
if (this.state.verifyType === "email") {
|
const login = () => {
|
||||||
this.setState({username: this.state.email});
|
|
||||||
} else if (this.state.verifyType === "phone") {
|
|
||||||
this.setState({username: this.state.phone});
|
|
||||||
}
|
|
||||||
AuthBackend.login({
|
AuthBackend.login({
|
||||||
application: forms.step2.getFieldValue("application"),
|
application: forms.step2.getFieldValue("application"),
|
||||||
organization: forms.step2.getFieldValue("organization"),
|
organization: forms.step2.getFieldValue("organization"),
|
||||||
@ -156,6 +152,12 @@ class ForgetPage extends React.Component {
|
|||||||
Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
|
Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
if (this.state.verifyType === "email") {
|
||||||
|
this.setState({username: this.state.email}, () => {login();});
|
||||||
|
} else if (this.state.verifyType === "phone") {
|
||||||
|
this.setState({username: this.state.phone}, () => {login();});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -28,11 +28,7 @@ import i18next from "i18next";
|
|||||||
import CustomGithubCorner from "../CustomGithubCorner";
|
import CustomGithubCorner from "../CustomGithubCorner";
|
||||||
import {CountDownInput} from "../common/CountDownInput";
|
import {CountDownInput} from "../common/CountDownInput";
|
||||||
import SelectLanguageBox from "../SelectLanguageBox";
|
import SelectLanguageBox from "../SelectLanguageBox";
|
||||||
import {withTranslation} from "react-i18next";
|
|
||||||
import {CaptchaModal} from "../common/CaptchaModal";
|
import {CaptchaModal} from "../common/CaptchaModal";
|
||||||
import {withRouter} from "react-router-dom";
|
|
||||||
|
|
||||||
const {TabPane} = Tabs;
|
|
||||||
|
|
||||||
class LoginPage extends React.Component {
|
class LoginPage extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -69,7 +65,7 @@ class LoginPage extends React.Component {
|
|||||||
} else if (this.state.type === "saml") {
|
} else if (this.state.type === "saml") {
|
||||||
this.getSamlApplication();
|
this.getSamlApplication();
|
||||||
} else {
|
} else {
|
||||||
Util.showMessage("error", `Unknown authentication type: ${this.state.type}`);
|
Setting.showMessage("error", `Unknown authentication type: ${this.state.type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +90,7 @@ class LoginPage extends React.Component {
|
|||||||
application: res.data,
|
application: res.data,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Util.showMessage("error", res.msg);
|
// Setting.showMessage("error", res.msg);
|
||||||
this.setState({
|
this.setState({
|
||||||
application: res.data,
|
application: res.data,
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
@ -124,7 +120,7 @@ class LoginPage extends React.Component {
|
|||||||
applicationName: res.data.name,
|
applicationName: res.data.name,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Util.showMessage("error", res.msg);
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -271,7 +267,7 @@ class LoginPage extends React.Component {
|
|||||||
// If service was not specified, Casdoor must display a message notifying the client that it has successfully initiated a single sign-on session.
|
// If service was not specified, Casdoor must display a message notifying the client that it has successfully initiated a single sign-on session.
|
||||||
msg += "Now you can visit apps protected by Casdoor.";
|
msg += "Now you can visit apps protected by Casdoor.";
|
||||||
}
|
}
|
||||||
Util.showMessage("success", msg);
|
Setting.showMessage("success", msg);
|
||||||
|
|
||||||
this.setState({openCaptchaModal: false});
|
this.setState({openCaptchaModal: false});
|
||||||
|
|
||||||
@ -283,7 +279,7 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({openCaptchaModal: false});
|
this.setState({openCaptchaModal: false});
|
||||||
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
Setting.showMessage("error", `Failed to log in: ${res.msg}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -297,13 +293,13 @@ class LoginPage extends React.Component {
|
|||||||
const responseType = values["type"];
|
const responseType = values["type"];
|
||||||
|
|
||||||
if (responseType === "login") {
|
if (responseType === "login") {
|
||||||
Util.showMessage("success", "Logged in successfully");
|
Setting.showMessage("success", "Logged in successfully");
|
||||||
|
|
||||||
const link = Setting.getFromLink();
|
const link = Setting.getFromLink();
|
||||||
Setting.goToLink(link);
|
Setting.goToLink(link);
|
||||||
} else if (responseType === "code") {
|
} else if (responseType === "code") {
|
||||||
this.postCodeLoginAction(res);
|
this.postCodeLoginAction(res);
|
||||||
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
// Setting.showMessage("success", `Authorization code: ${res.data}`);
|
||||||
} else if (responseType === "token" || responseType === "id_token") {
|
} else if (responseType === "token" || responseType === "id_token") {
|
||||||
const accessToken = res.data;
|
const accessToken = res.data;
|
||||||
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
||||||
@ -314,7 +310,7 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({openCaptchaModal: false});
|
this.setState({openCaptchaModal: false});
|
||||||
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
Setting.showMessage("error", `Failed to log in: ${res.msg}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -337,8 +333,8 @@ class LoginPage extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<Result
|
<Result
|
||||||
status="error"
|
status="error"
|
||||||
title="Sign Up Error"
|
title={i18next.t("application:Sign Up Error")}
|
||||||
subTitle={"The application does not allow to sign up new account"}
|
subTitle={i18next.t("application:The application does not allow to sign up new account")}
|
||||||
extra={[
|
extra={[
|
||||||
<Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application, this.props.history)}>
|
<Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application, this.props.history)}>
|
||||||
{
|
{
|
||||||
@ -370,7 +366,7 @@ class LoginPage extends React.Component {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "Please input your application!",
|
message: i18next.t("application:Please input your application!"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@ -381,7 +377,7 @@ class LoginPage extends React.Component {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "Please input your organization!",
|
message: i18next.t("application:Please input your organization!"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@ -675,7 +671,7 @@ class LoginPage extends React.Component {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then(res => res.json()).then((res) => {
|
.then(res => res.json()).then((res) => {
|
||||||
if (res.msg === "") {
|
if (res.status === "ok") {
|
||||||
const responseType = values["type"];
|
const responseType = values["type"];
|
||||||
if (responseType === "code") {
|
if (responseType === "code") {
|
||||||
this.postCodeLoginAction(res);
|
this.postCodeLoginAction(res);
|
||||||
@ -683,7 +679,7 @@ class LoginPage extends React.Component {
|
|||||||
const accessToken = res.data;
|
const accessToken = res.data;
|
||||||
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("success", "Successfully logged in with webauthn credentials");
|
Setting.showMessage("success", i18next.t("login:Successfully logged in with webauthn credentials"));
|
||||||
Setting.goToLink("/");
|
Setting.goToLink("/");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -691,7 +687,7 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}${error}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -736,21 +732,16 @@ class LoginPage extends React.Component {
|
|||||||
|
|
||||||
renderMethodChoiceBox() {
|
renderMethodChoiceBox() {
|
||||||
const application = this.getApplicationObj();
|
const application = this.getApplicationObj();
|
||||||
|
const items = [
|
||||||
|
{label: i18next.t("login:Password"), key: "password"},
|
||||||
|
];
|
||||||
|
application.enableCodeSignin ? items.push({label: i18next.t("login:Verification Code"), key: "verificationCode"}) : null;
|
||||||
|
application.enableWebAuthn ? items.push({label: i18next.t("login:WebAuthn"), key: "webAuthn"}) : null;
|
||||||
|
|
||||||
if (application.enableCodeSignin || application.enableWebAuthn) {
|
if (application.enableCodeSignin || application.enableWebAuthn) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Tabs size={"small"} defaultActiveKey="password" onChange={(key) => {this.setState({loginMethod: key});}} centered>
|
<Tabs items={items} size={"small"} defaultActiveKey="password" onChange={(key) => {this.setState({loginMethod: key});}} centered>
|
||||||
<TabPane tab={i18next.t("login:Password")} key="password" />
|
|
||||||
{
|
|
||||||
!application.enableCodeSignin ? null : (
|
|
||||||
<TabPane tab={i18next.t("login:Verification Code")} key="verificationCode" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
!application.enableWebAuthn ? null : (
|
|
||||||
<TabPane tab={i18next.t("login:WebAuthn")} key="webAuthn" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -783,7 +774,7 @@ class LoginPage extends React.Component {
|
|||||||
<div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${application.formBackgroundUrl})`}}>
|
<div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${application.formBackgroundUrl})`}}>
|
||||||
<CustomGithubCorner />
|
<CustomGithubCorner />
|
||||||
<div className="login-content" style={{margin: this.parseOffset(application.formOffset)}}>
|
<div className="login-content" style={{margin: this.parseOffset(application.formOffset)}}>
|
||||||
{Setting.inIframe() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
|
{Setting.inIframe() || Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
|
||||||
<div className="login-panel">
|
<div className="login-panel">
|
||||||
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
|
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
|
||||||
<div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
|
<div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
|
||||||
@ -800,7 +791,7 @@ class LoginPage extends React.Component {
|
|||||||
{/* {*/}
|
{/* {*/}
|
||||||
{/* this.state.clientId !== null ? "Redirect" : null*/}
|
{/* this.state.clientId !== null ? "Redirect" : null*/}
|
||||||
{/* }*/}
|
{/* }*/}
|
||||||
<SelectLanguageBox id="language-box-corner" style={{top: "55px", right: "5px", position: "absolute"}} />
|
<SelectLanguageBox languages={application.organizationObj.languages} style={{top: "55px", right: "5px", position: "absolute"}} />
|
||||||
{
|
{
|
||||||
this.renderSignedInBox()
|
this.renderSignedInBox()
|
||||||
}
|
}
|
||||||
@ -817,4 +808,4 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTranslation()(withRouter(LoginPage));
|
export default LoginPage;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user