Compare commits

..

13 Commits

Author SHA1 Message Date
Resulte Lee
1f11d22c1c fix: add managed account table for supporting Chrome extension to auto login (#1030)
* feat: add manage accounts table(support chrome extension to auto login)

* fix go lint err

* rename manageAccounts to managedAccounts

* expand up&down buttom column width

* rename ManagedAccountsTable to ManagedAccountTable
2022-08-22 00:25:39 +08:00
Gucheng Wang
b6988286b5 Improve i18n for permission page 2022-08-21 23:17:14 +08:00
leoshine
64f787fab5 feat: can modify static resource url by app.conf (#1045)
* feat: can modify static resource url by app.conf

Signed-off-by: magicwind <2814461814@qq.com>

* Update static_filter.go

Signed-off-by: magicwind <2814461814@qq.com>
Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-08-21 21:40:27 +08:00
Resulte Lee
39c6bd5850 fix: country/region prompted page not show when signin (#1047) 2022-08-21 17:41:07 +08:00
Gucheng Wang
7312c5ce3c Don't check domain for submitPermissionEdit() 2022-08-21 15:28:19 +08:00
Resulte Lee
0bc5b90218 fix: add country/region selectbox in prompt page (#1022) 2022-08-21 11:12:23 +08:00
q1anx1
f3b3376a3c fix: fix get version error (#1044)
* feat: fix get version error

* feat: more safe

* fix
2022-08-21 10:47:36 +08:00
q1anx1
feec6abd88 fix: fix translations for system info page (#1042) 2022-08-20 23:00:37 +08:00
q1anx1
c50042c85a feat: fix the go.sum error (#1040) 2022-08-20 22:04:37 +08:00
q1anx1
ef4c3833a4 feat: add system info page (#1033)
* feat: add system info page

* feat: add some code

* fix
2022-08-20 21:22:46 +08:00
疯魔慕薇
67a5adf585 feat: replace panic with details json error payload. (#1039)
Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>

Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>
2022-08-20 21:09:32 +08:00
疯魔慕薇
08a1e7ae32 fix: keep phone/email unique. (#1038)
Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>

Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>
2022-08-20 12:14:08 +08:00
Fernando López Guevara
7d979cbaf0 feat(storage): add support for min.io storage (#1037)
* feat(storage): add support for min.io storage

* fix(minio): use doublequote

* fix(storage): change storage name to MinIO
2022-08-20 11:30:13 +08:00
60 changed files with 1125 additions and 130 deletions

View File

@@ -19,6 +19,7 @@ RUN apk add curl
RUN apk add ca-certificates && update-ca-certificates
WORKDIR /
COPY --from=BACK /go/src/casdoor/.git/refs/heads .git/refs/heads
COPY --from=BACK /go/src/casdoor/server ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
@@ -41,6 +42,7 @@ RUN apt update
RUN apt install -y ca-certificates && update-ca-certificates
WORKDIR /
COPY --from=BACK /go/src/casdoor/.git/refs/heads .git/refs/heads
COPY --from=BACK /go/src/casdoor/server ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh

View File

@@ -107,6 +107,7 @@ p, *, *, POST, /api/acs, *, *
p, *, *, GET, /api/saml/metadata, *, *
p, *, *, *, /cas, *, *
p, *, *, *, /api/webauthn, *, *
p, *, *, GET, /api/get-release, *, *
`
sa := stringadapter.NewAdapter(ruleText)

View File

@@ -105,7 +105,8 @@ func (c *ApiController) Signup() {
var form RequestForm
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
@@ -156,6 +157,12 @@ func (c *ApiController) Signup() {
username = id
}
initScore, err := getInitScore()
if err != nil {
c.ResponseError(fmt.Errorf("get init score failed, error: %w", err).Error())
return
}
user := &object.User{
Owner: form.Organization,
Name: username,
@@ -171,7 +178,7 @@ func (c *ApiController) Signup() {
Affiliation: form.Affiliation,
IdCard: form.IdCard,
Region: form.Region,
Score: getInitScore(),
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,

View File

@@ -111,8 +111,7 @@ func (c *ApiController) GetOrganizationApplications() {
return
}
var applications []*object.Application
applications = object.GetApplicationsByOrganizationName(owner, organization)
applications := object.GetApplicationsByOrganizationName(owner, organization)
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
}
@@ -131,7 +130,8 @@ func (c *ApiController) UpdateApplication() {
var application object.Application
err := json.Unmarshal(c.Ctx.Input.RequestBody, &application)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application))
@@ -149,7 +149,8 @@ func (c *ApiController) AddApplication() {
var application object.Application
err := json.Unmarshal(c.Ctx.Input.RequestBody, &application)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddApplication(&application))
@@ -167,7 +168,8 @@ func (c *ApiController) DeleteApplication() {
var application object.Application
err := json.Unmarshal(c.Ctx.Input.RequestBody, &application)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteApplication(&application))

View File

@@ -344,7 +344,7 @@ func (c *ApiController) Login() {
user = object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
}
if user != nil && user.IsDeleted == false {
if user != nil && !user.IsDeleted {
// Sign in via OAuth (want to sign up but already have account)
if user.IsForbidden {
@@ -384,6 +384,12 @@ func (c *ApiController) Login() {
properties := map[string]string{}
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore()
if err != nil {
c.ResponseError(fmt.Errorf("get init score failed, error: %w", err).Error())
return
}
user = &object.User{
Owner: application.Organization,
Name: userInfo.Username,
@@ -394,7 +400,7 @@ func (c *ApiController) Login() {
Avatar: userInfo.AvatarUrl,
Address: []string{},
Email: userInfo.Email,
Score: getInitScore(),
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,

View File

@@ -19,6 +19,7 @@ import (
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -58,6 +59,7 @@ func (c *ApiController) IsGlobalAdmin() bool {
func (c *ApiController) GetSessionUsername() string {
// check if user session expired
sessionData := c.GetSessionData()
if sessionData != nil &&
sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() {
@@ -120,7 +122,8 @@ func (c *ApiController) GetSessionData() *SessionData {
sessionData := &SessionData{}
err := util.JsonToStruct(session.(string), sessionData)
if err != nil {
panic(err)
logs.Error("GetSessionData failed, error: %s", err)
return nil
}
return sessionData

View File

@@ -76,7 +76,8 @@ func (c *ApiController) UpdateCert() {
var cert object.Cert
err := json.Unmarshal(c.Ctx.Input.RequestBody, &cert)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateCert(id, &cert))
@@ -94,7 +95,8 @@ func (c *ApiController) AddCert() {
var cert object.Cert
err := json.Unmarshal(c.Ctx.Input.RequestBody, &cert)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddCert(&cert))
@@ -112,7 +114,8 @@ func (c *ApiController) DeleteCert() {
var cert object.Cert
err := json.Unmarshal(c.Ctx.Input.RequestBody, &cert)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteCert(&cert))

View File

@@ -30,7 +30,8 @@ func (c *ApiController) Enforce() {
var permissionRule object.PermissionRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRule)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = object.Enforce(userId, &permissionRule)
@@ -47,7 +48,8 @@ func (c *ApiController) BatchEnforce() {
var permissionRules []object.PermissionRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRules)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = object.BatchEnforce(userId, permissionRules)

View File

@@ -199,7 +199,8 @@ func (c *ApiController) DeleteLdap() {
var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
object.GetLdapAutoSynchronizer().StopAutoSync(ldap.Id)
@@ -217,7 +218,8 @@ func (c *ApiController) SyncLdapUsers() {
var users []object.LdapRespUser
err := json.Unmarshal(c.Ctx.Input.RequestBody, &users)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
object.UpdateLdapSyncTime(ldapId)
@@ -239,7 +241,8 @@ func (c *ApiController) CheckLdapUsersExist() {
var uuids []string
err := json.Unmarshal(c.Ctx.Input.RequestBody, &uuids)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
exist := object.CheckLdapUuidExist(owner, uuids)

View File

@@ -37,7 +37,8 @@ func (c *ApiController) Unlink() {
var form LinkForm
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
providerType := form.ProviderType

View File

@@ -76,7 +76,8 @@ func (c *ApiController) UpdateModel() {
var model object.Model
err := json.Unmarshal(c.Ctx.Input.RequestBody, &model)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateModel(id, &model))
@@ -94,7 +95,8 @@ func (c *ApiController) AddModel() {
var model object.Model
err := json.Unmarshal(c.Ctx.Input.RequestBody, &model)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddModel(&model))
@@ -112,7 +114,8 @@ func (c *ApiController) DeleteModel() {
var model object.Model
err := json.Unmarshal(c.Ctx.Input.RequestBody, &model)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteModel(&model))

View File

@@ -76,7 +76,8 @@ func (c *ApiController) UpdateOrganization() {
var organization object.Organization
err := json.Unmarshal(c.Ctx.Input.RequestBody, &organization)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateOrganization(id, &organization))
@@ -94,7 +95,8 @@ func (c *ApiController) AddOrganization() {
var organization object.Organization
err := json.Unmarshal(c.Ctx.Input.RequestBody, &organization)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization))
@@ -112,7 +114,8 @@ func (c *ApiController) DeleteOrganization() {
var organization object.Organization
err := json.Unmarshal(c.Ctx.Input.RequestBody, &organization)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteOrganization(&organization))

View File

@@ -95,7 +95,8 @@ func (c *ApiController) UpdatePayment() {
var payment object.Payment
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdatePayment(id, &payment))
@@ -113,7 +114,8 @@ func (c *ApiController) AddPayment() {
var payment object.Payment
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddPayment(&payment))
@@ -131,7 +133,8 @@ func (c *ApiController) DeletePayment() {
var payment object.Payment
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeletePayment(&payment))
@@ -157,7 +160,8 @@ func (c *ApiController) NotifyPayment() {
if ok {
_, err := c.Ctx.ResponseWriter.Write([]byte("success"))
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
} else {
panic(fmt.Errorf("NotifyPayment() failed: %v", ok))

View File

@@ -94,7 +94,8 @@ func (c *ApiController) UpdatePermission() {
var permission object.Permission
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdatePermission(id, &permission))
@@ -112,7 +113,8 @@ func (c *ApiController) AddPermission() {
var permission object.Permission
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddPermission(&permission))
@@ -130,7 +132,8 @@ func (c *ApiController) DeletePermission() {
var permission object.Permission
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeletePermission(&permission))

View File

@@ -80,7 +80,8 @@ func (c *ApiController) UpdateProduct() {
var product object.Product
err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateProduct(id, &product))
@@ -98,7 +99,8 @@ func (c *ApiController) AddProduct() {
var product object.Product
err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddProduct(&product))
@@ -116,7 +118,8 @@ func (c *ApiController) DeleteProduct() {
var product object.Product
err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteProduct(&product))

View File

@@ -76,7 +76,8 @@ func (c *ApiController) UpdateProvider() {
var provider object.Provider
err := json.Unmarshal(c.Ctx.Input.RequestBody, &provider)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateProvider(id, &provider))
@@ -94,7 +95,8 @@ func (c *ApiController) AddProvider() {
var provider object.Provider
err := json.Unmarshal(c.Ctx.Input.RequestBody, &provider)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddProvider(&provider))
@@ -112,7 +114,8 @@ func (c *ApiController) DeleteProvider() {
var provider object.Provider
err := json.Unmarshal(c.Ctx.Input.RequestBody, &provider)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteProvider(&provider))

View File

@@ -59,7 +59,8 @@ func (c *ApiController) GetRecordsByFilter() {
record := &object.Record{}
err := util.JsonToStruct(body, record)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = object.GetRecordsByField(record)

View File

@@ -72,7 +72,8 @@ func (c *ApiController) UpdateResource() {
var resource object.Resource
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateResource(id, &resource))
@@ -87,7 +88,8 @@ func (c *ApiController) AddResource() {
var resource object.Resource
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddResource(&resource))
@@ -102,7 +104,8 @@ func (c *ApiController) DeleteResource() {
var resource object.Resource
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
provider, _, ok := c.GetProviderFromContext("Storage")

View File

@@ -76,7 +76,8 @@ func (c *ApiController) UpdateRole() {
var role object.Role
err := json.Unmarshal(c.Ctx.Input.RequestBody, &role)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateRole(id, &role))
@@ -94,7 +95,8 @@ func (c *ApiController) AddRole() {
var role object.Role
err := json.Unmarshal(c.Ctx.Input.RequestBody, &role)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddRole(&role))
@@ -112,7 +114,8 @@ func (c *ApiController) DeleteRole() {
var role object.Role
err := json.Unmarshal(c.Ctx.Input.RequestBody, &role)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteRole(&role))

View File

@@ -76,7 +76,8 @@ func (c *ApiController) UpdateSyncer() {
var syncer object.Syncer
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer))
@@ -94,7 +95,8 @@ func (c *ApiController) AddSyncer() {
var syncer object.Syncer
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddSyncer(&syncer))
@@ -112,7 +114,8 @@ func (c *ApiController) DeleteSyncer() {
var syncer object.Syncer
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteSyncer(&syncer))

View File

@@ -0,0 +1,82 @@
// 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 controllers
import (
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
type SystemInfo struct {
MemoryUsed uint64 `json:"memory_used"`
MemoryTotal uint64 `json:"memory_total"`
CpuUsage []float64 `json:"cpu_usage"`
}
// GetSystemInfo
// @Title GetSystemInfo
// @Tag System API
// @Description get user's system info
// @Param id query string true "The id of the user"
// @Success 200 {object} object.SystemInfo The Response object
// @router /get-system-info [get]
func (c *ApiController) GetSystemInfo() {
id := c.GetString("id")
if id == "" {
id = c.GetSessionUsername()
}
user := object.GetUser(id)
if user == nil || !user.IsGlobalAdmin {
c.ResponseError("You are not authorized to access this resource")
return
}
cpuUsage, err := util.GetCpuUsage()
if err != nil {
c.ResponseError(err.Error())
return
}
memoryUsed, memoryTotal, err := util.GetMemoryUsage()
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = SystemInfo{
CpuUsage: cpuUsage,
MemoryUsed: memoryUsed,
MemoryTotal: memoryTotal,
}
c.ServeJSON()
}
// GitRepoVersion
// @Title GitRepoVersion
// @Tag System API
// @Description get local github repo's latest release version info
// @Success 200 {string} local latest version hash of casdoor
// @router /get-release [get]
func (c *ApiController) GitRepoVersion() {
version, err := util.GetGitRepoVersion()
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = version
c.ServeJSON()
}

View File

@@ -79,7 +79,8 @@ func (c *ApiController) UpdateToken() {
var token object.Token
err := json.Unmarshal(c.Ctx.Input.RequestBody, &token)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateToken(id, &token))
@@ -97,7 +98,8 @@ func (c *ApiController) AddToken() {
var token object.Token
err := json.Unmarshal(c.Ctx.Input.RequestBody, &token)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddToken(&token))
@@ -115,7 +117,8 @@ func (c *ApiController) DeleteToken() {
var token object.Token
err := json.Unmarshal(c.Ctx.Input.RequestBody, &token)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteToken(&token))

View File

@@ -149,7 +149,8 @@ func (c *ApiController) UpdateUser() {
var user object.User
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
if user.DisplayName == "" {
@@ -183,7 +184,8 @@ func (c *ApiController) AddUser() {
var user object.User
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddUser(&user))
@@ -201,7 +203,8 @@ func (c *ApiController) DeleteUser() {
var user object.User
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteUser(&user))
@@ -220,7 +223,8 @@ func (c *ApiController) GetEmailAndPhone() {
var form RequestForm
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
user := object.GetUserByFields(form.Organization, form.Username)
@@ -306,7 +310,8 @@ func (c *ApiController) CheckUserPassword() {
var user object.User
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
_, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password)

View File

@@ -24,17 +24,18 @@ import (
"github.com/casdoor/casdoor/util"
)
func saveFile(path string, file *multipart.File) {
func saveFile(path string, file *multipart.File) (err error) {
f, err := os.Create(path)
if err != nil {
panic(err)
return err
}
defer f.Close()
_, err = io.Copy(f, *file)
if err != nil {
panic(err)
return err
}
return nil
}
func (c *ApiController) UploadUsers() {
@@ -43,13 +44,18 @@ func (c *ApiController) UploadUsers() {
file, header, err := c.Ctx.Request.FormFile("file")
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path)
saveFile(path, &file)
err = saveFile(path, &file)
if err != nil {
c.ResponseError(err.Error())
return
}
affected := object.UploadUsers(owner, fileId)
if affected {

View File

@@ -23,9 +23,8 @@ import (
"github.com/casdoor/casdoor/util"
)
// ResponseOk ...
func (c *ApiController) ResponseOk(data ...interface{}) {
resp := Response{Status: "ok"}
// ResponseJsonData ...
func (c *ApiController) ResponseJsonData(resp *Response, data ...interface{}) {
switch len(data) {
case 2:
resp.Data2 = data[1]
@@ -37,18 +36,16 @@ func (c *ApiController) ResponseOk(data ...interface{}) {
c.ServeJSON()
}
// ResponseOk ...
func (c *ApiController) ResponseOk(data ...interface{}) {
resp := &Response{Status: "ok"}
c.ResponseJsonData(resp, data...)
}
// ResponseError ...
func (c *ApiController) ResponseError(error string, data ...interface{}) {
resp := Response{Status: "error", Msg: error}
switch len(data) {
case 2:
resp.Data2 = data[1]
fallthrough
case 1:
resp.Data = data[0]
}
c.Data["json"] = resp
c.ServeJSON()
resp := &Response{Status: "error", Msg: error}
c.ResponseJsonData(resp, data...)
}
// SetTokenErrorHttpStatus ...
@@ -78,13 +75,8 @@ func (c *ApiController) RequireSignedIn() (string, bool) {
return userId, true
}
func getInitScore() int {
score, err := strconv.Atoi(conf.GetConfigString("initScore"))
if err != nil {
panic(err)
}
return score
func getInitScore() (int, error) {
return strconv.Atoi(conf.GetConfigString("initScore"))
}
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, *object.User, bool) {

View File

@@ -76,7 +76,8 @@ func (c *ApiController) UpdateWebhook() {
var webhook object.Webhook
err := json.Unmarshal(c.Ctx.Input.RequestBody, &webhook)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook))
@@ -94,7 +95,8 @@ func (c *ApiController) AddWebhook() {
var webhook object.Webhook
err := json.Unmarshal(c.Ctx.Input.RequestBody, &webhook)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddWebhook(&webhook))
@@ -112,7 +114,8 @@ func (c *ApiController) DeleteWebhook() {
var webhook object.Webhook
err := json.Unmarshal(c.Ctx.Input.RequestBody, &webhook)
if err != nil {
panic(err)
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteWebhook(&webhook))

7
go.mod
View File

@@ -21,6 +21,7 @@ require (
github.com/go-pay/gopay v1.5.72
github.com/go-sql-driver/mysql v1.5.0
github.com/golang-jwt/jwt/v4 v4.2.0
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/lestrrat-go/jwx v0.9.0
@@ -31,13 +32,17 @@ require (
github.com/russellhaering/gosaml2 v0.6.0
github.com/russellhaering/goxmldsig v1.1.1
github.com/satori/go.uuid v1.2.0
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.8.0
github.com/tealeg/xlsx v1.0.5
github.com/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/ini.v1 v1.62.0 // indirect

28
go.sum
View File

@@ -156,6 +156,8 @@ github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-pay/gopay v1.5.72 h1:3zm64xMBhJBa8rXbm//q5UiGgOa4WO5XYEnU394N2Zw=
github.com/go-pay/gopay v1.5.72/go.mod h1:0qOGIJuFW7PKDOjmecwKyW0mgsVImgwB9yPJj0ilpn8=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
@@ -219,8 +221,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -376,6 +379,8 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
@@ -389,13 +394,15 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
@@ -406,6 +413,10 @@ github.com/tencentcloud/tencentcloud-sdk-go v1.0.154 h1:THBgwGwUQtsw6L53cSSA2wwL
github.com/tencentcloud/tencentcloud-sdk-go v1.0.154/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI=
github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo=
github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/volcengine/volc-sdk-golang v1.0.19 h1:jJp+aJgK0e//rZ9I0K2Y7ufJwvuZRo/AQsYDynXMNgA=
github.com/volcengine/volc-sdk-golang v1.0.19/go.mod h1:+GGi447k4p1I5PNdbpG2GLaF0Ui9vIInTojMM0IfSS4=
@@ -417,6 +428,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -537,6 +550,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -563,8 +577,10 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -626,7 +642,6 @@ golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4X
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -739,8 +754,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -45,7 +45,8 @@ func main() {
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
// beego.DelStaticPath("/static")
beego.SetStaticPath("/static", "web/build/static")
// beego.SetStaticPath("/static", "web/build/static")
beego.BConfig.WebConfig.DirectoryIndex = true
beego.SetStaticPath("/swagger", "swagger")
beego.SetStaticPath("/files", "files")

View File

@@ -62,10 +62,10 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if HasUserByField(organization.Name, "name", username) {
return "username already exists"
}
if HasUserByField(organization.Name, "email", username) {
if HasUserByField(organization.Name, "email", email) {
return "email already exists"
}
if HasUserByField(organization.Name, "phone", username) {
if HasUserByField(organization.Name, "phone", phone) {
return "phone already exists"
}
}

View File

@@ -84,6 +84,7 @@ func initBuiltInOrganization() bool {
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
},
}
AddOrganization(organization)

View File

@@ -46,7 +46,7 @@ type Organization struct {
EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"`
AccountItems []*AccountItem `xorm:"varchar(2000)" json:"accountItems"`
AccountItems []*AccountItem `xorm:"varchar(3000)" json:"accountItems"`
}
func GetOrganizationCount(owner, field, value string) int {

View File

@@ -114,6 +114,8 @@ type User struct {
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
}
type Userinfo struct {
@@ -128,6 +130,13 @@ type Userinfo struct {
Phone string `json:"phone,omitempty"`
}
type ManagedAccount struct {
Application string `xorm:"varchar(100)" json:"application"`
Username string `xorm:"varchar(100)" json:"username"`
Password string `xorm:"varchar(100)" json:"password"`
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
}
func GetGlobalUserCount(field, value string) int {
session := GetSession("", -1, -1, field, value, "", "")
count, err := session.Count(&User{})
@@ -334,6 +343,12 @@ func GetMaskedUser(user *User) *User {
if user.Password != "" {
user.Password = "***"
}
if user.ManagedAccounts != nil {
for _, manageAccount := range user.ManagedAccounts {
manageAccount.Password = "***"
}
}
return user
}
@@ -378,7 +393,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
columns = []string{
"owner", "display_name", "avatar",
"location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "signup_application",
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials",
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
"signin_wrong_times", "last_signin_wrong_time",
}
}

View File

@@ -202,4 +202,7 @@ func initAPI() {
beego.Router("/api/webauthn/signup/finish", &controllers.ApiController{}, "Post:WebAuthnSignupFinish")
beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "Get:WebAuthnSigninBegin")
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "Post:WebAuthnSigninFinish")
beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo")
beego.Router("/api/get-release", &controllers.ApiController{}, "GET:GitRepoVersion")
}

View File

@@ -16,12 +16,19 @@ package routers
import (
"net/http"
"os"
"strings"
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
"github.com/casdoor/casdoor/util"
)
var (
oldStaticBaseUrl = "https://cdn.casbin.org"
newStaticBaseUrl = beego.AppConfig.String("staticBaseUrl")
)
func StaticFilter(ctx *context.Context) {
urlPath := ctx.Request.URL.Path
if strings.HasPrefix(urlPath, "/api/") || strings.HasPrefix(urlPath, "/.well-known/") {
@@ -38,9 +45,35 @@ func StaticFilter(ctx *context.Context) {
path += urlPath
}
if util.FileExist(path) {
if !util.FileExist(path) {
path = "web/build/index.html"
}
if oldStaticBaseUrl == newStaticBaseUrl {
http.ServeFile(ctx.ResponseWriter, ctx.Request, path)
} else {
http.ServeFile(ctx.ResponseWriter, ctx.Request, "web/build/index.html")
serveFileWithReplace(ctx.ResponseWriter, ctx.Request, path, oldStaticBaseUrl, newStaticBaseUrl)
}
}
func serveFileWithReplace(w http.ResponseWriter, r *http.Request, name string, old string, new string) {
f, err := os.Open(name)
if err != nil {
panic(err)
}
defer f.Close()
d, err := f.Stat()
if err != nil {
panic(err)
}
oldContent := util.ReadStringFromPath(name)
newContent := strings.ReplaceAll(oldContent, old, new)
http.ServeContent(w, r, d.Name(), d.ModTime(), strings.NewReader(newContent))
_, err = w.Write([]byte(newContent))
if err != nil {
panic(err)
}
}

36
storage/minio_s3.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright 2021 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 storage
import (
awss3 "github.com/aws/aws-sdk-go/service/s3"
"github.com/casdoor/oss"
"github.com/casdoor/oss/s3"
)
func NewMinIOS3StorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
sp := s3.New(&s3.Config{
AccessID: clientId,
AccessKey: clientSecret,
Region: region,
Bucket: bucket,
Endpoint: endpoint,
S3Endpoint: endpoint,
ACL: awss3.BucketCannedACLPublicRead,
S3ForcePathStyle: true,
})
return sp
}

View File

@@ -22,6 +22,8 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
return NewLocalFileSystemStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "AWS S3":
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "MinIO":
return NewMinIOS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Aliyun OSS":
return NewAliyunOssStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Tencent Cloud COS":

78
util/system.go Normal file
View File

@@ -0,0 +1,78 @@
// 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 (
"io/ioutil"
"os"
"runtime"
"strings"
"time"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/mem"
)
// get cpu usage
func GetCpuUsage() ([]float64, error) {
usage, err := cpu.Percent(time.Second, true)
return usage, err
}
var fileDate, version string
// get memory usage
func GetMemoryUsage() (uint64, uint64, error) {
virtualMem, err := mem.VirtualMemory()
if err != nil {
return 0, 0, err
}
var m runtime.MemStats
runtime.ReadMemStats(&m)
return m.TotalAlloc, virtualMem.Total, nil
}
// get github repo release version
func GetGitRepoVersion() (string, error) {
pwd, err := os.Getwd()
if err != nil {
return "", err
}
fileInfos, err := ioutil.ReadDir(pwd + "/.git/refs/heads")
for _, v := range fileInfos {
if v.Name() == "master" {
if v.ModTime().String() == fileDate {
return version, nil
} else {
fileDate = v.ModTime().String()
break
}
}
}
content, err := ioutil.ReadFile(pwd + "/.git/refs/heads/master")
if err != nil {
return "", err
}
// Convert to full length
temp := string(content)
version = strings.ReplaceAll(temp, "\n", "")
return version, nil
}

33
util/sysytem_test.go Normal file
View File

@@ -0,0 +1,33 @@
// 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 (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetCpuUsage(t *testing.T) {
usage, err := GetCpuUsage()
assert.Nil(t, err)
t.Log(usage)
}
func TestGetMemoryUsage(t *testing.T) {
used, total, err := GetMemoryUsage()
assert.Nil(t, err)
t.Log(used, total)
}

View File

@@ -19,7 +19,7 @@
name="description"
content="Casdoor - An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS"
/>
<link rel="apple-touch-icon" href="https://cdn.casdoor.com/static/favicon.png" />
<link rel="apple-touch-icon" href="https://cdn.casbin.org/img/favicon.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@@ -95,6 +95,7 @@ class AccountTable extends React.Component {
{name: "Is forbidden", displayName: i18next.t("user:Is forbidden")},
{name: "Is deleted", displayName: i18next.t("user:Is deleted")},
{name: "WebAuthn credentials", displayName: i18next.t("user:WebAuthn credentials")},
{name: "Managed accounts", displayName: i18next.t("user:Managed accounts")},
];
const getItemDisplayName = (text) => {

View File

@@ -71,6 +71,7 @@ import SamlCallback from "./auth/SamlCallback";
import CasLogout from "./auth/CasLogout";
import ModelListPage from "./ModelListPage";
import ModelEditPage from "./ModelEditPage";
import SystemInfo from "./SystemInfo";
const {Header, Footer} = Layout;
@@ -148,6 +149,8 @@ class App extends Component {
this.setState({selectedMenuKey: "/login"});
} else if (uri.includes("/result")) {
this.setState({selectedMenuKey: "/result"});
} else if (uri.includes("/sysinfo")) {
this.setState({selectedMenuKey: "/sysinfo"});
} else {
this.setState({selectedMenuKey: -1});
}
@@ -478,8 +481,14 @@ class App extends Component {
</Link>
</Menu.Item>
);
res.push(
<Menu.Item key="/sysinfo">
<Link to="/sysinfo">
{i18next.t("general:SysInfo")}
</Link>
</Menu.Item>
);
}
res.push(
<Menu.Item key="/swagger">
<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>
@@ -560,6 +569,7 @@ class App extends Component {
<Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
@@ -691,6 +701,7 @@ class App extends Component {
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage account={this.state.account} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage account={this.state.account} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} {...props} />)} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo {...props} />)} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>

View File

@@ -0,0 +1,165 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Select, Table, Tooltip} from "antd";
import * as Setting from "./Setting";
import i18next from "i18next";
const {Option} = Select;
class ManagedAccountTable extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
};
}
updateTable(table) {
this.props.onUpdateTable(table);
}
updateField(table, index, key, value) {
table[index][key] = value;
this.updateTable(table);
}
addRow(table) {
const row = {application: "", username: "", password: "", signinUrl: ""};
if (table === undefined || table === null) {
table = [];
}
table = Setting.addRow(table, row);
this.updateTable(table);
}
deleteRow(table, i) {
table = Setting.deleteRow(table, i);
this.updateTable(table);
}
upRow(table, i) {
table = Setting.swapRow(table, i - 1, i);
this.updateTable(table);
}
downRow(table, i) {
table = Setting.swapRow(table, i, i + 1);
this.updateTable(table);
}
renderTable(table) {
const columns = [
{
title: i18next.t("general:Application"),
dataIndex: "application",
key: "application",
render: (text, record, index) => {
const items = this.props.applications;
const signinUrlMap = new Map();
for (const application of items) {
signinUrlMap.set(application.name, application.signinUrl);
}
return (
<Select virtual={false} style={{width: "100%"}}
value={text}
onChange={value => {
this.updateField(table, index, "application", value);
this.updateField(table, index, "signinUrl", signinUrlMap.get(value));
}} >
{
items.map((item, index) => <Option key={index} value={item.name}>{item.name}</Option>)
}
</Select>
);
},
},
{
title: i18next.t("signup:Username"),
dataIndex: "username",
key: "username",
width: "420px",
render: (text, record, index) => {
return (
<Input defaultValue={text} onChange={e => {
this.updateField(table, index, "username", e.target.value);
}} />
);
},
},
{
title: i18next.t("general:Password"),
dataIndex: "password",
key: "password",
width: "420px",
render: (text, record, index) => {
return (
<Input defaultValue={text} onChange={e => {
this.updateField(table, index, "password", e.target.value);
}} />
);
},
},
{
title: i18next.t("general:Action"),
key: "action",
width: "100px",
render: (text, record, index) => {
return (
<div>
<Tooltip placement="bottomLeft" title={i18next.t("general:Up")}>
<Button style={{marginRight: "5px"}} disabled={index === 0} icon={<UpOutlined />} size="small" onClick={() => this.upRow(table, index)} />
</Tooltip>
<Tooltip placement="topLeft" title={i18next.t("general:Down")}>
<Button style={{marginRight: "5px"}} disabled={index === table.length - 1} icon={<DownOutlined />} size="small" onClick={() => this.downRow(table, index)} />
</Tooltip>
<Tooltip placement="topLeft" title={i18next.t("general:Delete")}>
<Button icon={<DeleteOutlined />} size="small" onClick={() => this.deleteRow(table, index)} />
</Tooltip>
</div>
);
},
},
];
return (
<Table scroll={{x: "max-content"}} rowKey="name" columns={columns} dataSource={table} size="middle" bordered pagination={false}
title={() => (
<div>
{this.props.title}&nbsp;&nbsp;&nbsp;&nbsp;
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
</div>
)}
/>
);
}
render() {
return (
<div>
<Row style={{marginTop: "20px"}} >
<Col span={24}>
{
this.renderTable(this.props.table)
}
</Col>
</Row>
</div>
);
}
}
export default ManagedAccountTable;

View File

@@ -245,7 +245,8 @@ class PermissionEditPage extends React.Component {
})}>
{
[
{id: "Application", name: "Application"},
{id: "Application", name: i18next.t("general:Application")},
{id: "TreeNode", name: i18next.t("permission:TreeNode")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
@@ -273,9 +274,9 @@ class PermissionEditPage extends React.Component {
})}>
{
[
{id: "Read", name: "Read"},
{id: "Write", name: "Write"},
{id: "Admin", name: "Admin"},
{id: "Read", name: i18next.t("permission:Read")},
{id: "Write", name: i18next.t("permission:Write")},
{id: "Admin", name: i18next.t("permission:Admin")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
@@ -291,8 +292,8 @@ class PermissionEditPage extends React.Component {
})}>
{
[
{id: "Allow", name: "Allow"},
{id: "Deny", name: "Deny"},
{id: "Allow", name: i18next.t("permission:Allow")},
{id: "Deny", name: i18next.t("permission:Deny")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
@@ -358,8 +359,8 @@ class PermissionEditPage extends React.Component {
})}>
{
[
{id: "Approved", name: "Approved"},
{id: "Pending", name: "Pending"},
{id: "Approved", name: i18next.t("permission:Approved")},
{id: "Pending", name: i18next.t("permission:Pending")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
@@ -374,10 +375,10 @@ class PermissionEditPage extends React.Component {
Setting.showMessage("error", "The users and roles cannot be empty at the same time");
return;
}
if (this.state.permission.domains.length === 0) {
Setting.showMessage("error", "The domains cannot be empty");
return;
}
// if (this.state.permission.domains.length === 0) {
// Setting.showMessage("error", "The domains cannot be empty");
// return;
// }
if (this.state.permission.resources.length === 0) {
Setting.showMessage("error", "The resources cannot be empty");
return;

View File

@@ -188,7 +188,19 @@ class PermissionListPage extends BaseListPage {
sorter: true,
...this.getColumnSearchProps("actions"),
render: (text, record, index) => {
return Setting.getTags(text);
const tags = text.map((tag, i) => {
switch (tag) {
case "Read":
return i18next.t("permission:Read");
case "Write":
return i18next.t("permission:Write");
case "Admin":
return i18next.t("permission:Admin");
default:
return null;
}
});
return Setting.getTags(tags);
},
},
{
@@ -197,11 +209,21 @@ class PermissionListPage extends BaseListPage {
key: "effect",
filterMultiple: false,
filters: [
{text: "Allow", value: "Allow"},
{text: "Deny", value: "Deny"},
{text: i18next.t("permission:Allow"), value: "Allow"},
{text: i18next.t("permission:Deny"), value: "Deny"},
],
width: "120px",
sorter: true,
render: (text, record, index) => {
switch (text) {
case "Allow":
return Setting.getTag("success", i18next.t("permission:Allow"));
case "Deny":
return Setting.getTag("error", i18next.t("permission:Deny"));
default:
return null;
}
},
},
{
title: i18next.t("general:Is enabled"),
@@ -248,11 +270,21 @@ class PermissionListPage extends BaseListPage {
key: "state",
filterMultiple: false,
filters: [
{text: "Approved", value: "Approved"},
{text: "Pending", value: "Pending"},
{text: i18next.t("permission:Approved"), value: "Approved"},
{text: i18next.t("permission:Pending"), value: "Pending"},
],
width: "120px",
sorter: true,
render: (text, record, index) => {
switch (text) {
case "Approved":
return Setting.getTag("success", i18next.t("permission:Approved"));
case "Pending":
return Setting.getTag("error", i18next.t("permission:Pending"));
default:
return null;
}
},
},
{
title: i18next.t("general:Action"),

View File

@@ -474,7 +474,7 @@ class ProviderEditPage extends React.Component {
}} />
</Col>
</Row>
{this.state.provider.type === "AWS S3" || this.state.provider.type === "Tencent Cloud COS" ? (
{["AWS S3", "MinIO", "Tencent Cloud COS"].includes(this.state.provider.type) ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :

View File

@@ -69,6 +69,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_aws.png`,
url: "https://aws.amazon.com/s3",
},
"MinIO": {
logo: "https://min.io/resources/img/logo.svg",
url: "https://min.io/",
},
"Aliyun OSS": {
logo: `${StaticBaseUrl}/img/social_aliyun.png`,
url: "https://aliyun.com/product/oss",
@@ -204,10 +208,18 @@ export function isProviderPrompted(providerItem) {
return isProviderVisible(providerItem) && providerItem.prompted;
}
export function isSignupItemPrompted(signupItem) {
return signupItem.visible && signupItem.prompted;
}
export function getAllPromptedProviderItems(application) {
return application.providers.filter(providerItem => isProviderPrompted(providerItem));
}
export function getAllPromptedSignupItems(application) {
return application.signupItems.filter(signupItem => isSignupItemPrompted(signupItem));
}
export function getSignupItem(application, itemName) {
const signupItems = application.signupItems?.filter(signupItem => signupItem.name === itemName);
if (signupItems.length === 0) {
@@ -279,6 +291,11 @@ export function hasPromptPage(application) {
return true;
}
const signupItems = getAllPromptedSignupItems(application);
if (signupItems.length !== 0) {
return true;
}
return isAffiliationPrompted(application);
}
@@ -303,6 +320,19 @@ function isProviderItemAnswered(user, application, providerItem) {
return linkedValue !== undefined && linkedValue !== "";
}
function isSignupItemAnswered(user, signupItem) {
if (user === null) {
return false;
}
if (signupItem.name !== "Country/Region") {
return true;
}
const value = user["region"];
return value !== undefined && value !== "";
}
export function isPromptAnswered(user, application) {
if (!isAffiliationAnswered(user, application)) {
return false;
@@ -314,6 +344,13 @@ export function isPromptAnswered(user, application) {
return false;
}
}
const signupItems = getAllPromptedSignupItems(application);
for (let i = 0; i < signupItems.length; i++) {
if (!isSignupItemAnswered(user, signupItems[i])) {
return false;
}
}
return true;
}
@@ -612,6 +649,7 @@ export function getProviderTypeOptions(category) {
[
{id: "Local File System", name: "Local File System"},
{id: "AWS S3", name: "AWS S3"},
{id: "MinIO", name: "MinIO"},
{id: "Aliyun OSS", name: "Aliyun OSS"},
{id: "Tencent Cloud COS", name: "Tencent Cloud COS"},
{id: "Azure Blob", name: "Azure Blob"},
@@ -819,7 +857,10 @@ export function getTagColor(s) {
export function getTags(tags) {
const res = [];
if (!tags) {return res;}
if (!tags) {
return res;
}
tags.forEach((tag, i) => {
res.push(
<Tag color={getTagColor(tag)}>
@@ -830,6 +871,14 @@ export function getTags(tags) {
return res;
}
export function getTag(color, text) {
return (
<Tag color={color}>
{text}
</Tag>
);
}
export function getApplicationOrgName(application) {
return `${application?.organizationObj.owner}/${application?.organizationObj.name}`;
}

View File

@@ -152,7 +152,7 @@ class SignupTable extends React.Component {
return null;
}
if (record.visible) {
if (record.visible && record.name !== "Country/Region") {
return null;
}

127
web/src/SystemInfo.js Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {Card, Col, Divider, Progress, Row, Spin} from "antd";
import * as SystemBackend from "./backend/SystemInfo";
import React from "react";
import * as Setting from "./Setting";
import i18next from "i18next";
class SystemInfo extends React.Component {
constructor(props) {
super(props);
this.state = {
cpuUsage: [],
memUsed: 0,
memTotal: 0,
latestVersion: undefined,
intervalId: null,
href: "",
loading: true,
};
}
UNSAFE_componentWillMount() {
SystemBackend.getSystemInfo(this.props.account?.owner, this.props.account?.name).then(res => {
this.setState({
cpuUsage: res.cpu_usage,
memUsed: res.memory_used,
memTotal: res.memory_total,
loading: false,
});
const id = setInterval(() => {
SystemBackend.getSystemInfo(this.props.account?.owner, this.props.account?.name).then(res => {
this.setState({
cpuUsage: res.cpu_usage,
memUsed: res.memory_used,
memTotal: res.memory_total,
});
}).catch(error => {
Setting.showMessage("error", `System info failed to get: ${error}`);
});
}, 1000 * 3);
this.setState({intervalId: id});
}).catch(error => {
Setting.showMessage("error", `System info failed to get: ${error}`);
});
SystemBackend.getGitHubLatestReleaseVersion().then(res => {
const href = res && res.length >= 8 ? `https://github.com/casdoor/casdoor/commit/${res}` : "";
const versionText = res && res.length >= 8 ? res.substring(0, 8) : i18next.t("system:Unknown Version");
this.setState({latestVersion: versionText, href: href});
}).catch(err => {
Setting.showMessage("error", `get latest commit version failed: ${err}`);
});
}
componentWillUnmount() {
if (this.state.intervalId !== null) {
clearInterval(this.state.intervalId);
}
}
render() {
const CPUInfo = this.state.cpuUsage?.length > 0 ?
this.state.cpuUsage.map((usage, i) => {
return (
<Progress key={i} percent={Number(usage.toFixed(1))} />
);
}) : i18next.t("system:Get CPU Usage Failed");
const MemInfo = (
this.state.memUsed && this.state.memTotal && this.state.memTotal !== 0 ?
<div>
{Setting.getFriendlyFileSize(this.state.memUsed)} / {Setting.getFriendlyFileSize(this.state.memTotal)}
<br /> <br />
<Progress type="circle" percent={Number((Number(this.state.memUsed) / Number(this.state.memTotal) * 100).toFixed(2))} />
</div> : i18next.t("system:Get Memory Usage Failed")
);
return (
<Row>
<Col span={6}></Col>
<Col span={12}>
<Row gutter={[10, 10]}>
<Col span={12}>
<Card title={i18next.t("system:CPU Usage")} bordered={true} style={{textAlign: "center"}}>
{this.state.loading ? <Spin size="large" /> : CPUInfo}
</Card>
</Col>
<Col span={12}>
<Card title={i18next.t("system:Memory Usage")} bordered={true} style={{textAlign: "center"}}>
{this.state.loading ? <Spin size="large" /> : MemInfo}
</Card>
</Col>
</Row>
<Divider />
<Card title={i18next.t("system:About Casdoor")} bordered={true} style={{textAlign: "center"}}>
<div>{i18next.t("system:An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS")}</div>
GitHub: <a href="https://github.com/casdoor/casdoor">casdoor</a>
<br />
{i18next.t("system:Version")}: <a href={this.state.href}>{this.state.latestVersion}</a>
<br />
{i18next.t("system:Official Website")}: <a href="https://casdoor.org/">casdoor.org</a>
<br />
{i18next.t("system:Community")}: <a href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">contact us</a>
</Card>
</Col>
<Col span={6}></Col>
</Row>
);
}
}
export default SystemInfo;

View File

@@ -28,6 +28,7 @@ import OAuthWidget from "./common/OAuthWidget";
import SamlWidget from "./common/SamlWidget";
import SelectRegionBox from "./SelectRegionBox";
import WebAuthnCredentialTable from "./WebauthnCredentialTable";
import ManagedAccountTable from "./ManagedAccountTable";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
@@ -543,7 +544,7 @@ class UserEditPage extends React.Component {
</Col>
</Row>
);
} else if(accountItem.name === "WebAuthn credentials") {
} else if (accountItem.name === "WebAuthn credentials") {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
@@ -554,6 +555,22 @@ class UserEditPage extends React.Component {
</Col>
</Row>
);
} else if (accountItem.name === "Managed accounts") {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("user:Managed accounts"), i18next.t("user:Managed accounts"))} :
</Col>
<Col span={22} >
<ManagedAccountTable
title={i18next.t("user:Managed accounts")}
table={this.state.user.managedAccounts ?? []}
onUpdateTable={(table) => {this.updateUserField("managedAccounts", table);}}
applications={this.state.applications}
/>
</Col>
</Row>
);
}
}

View File

@@ -21,6 +21,7 @@ import * as Setting from "../Setting";
import i18next from "i18next";
import AffiliationSelect from "../common/AffiliationSelect";
import OAuthWidget from "../common/OAuthWidget";
import SelectRegionBox from "../SelectRegionBox";
class PromptPage extends React.Component {
constructor(props) {
@@ -90,6 +91,16 @@ class PromptPage extends React.Component {
this.submitUserEdit(false);
}
updateUserFieldWithoutSubmit(key, value) {
value = this.parseUserField(key, value);
const user = this.state.user;
user[key] = value;
this.setState({
user: user,
});
}
renderAffiliation(application) {
if (!Setting.isAffiliationPrompted(application)) {
return null;
@@ -120,6 +131,31 @@ class PromptPage extends React.Component {
application?.providers.filter(providerItem => Setting.isProviderPrompted(providerItem)).map((providerItem, index) => <OAuthWidget key={providerItem.name} labelSpan={6} user={this.state.user} application={application} providerItem={providerItem} account={this.props.account} onUnlinked={() => {return this.unlinked();}} />)
)
}
{
(application === null || this.state.user === null) ? null : (
application?.signupItems.filter(signupItem => Setting.isSignupItemPrompted(signupItem)).map((signupItem, index) => {
if (signupItem.name !== "Country/Region") {
return null;
}
return (
<Row key={signupItem.name} style={{marginTop: "20px", justifyContent: "space-between"}} >
<Col style={{marginTop: "5px"}} >
<span style={{marginLeft: "5px"}}>
{
i18next.t("user:Country/Region")
}:
</span>
</Col>
<Col >
<SelectRegionBox defaultValue={this.state.user.region} onChange={(value) => {
this.updateUserFieldWithoutSubmit("region", value);
}} />
</Col>
</Row>
);
})
)
}
</div>
</div>
);
@@ -151,7 +187,7 @@ class PromptPage extends React.Component {
if (redirectUrl === "") {
redirectUrl = res.data2;
}
if (redirectUrl !== "") {
if (redirectUrl !== "" && redirectUrl !== null) {
Setting.goToLink(redirectUrl);
} else {
Setting.goToLogin(this, this.getApplicationObj());

View 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.
import * as Setting from "../Setting";
export function getSystemInfo(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-system-info?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}
export function getGitHubLatestReleaseVersion() {
return fetch(`${Setting.ServerUrl}/api/get-release`, {
method: "GET",
credentials: "include",
}).then(res => res.json());
}

View File

@@ -13,7 +13,7 @@ code {
}
.logo {
background: url("https://cdn.casdoor.com/logo/casdoor-logo_1185x256.png");
background: url("https://cdn.casbin.org/img/casdoor-logo_1185x256.png");
background-size: 130px, 27px;
width: 130px;
height: 27px;

View File

@@ -198,6 +198,7 @@
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "SysInfo",
"Timestamp": "Zeitstempel",
"Tokens": "Token",
"URL": "URL",
@@ -345,14 +346,20 @@
"permission": {
"Actions": "Aktionen",
"Actions - Tooltip": "Aktionen - Tooltip",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Deny": "Deny",
"Edit Permission": "Berechtigung bearbeiten",
"Effect": "Effekt",
"Effect - Tooltip": "Effekt - Tooltip",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Ressourcentyp",
"Resource type - Tooltip": "Ressourcentyp - Tooltip",
"Resources": "Ressourcen",
@@ -360,7 +367,9 @@
"State": "State",
"State - Tooltip": "State - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip"
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
@@ -593,6 +602,18 @@
"Table primary key": "Primärschlüssel der Tabelle",
"Table primary key - Tooltip": "Primärschlüssel der Tabelle - Tooltip"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"token": {
"Access token": "Zugangs-Token",
"Authorization code": "Autorisierungscode",
@@ -636,6 +657,7 @@
"Link": "Link",
"Location": "Standort",
"Location - Tooltip": "Standort - Tooltip",
"Managed accounts": "Managed accounts",
"Modify password...": "Passwort ändern...",
"New Email": "Neue E-Mail",
"New Password": "Neues Passwort",

View File

@@ -198,6 +198,7 @@
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "SysInfo",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
@@ -345,14 +346,20 @@
"permission": {
"Actions": "Actions",
"Actions - Tooltip": "Actions - Tooltip",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Deny": "Deny",
"Edit Permission": "Edit Permission",
"Effect": "Effect",
"Effect - Tooltip": "Effect - Tooltip",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Resource type",
"Resource type - Tooltip": "Resource type - Tooltip",
"Resources": "Resources",
@@ -360,7 +367,9 @@
"State": "State",
"State - Tooltip": "State - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip"
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
@@ -593,6 +602,18 @@
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key - Tooltip"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"token": {
"Access token": "Access token",
"Authorization code": "Authorization code",
@@ -636,6 +657,7 @@
"Link": "Link",
"Location": "Location",
"Location - Tooltip": "Location - Tooltip",
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"New Email": "New Email",
"New Password": "New Password",

View File

@@ -198,6 +198,7 @@
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Synchronisateurs",
"SysInfo": "SysInfo",
"Timestamp": "Horodatage",
"Tokens": "Jetons",
"URL": "URL",
@@ -345,14 +346,20 @@
"permission": {
"Actions": "Actions",
"Actions - Tooltip": "Actions - Info-bulle",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Deny": "Deny",
"Edit Permission": "Autorisation d'édition",
"Effect": "Effet",
"Effect - Tooltip": "Effet - Infobulle",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Type de ressource",
"Resource type - Tooltip": "Type de ressource - infobulle",
"Resources": "Ressource",
@@ -360,7 +367,9 @@
"State": "State",
"State - Tooltip": "State - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip"
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
@@ -593,6 +602,18 @@
"Table primary key": "Clé primaire de la table",
"Table primary key - Tooltip": "Clé primaire du tableau - infobulle"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"token": {
"Access token": "Jeton d'accès",
"Authorization code": "Code d'autorisation",
@@ -636,6 +657,7 @@
"Link": "Lier",
"Location": "Localisation",
"Location - Tooltip": "Localisation - Infobulle",
"Managed accounts": "Managed accounts",
"Modify password...": "Modifier le mot de passe...",
"New Email": "Nouvel e-mail",
"New Password": "Nouveau mot de passe",

View File

@@ -198,6 +198,7 @@
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "システム情報",
"Timestamp": "タイムスタンプ",
"Tokens": "トークン",
"URL": "URL",
@@ -345,14 +346,20 @@
"permission": {
"Actions": "アクション",
"Actions - Tooltip": "アクション → ツールチップ",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Deny": "Deny",
"Edit Permission": "権限を編集",
"Effect": "効果",
"Effect - Tooltip": "エフェクト - ツールチップ",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "リソースタイプ",
"Resource type - Tooltip": "リソースタイプ - ツールチップ",
"Resources": "リソース",
@@ -360,7 +367,9 @@
"State": "State",
"State - Tooltip": "State - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip"
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
@@ -593,6 +602,18 @@
"Table primary key": "テーブルのプライマリキー",
"Table primary key - Tooltip": "テーブルのプライマリキー - ツールチップ"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"token": {
"Access token": "アクセストークン",
"Authorization code": "認証コード",
@@ -636,6 +657,7 @@
"Link": "リンク",
"Location": "場所",
"Location - Tooltip": "場所 → ツールチップ",
"Managed accounts": "Managed accounts",
"Modify password...": "パスワードを変更...",
"New Email": "新しいメール",
"New Password": "新しいパスワード",

View File

@@ -198,6 +198,7 @@
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "SysInfo",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
@@ -345,14 +346,20 @@
"permission": {
"Actions": "Actions",
"Actions - Tooltip": "Actions - Tooltip",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Deny": "Deny",
"Edit Permission": "Edit Permission",
"Effect": "Effect",
"Effect - Tooltip": "Effect - Tooltip",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Resource type",
"Resource type - Tooltip": "Resource type - Tooltip",
"Resources": "Resources",
@@ -360,7 +367,9 @@
"State": "State",
"State - Tooltip": "State - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip"
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
@@ -593,6 +602,18 @@
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key - Tooltip"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"token": {
"Access token": "Access token",
"Authorization code": "Authorization code",
@@ -636,6 +657,7 @@
"Link": "Link",
"Location": "Location",
"Location - Tooltip": "Location - Tooltip",
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"New Email": "New Email",
"New Password": "New Password",

View File

@@ -198,6 +198,7 @@
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Синхронизаторы",
"SysInfo": "Информация о системе",
"Timestamp": "Отметка времени",
"Tokens": "Жетоны",
"URL": "URL",
@@ -345,14 +346,20 @@
"permission": {
"Actions": "Действия",
"Actions - Tooltip": "Действия - Подсказка",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Deny": "Deny",
"Edit Permission": "Изменить права доступа",
"Effect": "Эффект",
"Effect - Tooltip": "Эффект - Подсказка",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Тип ресурса",
"Resource type - Tooltip": "Тип ресурса - Подсказка",
"Resources": "Ресурсы",
@@ -360,7 +367,9 @@
"State": "State",
"State - Tooltip": "State - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip"
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
@@ -593,6 +602,18 @@
"Table primary key": "Основной ключ таблицы",
"Table primary key - Tooltip": "Основная таблица - Подсказка"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"token": {
"Access token": "Маркер доступа",
"Authorization code": "Код авторизации",
@@ -636,6 +657,7 @@
"Link": "Ссылка",
"Location": "Местоположение",
"Location - Tooltip": "Расположение - Подсказка",
"Managed accounts": "Managed accounts",
"Modify password...": "Изменить пароль...",
"New Email": "Новое письмо",
"New Password": "Новый пароль",

View File

@@ -198,6 +198,7 @@
"Swagger": "API文档",
"Sync": "同步",
"Syncers": "同步器",
"SysInfo": "系统信息",
"Timestamp": "时间戳",
"Tokens": "令牌",
"URL": "链接",
@@ -345,14 +346,20 @@
"permission": {
"Actions": "动作",
"Actions - Tooltip": "授权的动作",
"Admin": "管理员权限",
"Allow": "允许",
"Approve time": "审批时间",
"Approve time - Tooltip": "该授权被审批通过的时间",
"Approved": "审批通过",
"Approver": "审批者",
"Approver - Tooltip": "审批通过该授权的人",
"Deny": "拒绝",
"Edit Permission": "编辑权限",
"Effect": "效果",
"Effect - Tooltip": "允许还是拒绝",
"New Permission": "添加权限",
"Pending": "待审批",
"Read": "读权限",
"Resource type": "资源类型",
"Resource type - Tooltip": "授权资源的类型",
"Resources": "资源",
@@ -360,7 +367,9 @@
"State": "审批状态",
"State - Tooltip": "该授权现在的状态",
"Submitter": "申请者",
"Submitter - Tooltip": "申请该授权的人"
"Submitter - Tooltip": "申请该授权的人",
"TreeNode": "树节点",
"Write": "写权限"
},
"product": {
"Alipay": "支付宝",
@@ -593,6 +602,18 @@
"Table primary key": "表主键",
"Table primary key - Tooltip": "表主键如id"
},
"system": {
"About Casdoor": "关于 Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "一个支持 OAuth 2.0、OIDC、SAML 和 CAS 的 Web UI 的身份和访问管理 (IAM)/单点登录 (SSO) 平台",
"CPU Usage": "CPU使用率",
"Community": "社区",
"Get CPU Usage Failed": "获取CPU使用率失败",
"Get Memory Usage Failed": "获取内存使用率失败",
"Memory Usage": "内存使用率",
"Official Website": "官方网站",
"Unknown Version": "未知版本",
"Version": "版本"
},
"token": {
"Access token": "访问令牌",
"Authorization code": "授权码",
@@ -636,6 +657,7 @@
"Link": "绑定",
"Location": "城市",
"Location - Tooltip": "居住地址所在的城市",
"Managed accounts": "托管账户",
"Modify password...": "编辑密码...",
"New Email": "新邮箱",
"New Password": "新密码",