Compare commits

...

59 Commits

Author SHA1 Message Date
739a9bcd0d feat: add CasvisorUrl 2023-08-25 11:56:12 +08:00
fb0949b9ed Fix docker cannot get version bug 2023-08-25 11:49:47 +08:00
27ed901167 Restrict sysinfo page to global admin 2023-08-25 11:20:11 +08:00
ceab662b88 Remove dup swagger page 2023-08-25 11:09:59 +08:00
05b2f00057 feat: support Pricings flow (#2250)
* feat: fix price display

* feat: support subscription

* feat: fix select-plan-> signup -> buy-plan -> login flow

* feat: support paid-user to login and jump to the pricing page

* feat: support more subscription state

* feat: add payment providers for plan

* feat: format code

* feat: gofumpt

* feat: redirect to buy-plan-result page when user have pending subscription

* feat: response err when pricing don't exit

* Update PricingListPage.js

* Update ProductBuyPage.js

* Update LoginPage.js

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-24 23:20:50 +08:00
8073dfa88c Remove tmpFiles folder usage 2023-08-24 22:03:36 +08:00
1eeeb64a0c Add checkModel() for UserGroupEnforcer 2023-08-24 18:22:23 +08:00
f5e0461cae feat: add invitation code for signup feature (#2249)
* feat: add invitation code for signup feature

* feat: add invitation code for signup feature
2023-08-24 13:42:17 +08:00
a0c5eb241f feat: add fields to syncer (PreferredMfaType, TotpSecret, SignupApplication) #2239 (#2245) 2023-08-23 21:40:00 +08:00
4d8edcc446 fix: dropped controllers err (#2244)
Signed-off-by: Lars Lehtonen <lars.lehtonen@gmail.com>
2023-08-23 21:37:51 +08:00
2b23c04f49 fix: add SignupApplication and type for user synced from LDAP (#2240) 2023-08-21 22:52:35 +08:00
e60ee52d91 feat: replace satori/go.uuid with google/uuid (#2238) 2023-08-21 13:58:15 +08:00
c54b54ca19 fix: Adjust custom http to notification provider (#2237)
* feat: Adjust custom http to notification provider

* fix go linter

* update ProviderEditPage

* update ProviderEditPage
2023-08-20 21:04:30 +08:00
f0e097e138 feat: fix home page (#2236)
* fix: home page

* fix: home page
2023-08-20 00:58:39 +08:00
25ec1bdfa8 Fix bug in getUserOrganization() 2023-08-20 00:53:51 +08:00
ea7718d7b7 Use Casvisor for records 2023-08-20 00:44:01 +08:00
463fa8b636 Add ormer_session.go 2023-08-19 18:41:08 +08:00
11895902f4 Move getCreateDatabaseFlag() to ormer 2023-08-19 16:44:34 +08:00
15269d3315 Refactor out conf_quota.go 2023-08-19 16:39:21 +08:00
4468859795 Improve sendTest msg 2023-08-19 12:47:51 +08:00
914128a78a fix: Support Telegram Notification provider (#2225)
* fear: support telegram provider

* fix: fix telegram logo

* fix: fix telegram bot package

* Update telegram.go

* Update notification.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-19 12:33:00 +08:00
e5a189e0f4 fix: remove isGlobalAdmin field in user (#2235)
* refactor: remove isGlobalAdmin field in user

* fix: upload xlsx

* fix: remove field in account table
2023-08-19 12:23:15 +08:00
a07216d0e1 Improve contentType parsing in downloadImage() 2023-08-19 02:35:45 +08:00
fec54944dd feat: fix CAS login bug (#2230)
* fix: cas login

* fix: cas login

* feat: rollback get-default-app change

* fix : move cas restrict logic to GetApplicationLogin()

* fix: format code

* fix: fix getOAuthGetParameters for cas

* fix: fix getOAuthGetParameters for cas

* fix: cas login
2023-08-19 01:15:41 +08:00
a2db61cc1a chore: Revert "feat: restrict redirectUrls for CAS login" (#2234)
This reverts commit b7a37126ad.
2023-08-19 00:30:35 +08:00
134541acde chore: put some dev dependency package to right place (#2232) 2023-08-18 22:17:16 +08:00
59fca0342e chore: fix yarn build warning (#2231) 2023-08-18 21:25:57 +08:00
abfc464155 Remove isEnabled for model, adapter and enforcer, improve UI 2023-08-18 19:22:47 +08:00
a41f6880a2 feat: move policy table from adapter to enforcer and improve it (#2228)
* feat: improve policiy table

* feat: add connection test in AdapterEditPage.js

* feat: update button style
2023-08-18 19:00:21 +08:00
d12117324c feat: support admin to enable MFA for other users (#2221)
* feat: support admin enable user sms and email mfa

* chore: update ci

* chore: update ci
2023-08-17 17:19:24 +08:00
1a6c9fbf69 Fix typo in README 2023-08-17 14:47:09 +08:00
dd60d79af9 Fix typo in README 2023-08-17 14:46:10 +08:00
73d314c7fe Add MfaTotpPeriodInSeconds param 2023-08-16 21:48:54 +08:00
27959e0f6f fix: fix crash in UserEditPage.js 2023-08-16 15:57:48 +08:00
47f40c5b24 feat: support 3 more UI languages (#2218)
Signed-off-by: baihhh <2542274498@qq.com>
2023-08-16 15:54:34 +08:00
2ff9020884 feat: support Stripe payment provider (#2204)
* feat: add stripe payment provider

* feat: support stripe payment

* feat: delete todo comment

* feat: remove description struct

* feat: change outOrderId->orderId
2023-08-15 00:16:30 +08:00
abaf4ca8d9 Make GetDashboard() faster 2023-08-14 15:43:09 +08:00
8ff0cfd6ec feat: support dashboard in homepage (#2207)
* feat: support dashboard

* feat: support dashboard
2023-08-14 15:31:29 +08:00
7a2a40edcc Improve table columns 2023-08-14 12:19:02 +08:00
b7a001ea39 Fix property empty issue 2023-08-14 12:09:50 +08:00
891e8e21d8 feat: support Web3-Onboard provider (#2209)
* feat: add Web3-Onboard idp

* feat: update Web3-Onboard logo

* feat: update package.json

* feat: remove unused package

* feat: add yarn build param --max_old_space_size=4096

* feat: remove log

* feat: add Wallet configure

* feat: remove hardware wallets
2023-08-13 23:58:57 +08:00
80b0d26813 fix: synchronize update the syncers (#2201)
Signed-off-by: baihhh <2542274498@qq.com>
2023-08-13 22:30:57 +08:00
db4ac60bb6 feat: fix LDAP mobile field incorrect mapped (#2206) 2023-08-12 13:45:26 +08:00
33a922f026 Add custom HTTP SMS provider 2023-08-12 12:52:53 +08:00
9f65053d04 Improve i18n 2023-08-12 02:44:38 +08:00
be969e5efa Fix typo 2023-08-11 22:18:35 +08:00
9156bd426b ci: Show provider.displayName in signin button 2023-08-11 16:29:52 +08:00
fe4a4328aa feat: refactor code in InitApi() 2023-08-11 16:17:29 +08:00
9899022bcd fix: check enforcer should not be nil (#2199)
* fix: check enforcer should not be nil

* fix: check enforcer should not be nil

* Update user.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-11 12:31:49 +08:00
1a9d02be46 feat: use the casbin model to store relationships between users and groups (#2178)
* fix:reslove conflict

* fix: remove interface
2023-08-11 10:59:18 +08:00
eafaa135b4 Change builtInAvailableField back to 5 2023-08-11 02:45:11 +08:00
6746551447 Improve error message in InitEnforcer() 2023-08-11 02:36:29 +08:00
3cb46c3628 Add isKey to syncer's table 2023-08-09 00:33:04 +08:00
558bcf95d6 feat: save policy in adapter edit page (#2190)
* fix: save policy in adapter

* fix: disable edit for builtin adapter
2023-08-09 00:12:53 +08:00
bb937c30c1 Fix empty cert in getPaymentProvider() 2023-08-08 22:37:48 +08:00
8dfdf7f767 ci: add GoogleCloud and QiNiu in Storage (#2188)
* feat: add GoogleCloud and QiNiu in Storage

Signed-off-by: baihhh <2542274498@qq.com>

* Update qiniu_cloud.go

* Update storage.go

---------

Signed-off-by: baihhh <2542274498@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-08 22:34:55 +08:00
62b2082e82 Add getUserOrganization() to user edit page 2023-08-08 21:58:27 +08:00
a1806439f8 Add UserPrincipalName and MemberOf to get-ldap-users API 2023-08-08 20:18:47 +08:00
01e58158b7 feat: Remove useless code 2023-08-08 19:16:55 +08:00
186 changed files with 12526 additions and 3035 deletions

View File

@ -110,7 +110,7 @@ jobs:
with: with:
start: yarn start start: yarn start
wait-on: 'http://localhost:7001' wait-on: 'http://localhost:7001'
wait-on-timeout: 180 wait-on-timeout: 210
working-directory: ./web working-directory: ./web
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3

View File

@ -11,7 +11,7 @@
<img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square"> <img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/releases/latest"> <a href="https://github.com/casdoor/casdoor/releases/latest">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casbin/casdoor.svg"> <img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg">
</a> </a>
<a href="https://hub.docker.com/repository/docker/casbin/casdoor"> <a href="https://hub.docker.com/repository/docker/casbin/casdoor">
<img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen"> <img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen">
@ -23,16 +23,16 @@
<img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square"> <img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/blob/master/LICENSE"> <a href="https://github.com/casdoor/casdoor/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/casbin/casdoor?style=flat-square" alt="license"> <img src="https://img.shields.io/github/license/casdoor/casdoor?style=flat-square" alt="license">
</a> </a>
<a href="https://github.com/casdoor/casdoor/issues"> <a href="https://github.com/casdoor/casdoor/issues">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/casbin/casdoor?style=flat-square"> <img alt="GitHub issues" src="https://img.shields.io/github/issues/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="#"> <a href="#">
<img alt="GitHub stars" src="https://img.shields.io/github/stars/casbin/casdoor?style=flat-square"> <img alt="GitHub stars" src="https://img.shields.io/github/stars/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/network"> <a href="https://github.com/casdoor/casdoor/network">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/casbin/casdoor?style=flat-square"> <img alt="GitHub forks" src="https://img.shields.io/github/forks/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://crowdin.com/project/casdoor-site"> <a href="https://crowdin.com/project/casdoor-site">
<img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg"> <img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg">

View File

@ -27,13 +27,7 @@ import (
var Enforcer *casbin.Enforcer var Enforcer *casbin.Enforcer
func InitApi() { func InitApi() {
var err error e, err := object.GetInitializedEnforcer(util.GetId("built-in", "api-enforcer-built-in"))
e, err := object.GetEnforcer(util.GetId("built-in", "api-enforcer-built-in"))
if err != nil {
panic(err)
}
err = e.InitEnforcer()
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -93,6 +87,7 @@ p, *, *, GET, /api/get-prometheus-info, *, *
p, *, *, *, /api/metrics, *, * p, *, *, *, /api/metrics, *, *
p, *, *, GET, /api/get-pricing, *, * p, *, *, GET, /api/get-pricing, *, *
p, *, *, GET, /api/get-plan, *, * p, *, *, GET, /api/get-plan, *, *
p, *, *, GET, /api/get-subscription, *, *
p, *, *, GET, /api/get-organization-names, *, * p, *, *, GET, /api/get-organization-names, *, *
` `

View File

@ -15,7 +15,7 @@
package conf package conf
import ( import (
"encoding/json" "fmt"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
@ -24,15 +24,6 @@ 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"}
@ -44,17 +35,6 @@ 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 {
@ -67,7 +47,7 @@ func GetConfigString(key string) string {
if key == "staticBaseUrl" { if key == "staticBaseUrl" {
res = "https://cdn.casbin.org" res = "https://cdn.casbin.org"
} else if key == "logConfig" { } else if key == "logConfig" {
res = "{\"filename\": \"logs/casdoor.log\", \"maxdays\":99999, \"perm\":\"0770\"}" res = fmt.Sprintf("{\"filename\": \"logs/%s.log\", \"maxdays\":99999, \"perm\":\"0770\"}", beego.AppConfig.String("appname"))
} }
} }
@ -129,10 +109,6 @@ func GetConfigBatchSize() int {
return res return res
} }
func GetConfigQuota() *Quota {
return quota
}
func GetConfigRealDataSourceName(driverName string) string { func GetConfigRealDataSourceName(driverName string) string {
var dataSourceName string var dataSourceName string
if driverName != "mysql" { if driverName != "mysql" {

48
conf/conf_quota.go Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2023 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 conf
import (
"encoding/json"
"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() {
initQuota()
}
func initQuota() {
res := beego.AppConfig.String("quota")
if res != "" {
err := json.Unmarshal([]byte(res), quota)
if err != nil {
panic(err)
}
}
}
func GetConfigQuota() *Quota {
return quota
}

View File

@ -153,12 +153,22 @@ func (c *ApiController) Signup() {
return return
} }
userType := "normal-user"
if authForm.Plan != "" && authForm.Pricing != "" {
err = object.CheckPricingAndPlan(authForm.Organization, authForm.Pricing, authForm.Plan)
if err != nil {
c.ResponseError(err.Error())
return
}
userType = "paid-user"
}
user := &object.User{ user := &object.User{
Owner: authForm.Organization, Owner: authForm.Organization,
Name: username, Name: username,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Id: id, Id: id,
Type: "normal-user", Type: userType,
Password: authForm.Password, Password: authForm.Password,
DisplayName: authForm.Name, DisplayName: authForm.Name,
Avatar: organization.DefaultAvatar, Avatar: organization.DefaultAvatar,
@ -171,7 +181,6 @@ func (c *ApiController) Signup() {
Region: authForm.Region, Region: authForm.Region,
Score: initScore, Score: initScore,
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false, IsDeleted: false,
SignupApplication: application.Name, SignupApplication: application.Name,
@ -211,7 +220,7 @@ func (c *ApiController) Signup() {
return return
} }
if application.HasPromptPage() { if application.HasPromptPage() && user.Type == "normal-user" {
// The prompt page needs the user to be signed in // The prompt page needs the user to be signed in
c.SetSessionUsername(user.GetId()) c.SetSessionUsername(user.GetId())
} }
@ -228,15 +237,6 @@ func (c *ApiController) Signup() {
return return
} }
isSignupFromPricing := authForm.Plan != "" && authForm.Pricing != ""
if isSignupFromPricing {
_, err = object.Subscribe(organization.Name, user.Name, authForm.Plan, authForm.Pricing)
if err != nil {
c.ResponseError(err.Error())
return
}
}
record := object.NewRecord(c.Ctx) record := object.NewRecord(c.Ctx)
record.Organization = application.Organization record.Organization = application.Organization
record.User = user.Name record.User = user.Name

View File

@ -20,7 +20,6 @@ import (
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
) )
// GetAdapters // GetAdapters
@ -144,92 +143,3 @@ func (c *ApiController) DeleteAdapter() {
c.Data["json"] = wrapActionResponse(object.DeleteAdapter(&adapter)) c.Data["json"] = wrapActionResponse(object.DeleteAdapter(&adapter))
c.ServeJSON() c.ServeJSON()
} }
func (c *ApiController) GetPolicies() {
id := c.Input().Get("id")
adapter, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
policies, err := object.GetPolicies(adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(policies)
}
func (c *ApiController) UpdatePolicy() {
id := c.Input().Get("id")
adapter, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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()
}

View File

@ -70,7 +70,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
} }
// check user's tag // check user's tag
if !user.IsGlobalAdmin && !user.IsAdmin && len(application.Tags) > 0 { if !user.IsGlobalAdmin() && !user.IsAdmin && len(application.Tags) > 0 {
// only users with the tag that is listed in the application tags can login // only users with the tag that is listed in the application tags can login
if !util.InSlice(application.Tags, user.Tag) { if !util.InSlice(application.Tags, user.Tag) {
c.ResponseError(fmt.Sprintf(c.T("auth:User's tag: %s is not listed in the application's tags"), user.Tag)) c.ResponseError(fmt.Sprintf(c.T("auth:User's tag: %s is not listed in the application's tags"), user.Tag))
@ -78,6 +78,46 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
} }
} }
// check whether paid-user have active subscription
if user.Type == "paid-user" {
subscriptions, err := object.GetSubscriptionsByUser(user.Owner, user.Name)
if err != nil {
c.ResponseError(err.Error())
return
}
existActiveSubscription := false
for _, subscription := range subscriptions {
if subscription.State == object.SubStateActive {
existActiveSubscription = true
break
}
}
if !existActiveSubscription {
// check pending subscription
for _, sub := range subscriptions {
if sub.State == object.SubStatePending {
c.ResponseOk("BuyPlanResult", sub)
return
}
}
// paid-user does not have active or pending subscription, find the default pricing of application
pricing, err := object.GetApplicationDefaultPricing(application.Organization, application.Name)
if err != nil {
c.ResponseError(err.Error())
return
}
if pricing == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"), user.Name, application.Name))
return
} else {
// let the paid-user select plan
c.ResponseOk("SelectPlan", pricing)
return
}
}
}
if form.Type == ResponseTypeLogin { if form.Type == ResponseTypeLogin {
c.SetSessionUsername(userId) c.SetSessionUsername(userId)
util.LogInfo(c.Ctx, "API: [%s] signed in", userId) util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
@ -187,11 +227,34 @@ func (c *ApiController) GetApplicationLogin() {
redirectUri := c.Input().Get("redirectUri") redirectUri := c.Input().Get("redirectUri")
scope := c.Input().Get("scope") scope := c.Input().Get("scope")
state := c.Input().Get("state") state := c.Input().Get("state")
id := c.Input().Get("id")
loginType := c.Input().Get("type")
msg, application, err := object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, c.GetAcceptLanguage()) var application *object.Application
if err != nil { var msg string
c.ResponseError(err.Error()) var err error
return if loginType == "code" {
msg, application, err = object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
} else if loginType == "cas" {
application, err = object.GetApplication(id)
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), id))
return
}
err = object.CheckCasLogin(application, c.GetAcceptLanguage(), redirectUri)
if err != nil {
c.ResponseError(err.Error())
return
}
} }
application = object.GetMaskedApplication(application, "") application = object.GetMaskedApplication(application, "")
@ -566,7 +629,6 @@ func (c *ApiController) Login() {
Region: userInfo.CountryCode, Region: userInfo.CountryCode,
Score: initScore, Score: initScore,
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false, IsDeleted: false,
SignupApplication: application.Name, SignupApplication: application.Name,

View File

@ -79,7 +79,7 @@ func (c *ApiController) isGlobalAdmin() (bool, *object.User) {
return false, nil return false, nil
} }
return user.Owner == "built-in" || user.IsGlobalAdmin, user return user.IsGlobalAdmin(), user
} }
func (c *ApiController) getCurrentUser() *object.User { func (c *ApiController) getCurrentUser() *object.User {

View File

@ -45,13 +45,7 @@ func (c *ApiController) Enforce() {
} }
if enforcerId != "" { if enforcerId != "" {
enforcer, err := object.GetEnforcer(enforcerId) enforcer, err := object.GetInitializedEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return
}
err = enforcer.InitEnforcer()
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -155,13 +149,7 @@ func (c *ApiController) BatchEnforce() {
} }
if enforcerId != "" { if enforcerId != "" {
enforcer, err := object.GetEnforcer(enforcerId) enforcer, err := object.GetInitializedEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return
}
err = enforcer.InitEnforcer()
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -20,6 +20,7 @@ import (
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
) )
// GetEnforcers // GetEnforcers
@ -74,6 +75,7 @@ func (c *ApiController) GetEnforcers() {
// @router /get-enforcer [get] // @router /get-enforcer [get]
func (c *ApiController) GetEnforcer() { func (c *ApiController) GetEnforcer() {
id := c.Input().Get("id") id := c.Input().Get("id")
loadModelCfg := c.Input().Get("loadModelCfg")
enforcer, err := object.GetEnforcer(id) enforcer, err := object.GetEnforcer(id)
if err != nil { if err != nil {
@ -81,6 +83,13 @@ func (c *ApiController) GetEnforcer() {
return return
} }
if loadModelCfg == "true" {
err := enforcer.LoadModelCfg()
if err != nil {
return
}
}
c.ResponseOk(enforcer) c.ResponseOk(enforcer)
} }
@ -143,3 +152,88 @@ func (c *ApiController) DeleteEnforcer() {
c.Data["json"] = wrapActionResponse(object.DeleteEnforcer(&enforcer)) c.Data["json"] = wrapActionResponse(object.DeleteEnforcer(&enforcer))
c.ServeJSON() c.ServeJSON()
} }
func (c *ApiController) GetPolicies() {
id := c.Input().Get("id")
adapterId := c.Input().Get("adapterId")
if adapterId != "" {
adapter, err := object.GetAdapter(adapterId)
if err != nil {
c.ResponseError(err.Error())
return
}
err = adapter.InitAdapter()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
return
}
policies, err := object.GetPolicies(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(policies)
}
func (c *ApiController) UpdatePolicy() {
id := c.Input().Get("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(id, util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) AddPolicy() {
id := c.Input().Get("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(id, util.CasbinToSlice(policy))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) RemovePolicy() {
id := c.Input().Get("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(id, util.CasbinToSlice(policy))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}

View File

@ -0,0 +1,35 @@
// Copyright 2023 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"
// GetDashboard
// @Title GetDashboard
// @Tag GetDashboard API
// @Description get information of dashboard
// @Success 200 {object} controllers.Response The Response object
// @router /get-dashboard [get]
func (c *ApiController) GetDashboard() {
owner := c.Input().Get("owner")
data, err := object.GetDashboard(owner)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(data)
}

View File

@ -45,13 +45,13 @@ func (c *ApiController) Unlink() {
// the user will be unlinked from the provider // the user will be unlinked from the provider
unlinkedUser := form.User unlinkedUser := form.User
if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin { if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin() {
// if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin. // if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin.
c.ResponseError(c.T("link:You are not the global admin, you can't unlink other users")) c.ResponseError(c.T("link:You are not the global admin, you can't unlink other users"))
return return
} }
if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin { if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin() {
// if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error. // if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error.
application, err := object.GetApplicationByUser(user) application, err := object.GetApplicationByUser(user)
if err != nil { if err != nil {

View File

@ -183,8 +183,6 @@ func (c *ApiController) DeleteOrganization() {
func (c *ApiController) GetDefaultApplication() { func (c *ApiController) GetDefaultApplication() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
id := c.Input().Get("id") id := c.Input().Get("id")
redirectUri := c.Input().Get("redirectUri")
typ := c.Input().Get("type")
application, err := object.GetDefaultApplication(id) application, err := object.GetDefaultApplication(id)
if err != nil { if err != nil {
@ -192,14 +190,6 @@ func (c *ApiController) GetDefaultApplication() {
return return
} }
if typ == "cas" {
err = object.CheckCasRestrict(application, c.GetAcceptLanguage(), redirectUri)
if err != nil {
c.ResponseError(err.Error())
return
}
}
maskedApplication := object.GetMaskedApplication(application, userId) maskedApplication := object.GetMaskedApplication(application, userId)
c.ResponseOk(maskedApplication) c.ResponseOk(maskedApplication)
} }

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"fmt" "fmt"
"os"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -32,16 +33,15 @@ func (c *ApiController) UploadPermissions() {
} }
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename)) fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
path := util.GetUploadXlsxPath(fileId) path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path) defer os.Remove(path)
err = saveFile(path, &file) err = saveFile(path, &file)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
affected, err := object.UploadPermissions(owner, fileId) affected, err := object.UploadPermissions(owner, path)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} }

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -82,7 +83,10 @@ func (c *ApiController) GetPlan() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if plan == nil {
c.ResponseError(fmt.Sprintf(c.T("plan:The plan: %s does not exist"), id))
return
}
if includeOption { if includeOption {
options, err := object.GetPermissionsByRole(plan.Role) options, err := object.GetPermissionsByRole(plan.Role)
if err != nil { if err != nil {
@ -117,7 +121,20 @@ func (c *ApiController) UpdatePlan() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if plan.Product != "" {
planId := util.GetId(plan.Owner, plan.Product)
product, err := object.GetProduct(planId)
if err != nil {
c.ResponseError(err.Error())
return
}
object.UpdateProductForPlan(&plan, product)
_, err = object.UpdateProduct(planId, product)
if err != nil {
c.ResponseError(err.Error())
return
}
}
c.Data["json"] = wrapActionResponse(object.UpdatePlan(id, &plan)) c.Data["json"] = wrapActionResponse(object.UpdatePlan(id, &plan))
c.ServeJSON() c.ServeJSON()
} }
@ -136,7 +153,14 @@ func (c *ApiController) AddPlan() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
// Create a related product for plan
product := object.CreateProductForPlan(&plan)
_, err = object.AddProduct(product)
if err != nil {
c.ResponseError(err.Error())
return
}
plan.Product = product.Name
c.Data["json"] = wrapActionResponse(object.AddPlan(&plan)) c.Data["json"] = wrapActionResponse(object.AddPlan(&plan))
c.ServeJSON() c.ServeJSON()
} }
@ -155,7 +179,13 @@ func (c *ApiController) DeletePlan() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if plan.Product != "" {
_, err = object.DeleteProduct(&object.Product{Owner: plan.Owner, Name: plan.Product})
if err != nil {
c.ResponseError(err.Error())
return
}
}
c.Data["json"] = wrapActionResponse(object.DeletePlan(&plan)) c.Data["json"] = wrapActionResponse(object.DeletePlan(&plan))
c.ServeJSON() c.ServeJSON()
} }

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -80,7 +81,10 @@ func (c *ApiController) GetPricing() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if pricing == nil {
c.ResponseError(fmt.Sprintf(c.T("pricing:The pricing: %s does not exist"), id))
return
}
c.ResponseOk(pricing) c.ResponseOk(pricing)
} }

View File

@ -161,10 +161,17 @@ func (c *ApiController) DeleteProduct() {
// @router /buy-product [post] // @router /buy-product [post]
func (c *ApiController) BuyProduct() { func (c *ApiController) BuyProduct() {
id := c.Input().Get("id") id := c.Input().Get("id")
providerName := c.Input().Get("providerName")
host := c.Ctx.Request.Host host := c.Ctx.Request.Host
providerName := c.Input().Get("providerName")
userId := c.GetSessionUsername() // buy `pricingName/planName` for `paidUserName`
pricingName := c.Input().Get("pricingName")
planName := c.Input().Get("planName")
paidUserName := c.Input().Get("userName")
owner, _ := util.GetOwnerAndNameFromId(id)
userId := util.GetId(owner, paidUserName)
if paidUserName == "" {
userId = c.GetSessionUsername()
}
if userId == "" { if userId == "" {
c.ResponseError(c.T("general:Please login first")) c.ResponseError(c.T("general:Please login first"))
return return
@ -175,13 +182,12 @@ func (c *ApiController) BuyProduct() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId)) c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return return
} }
payUrl, orderId, err := object.BuyProduct(id, providerName, user, host) payUrl, orderId, err := object.BuyProduct(id, user, providerName, pricingName, planName, host)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -1,121 +0,0 @@
// 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 controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetRecords
// @Title GetRecords
// @Tag Record API
// @Description get all records
// @Param pageSize query string true "The size of each page"
// @Param p query string true "The number of the page"
// @Success 200 {object} object.Record The Response object
// @router /get-records [get]
func (c *ApiController) GetRecords() {
organization, ok := c.RequireAdmin()
if !ok {
return
}
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")
organizationName := c.Input().Get("organizationName")
if limit == "" || page == "" {
records, err := object.GetRecords()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records)
} else {
limit := util.ParseInt(limit)
if c.IsGlobalAdmin() && organizationName != "" {
organization = organizationName
}
filterRecord := &object.Record{Organization: organization}
count, err := object.GetRecordCount(field, value, filterRecord)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
records, err := object.GetPaginationRecords(paginator.Offset(), limit, field, value, sortField, sortOrder, filterRecord)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records, paginator.Nums())
}
}
// GetRecordsByFilter
// @Tag Record API
// @Title GetRecordsByFilter
// @Description get records by filter
// @Param filter body string true "filter Record message"
// @Success 200 {object} object.Record The Response object
// @router /get-records-filter [post]
func (c *ApiController) GetRecordsByFilter() {
body := string(c.Ctx.Input.RequestBody)
record := &object.Record{}
err := util.JsonToStruct(body, record)
if err != nil {
c.ResponseError(err.Error())
return
}
records, err := object.GetRecordsByField(record)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records)
}
// AddRecord
// @Title AddRecord
// @Tag Record API
// @Description add a record
// @Param body body object.Record true "The details of the record"
// @Success 200 {object} controllers.Response The Response object
// @router /add-record [post]
func (c *ApiController) AddRecord() {
var record object.Record
err := json.Unmarshal(c.Ctx.Input.RequestBody, &record)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddRecord(&record))
c.ServeJSON()
}

View File

@ -361,6 +361,9 @@ func (c *ApiController) UploadResource() {
return return
} }
if user.Properties == nil {
user.Properties = map[string]string{}
}
user.Properties[tag] = fileUrl user.Properties[tag] = fileUrl
user.Properties["isIdCardVerified"] = "false" user.Properties["isIdCardVerified"] = "false"
_, err = object.UpdateUser(user.GetId(), user, []string{"properties"}, false) _, err = object.UpdateUser(user.GetId(), user, []string{"properties"}, false)

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"fmt" "fmt"
"os"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -32,16 +33,15 @@ func (c *ApiController) UploadRoles() {
} }
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename)) fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
path := util.GetUploadXlsxPath(fileId) path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path) defer os.Remove(path)
err = saveFile(path, &file) err = saveFile(path, &file)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
affected, err := object.UploadRoles(owner, fileId) affected, err := object.UploadRoles(owner, path)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} }

View File

@ -40,6 +40,10 @@ type SmsForm struct {
OrgId string `json:"organizationId"` // e.g. "admin/built-in" OrgId string `json:"organizationId"` // e.g. "admin/built-in"
} }
type NotificationForm struct {
Content string `json:"content"`
}
// SendEmail // SendEmail
// @Title SendEmail // @Title SendEmail
// @Tag Service API // @Tag Service API
@ -140,10 +144,12 @@ func (c *ApiController) SendSms() {
return return
} }
invalidReceivers := getInvalidSmsReceivers(smsForm) if provider.Type != "Custom HTTP SMS" {
if len(invalidReceivers) != 0 { invalidReceivers := getInvalidSmsReceivers(smsForm)
c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), strings.Join(invalidReceivers, ", "))) if len(invalidReceivers) != 0 {
return c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), strings.Join(invalidReceivers, ", ")))
return
}
} }
err = object.SendSms(provider, smsForm.Content, smsForm.Receivers...) err = object.SendSms(provider, smsForm.Content, smsForm.Receivers...)
@ -154,3 +160,33 @@ func (c *ApiController) SendSms() {
c.ResponseOk() c.ResponseOk()
} }
// SendNotification
// @Title SendNotification
// @Tag Service API
// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs.
// @Param from body controllers.NotificationForm true "Details of the notification request"
// @Success 200 {object} Response object
// @router /api/send-notification [post]
func (c *ApiController) SendNotification() {
provider, err := c.GetProviderFromContext("Notification")
if err != nil {
c.ResponseError(err.Error())
return
}
var notificationForm NotificationForm
err = json.Unmarshal(c.Ctx.Input.RequestBody, &notificationForm)
if err != nil {
c.ResponseError(err.Error())
return
}
err = object.SendNotification(provider, notificationForm.Content)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
}

View File

@ -47,16 +47,16 @@ func (c *ApiController) GetSystemInfo() {
// @router /get-version-info [get] // @router /get-version-info [get]
func (c *ApiController) GetVersionInfo() { func (c *ApiController) GetVersionInfo() {
versionInfo, err := util.GetVersionInfo() versionInfo, err := util.GetVersionInfo()
if versionInfo.Version != "" {
if versionInfo.Version == "" { c.ResponseOk(versionInfo)
versionInfo, err = util.GetVersionInfoFromFile() return
if err != nil {
c.ResponseError(err.Error())
return
}
} }
versionInfo, err = util.GetVersionInfoFromFile()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(versionInfo) c.ResponseOk(versionInfo)
} }

View File

@ -90,7 +90,7 @@ func (c *ApiController) GetUsers() {
if limit == "" || page == "" { if limit == "" || page == "" {
if groupName != "" { if groupName != "" {
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName)) maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -567,6 +567,22 @@ func (c *ApiController) RemoveUserFromGroup() {
name := c.Ctx.Request.Form.Get("name") name := c.Ctx.Request.Form.Get("name")
groupName := c.Ctx.Request.Form.Get("groupName") groupName := c.Ctx.Request.Form.Get("groupName")
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, util.GetId(owner, groupName))) organization, err := object.GetOrganization(util.GetId("admin", owner))
c.ServeJSON() if err != nil {
return
}
item := object.GetAccountItemByName("Groups", organization)
res, msg := object.CheckAccountItemModifyRule(item, c.IsAdmin(), c.GetAcceptLanguage())
if !res {
c.ResponseError(msg)
return
}
affected, err := object.DeleteGroupForUser(util.GetId(owner, name), groupName)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(affected)
} }

View File

@ -48,17 +48,17 @@ func (c *ApiController) UploadUsers() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
path := util.GetUploadXlsxPath(fileId) path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path) defer os.Remove(path)
err = saveFile(path, &file) err = saveFile(path, &file)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
affected, err := object.UploadUsers(owner, fileId) affected, err := object.UploadUsers(owner, path)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -17,17 +17,18 @@ package form
type AuthForm struct { type AuthForm struct {
Type string `json:"type"` Type string `json:"type"`
Organization string `json:"organization"` Organization string `json:"organization"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Name string `json:"name"` Name string `json:"name"`
FirstName string `json:"firstName"` FirstName string `json:"firstName"`
LastName string `json:"lastName"` LastName string `json:"lastName"`
Email string `json:"email"` Email string `json:"email"`
Phone string `json:"phone"` Phone string `json:"phone"`
Affiliation string `json:"affiliation"` Affiliation string `json:"affiliation"`
IdCard string `json:"idCard"` IdCard string `json:"idCard"`
Region string `json:"region"` Region string `json:"region"`
InvitationCode string `json:"invitationCode"`
Application string `json:"application"` Application string `json:"application"`
ClientId string `json:"clientId"` ClientId string `json:"clientId"`

27
go.mod
View File

@ -14,8 +14,9 @@ require (
github.com/casbin/casbin/v2 v2.30.1 github.com/casbin/casbin/v2 v2.30.1
github.com/casdoor/go-sms-sender v0.12.0 github.com/casdoor/go-sms-sender v0.12.0
github.com/casdoor/gomail/v2 v2.0.1 github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/oss v1.2.1 github.com/casdoor/oss v1.3.0
github.com/casdoor/xorm-adapter/v3 v3.0.4 github.com/casdoor/xorm-adapter/v3 v3.0.4
github.com/casvisor/casvisor-go-sdk v1.0.3
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.9.0 github.com/denisenkom/go-mssqldb v0.9.0
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
@ -26,34 +27,32 @@ require (
github.com/go-mysql-org/go-mysql v1.7.0 github.com/go-mysql-org/go-mysql v1.7.0
github.com/go-pay/gopay v1.5.72 github.com/go-pay/gopay v1.5.72
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/go-webauthn/webauthn v0.6.0 github.com/go-webauthn/webauthn v0.6.0
github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.7.3 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/lestrrat-go/jwx v1.2.21 github.com/lestrrat-go/jwx v1.2.21
github.com/lib/pq v1.10.2 github.com/lib/pq v1.10.9
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
github.com/markbates/goth v1.75.2 github.com/markbates/goth v1.75.2
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/nikoksr/notify v0.41.0
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/nyaruka/phonenumbers v1.1.5 github.com/nyaruka/phonenumbers v1.1.5
github.com/pquerna/otp v1.4.0 github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.11.1 github.com/prometheus/client_golang v1.11.1
github.com/prometheus/client_model v0.2.0 github.com/prometheus/client_model v0.3.0
github.com/qiangmzsx/string-adapter/v2 v2.1.0 github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.9.0 github.com/russellhaering/gosaml2 v0.9.0
github.com/russellhaering/goxmldsig v1.2.0 github.com/russellhaering/goxmldsig v1.2.0
github.com/satori/go.uuid v1.2.0
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.4
github.com/stripe/stripe-go/v74 v74.29.0
github.com/tealeg/xlsx v1.0.5 github.com/tealeg/xlsx v1.0.5
github.com/thanhpk/randstr v1.0.4 github.com/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect
@ -61,12 +60,10 @@ require (
github.com/xorm-io/core v0.7.4 github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6 github.com/xorm-io/xorm v1.1.6
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.6.0 golang.org/x/crypto v0.11.0
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/net v0.13.0
golang.org/x/net v0.7.0 golang.org/x/oauth2 v0.10.0
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.4.0 // indirect modernc.org/sqlite v1.18.2
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
) )

1576
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,9 @@ func TestGenerateI18nFrontend(t *testing.T) {
applyToOtherLanguage("frontend", "it", data) applyToOtherLanguage("frontend", "it", data)
applyToOtherLanguage("frontend", "ms", data) applyToOtherLanguage("frontend", "ms", data)
applyToOtherLanguage("frontend", "tr", data) applyToOtherLanguage("frontend", "tr", data)
applyToOtherLanguage("frontend", "ar", data)
applyToOtherLanguage("frontend", "he", data)
applyToOtherLanguage("frontend", "fi", data)
} }
func TestGenerateI18nBackend(t *testing.T) { func TestGenerateI18nBackend(t *testing.T) {
@ -55,4 +58,7 @@ func TestGenerateI18nBackend(t *testing.T) {
applyToOtherLanguage("backend", "it", data) applyToOtherLanguage("backend", "it", data)
applyToOtherLanguage("backend", "ms", data) applyToOtherLanguage("backend", "ms", data)
applyToOtherLanguage("backend", "tr", data) applyToOtherLanguage("backend", "tr", data)
applyToOtherLanguage("backend", "ar", data)
applyToOtherLanguage("backend", "he", data)
applyToOtherLanguage("backend", "fi", data)
} }

142
i18n/locales/ar/data.json Normal file
View File

@ -0,0 +1,142 @@
{
"account": {
"Failed to add user": "Failed to add user",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Please sign out first": "Please sign out first",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
"LastName cannot be blank": "LastName cannot be blank",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
"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.": "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.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect": "password or code is incorrect",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space."
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

142
i18n/locales/fi/data.json Normal file
View File

@ -0,0 +1,142 @@
{
"account": {
"Failed to add user": "Failed to add user",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Please sign out first": "Please sign out first",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
"LastName cannot be blank": "LastName cannot be blank",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
"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.": "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.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect": "password or code is incorrect",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space."
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

142
i18n/locales/he/data.json Normal file
View File

@ -0,0 +1,142 @@
{
"account": {
"Failed to add user": "Failed to add user",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Please sign out first": "Please sign out first",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
"LastName cannot be blank": "LastName cannot be blank",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
"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.": "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.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect": "password or code is incorrect",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space."
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -24,20 +24,10 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
const Web3AuthTokenKey = "web3AuthToken"
type MetaMaskIdProvider struct { type MetaMaskIdProvider struct {
Client *http.Client Client *http.Client
} }
type Web3AuthToken struct {
Address string `json:"address"`
Nonce string `json:"nonce"`
CreateAt uint64 `json:"createAt"`
TypedData string `json:"typedData"`
Signature string `json:"signature"` // signature for typed data
}
func NewMetaMaskIdProvider() *MetaMaskIdProvider { func NewMetaMaskIdProvider() *MetaMaskIdProvider {
idp := &MetaMaskIdProvider{} idp := &MetaMaskIdProvider{}
return idp return idp

View File

@ -111,6 +111,8 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) IdProvider {
return NewBilibiliIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl) return NewBilibiliIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "MetaMask": case "MetaMask":
return NewMetaMaskIdProvider() return NewMetaMaskIdProvider()
case "Web3Onboard":
return NewWeb3OnboardIdProvider()
default: default:
if isGothSupport(idpInfo.Type) { if isGothSupport(idpInfo.Type) {
return NewGothIdProvider(idpInfo.Type, idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl) return NewGothIdProvider(idpInfo.Type, idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)

103
idp/web3onboard.go Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2023 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 idp
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"golang.org/x/oauth2"
)
const Web3AuthTokenKey = "web3AuthToken"
type Web3AuthToken struct {
Address string `json:"address"`
Nonce string `json:"nonce"`
CreateAt uint64 `json:"createAt"`
TypedData string `json:"typedData"` // typed data use for application
Signature string `json:"signature"` // signature for typed data
WalletType string `json:"walletType"` // e.g."MetaMask", "Coinbase"
}
type Web3OnboardIdProvider struct {
Client *http.Client
}
func NewWeb3OnboardIdProvider() *Web3OnboardIdProvider {
idp := &Web3OnboardIdProvider{}
return idp
}
func (idp *Web3OnboardIdProvider) SetHttpClient(client *http.Client) {
idp.Client = client
}
func (idp *Web3OnboardIdProvider) GetToken(code string) (*oauth2.Token, error) {
web3AuthToken := Web3AuthToken{}
if err := json.Unmarshal([]byte(code), &web3AuthToken); err != nil {
return nil, err
}
token := &oauth2.Token{
AccessToken: fmt.Sprintf("%v:%v", Web3AuthTokenKey, web3AuthToken.Address),
TokenType: "Bearer",
Expiry: time.Now().AddDate(0, 1, 0),
}
token = token.WithExtra(map[string]interface{}{
Web3AuthTokenKey: web3AuthToken,
})
return token, nil
}
func (idp *Web3OnboardIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
web3AuthToken, ok := token.Extra(Web3AuthTokenKey).(Web3AuthToken)
if !ok {
return nil, errors.New("invalid web3AuthToken")
}
fmtAddress := fmt.Sprintf("%v_%v",
strings.ReplaceAll(strings.TrimSpace(web3AuthToken.WalletType), " ", "_"),
web3AuthToken.Address,
)
userInfo := &UserInfo{
Id: fmtAddress,
Username: fmtAddress,
DisplayName: fmtAddress,
AvatarUrl: fmt.Sprintf("metamask:%v", forceEthereumAddress(web3AuthToken.Address)),
}
return userInfo, nil
}
func forceEthereumAddress(address string) string {
// The required address to general MetaMask avatar is a string of length 42 that represents an Ethereum address.
// This function is used to force any address as an Ethereum address
address = strings.TrimSpace(address)
var builder strings.Builder
for _, ch := range address {
builder.WriteRune(ch)
}
for len(builder.String()) < 42 {
builder.WriteString("0")
}
if len(builder.String()) > 42 {
return builder.String()[:42]
}
return builder.String()
}

View File

@ -9,11 +9,11 @@
"passwordType": "plain", "passwordType": "plain",
"passwordSalt": "", "passwordSalt": "",
"passwordOptions": ["AtLeast6"], "passwordOptions": ["AtLeast6"],
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR"], "countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR", "DZ", "IL", "PH"],
"defaultAvatar": "", "defaultAvatar": "",
"defaultApplication": "", "defaultApplication": "",
"tags": [], "tags": [],
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr"], "languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "fi"],
"masterPassword": "", "masterPassword": "",
"initScore": 2000, "initScore": 2000,
"enableSoftDeletion": false, "enableSoftDeletion": false,
@ -123,7 +123,6 @@
"score": 2000, "score": 2000,
"ranking": 1, "ranking": 1,
"isAdmin": true, "isAdmin": true,
"isGlobalAdmin": true,
"isForbidden": false, "isForbidden": false,
"isDeleted": false, "isDeleted": false,
"signupApplication": "", "signupApplication": "",

View File

@ -62,7 +62,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
return return
} }
if bindOrg == "built-in" || bindUser.IsGlobalAdmin { if bindOrg == "built-in" || bindUser.IsGlobalAdmin() {
m.Client.IsGlobalAdmin, m.Client.IsOrgAdmin = true, true m.Client.IsGlobalAdmin, m.Client.IsOrgAdmin = true, true
} else if bindUser.IsAdmin { } else if bindUser.IsAdmin {
m.Client.IsOrgAdmin = true m.Client.IsOrgAdmin = true

16
main.go
View File

@ -15,7 +15,6 @@
package main package main
import ( import (
"flag"
"fmt" "fmt"
"github.com/beego/beego" "github.com/beego/beego"
@ -30,17 +29,10 @@ import (
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
func getCreateDatabaseFlag() bool {
res := flag.Bool("createDatabase", false, "true if you need Casdoor to create database")
flag.Parse()
return *res
}
func main() { func main() {
createDatabase := getCreateDatabaseFlag() object.InitFlag()
object.InitAdapter()
object.InitAdapter(createDatabase) object.CreateTables()
object.CreateTables(createDatabase)
object.DoMigration() object.DoMigration()
object.InitDb() object.InitDb()
@ -49,6 +41,8 @@ func main() {
object.InitLdapAutoSynchronizer() object.InitLdapAutoSynchronizer()
proxy.InitHttpClient() proxy.InitHttpClient()
authz.InitApi() authz.InitApi()
object.InitUserManager()
object.InitCasvisorConfig()
util.SafeGoroutine(func() { object.RunSyncUsersJob() }) util.SafeGoroutine(func() { object.RunSyncUsersJob() })

View File

@ -0,0 +1,73 @@
// Copyright 2023 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 notification
import (
"bytes"
"context"
"fmt"
"net/http"
"github.com/casdoor/casdoor/proxy"
)
type HttpNotificationClient struct {
endpoint string
method string
paramName string
}
func NewCustomHttpProvider(endpoint string, method string, paramName string) (*HttpNotificationClient, error) {
client := &HttpNotificationClient{
endpoint: endpoint,
method: method,
paramName: paramName,
}
return client, nil
}
func (c *HttpNotificationClient) Send(ctx context.Context, subject string, content string) error {
var err error
httpClient := proxy.DefaultHttpClient
req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content))
if err != nil {
return err
}
if c.method == "POST" {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.PostForm = map[string][]string{
c.paramName: {content},
}
} else if c.method == "GET" {
q := req.URL.Query()
q.Add(c.paramName, content)
req.URL.RawQuery = q.Encode()
}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("SendMessage() error, custom HTTP Notification request failed with status: %s", resp.Status)
}
return err
}

View File

@ -1,4 +1,4 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved. // Copyright 2023 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -12,18 +12,16 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package util package notification
import xormadapter "github.com/casdoor/xorm-adapter/v3" import "github.com/nikoksr/notify"
func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string { func GetNotificationProvider(typ string, appId string, receiver string, method string, title string) (notify.Notifier, error) {
s := []string{ if typ == "Telegram" {
casbinRule.V0, return NewTelegramProvider(appId, receiver)
casbinRule.V1, } else if typ == "Custom HTTP" {
casbinRule.V2, return NewCustomHttpProvider(receiver, method, title)
casbinRule.V3,
casbinRule.V4,
casbinRule.V5,
} }
return s
return nil, nil
} }

42
notification/telegram.go Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2023 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 notification
import (
"strconv"
"github.com/casdoor/casdoor/proxy"
api "github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/telegram"
)
func NewTelegramProvider(apiToken string, chatIdStr string) (notify.Notifier, error) {
client, err := api.NewBotAPIWithClient(apiToken, proxy.ProxyHttpClient)
if err != nil {
return nil, err
}
t := &telegram.Telegram{}
t.SetClient(client)
chatId, err := strconv.ParseInt(chatIdStr, 10, 64)
if err != nil {
return nil, err
}
t.AddReceivers(chatId)
return t, nil
}

View File

@ -18,11 +18,11 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/casbin/casbin/v2/model"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3" xormadapter "github.com/casdoor/xorm-adapter/v3"
"github.com/xorm-io/core" "github.com/xorm-io/core"
"github.com/xorm-io/xorm"
) )
type Adapter struct { type Adapter struct {
@ -40,8 +40,6 @@ type Adapter struct {
Table string `xorm:"varchar(100)" json:"table"` Table string `xorm:"varchar(100)" json:"table"`
TableNamePrefix string `xorm:"varchar(100)" json:"tableNamePrefix"` TableNamePrefix string `xorm:"varchar(100)" json:"tableNamePrefix"`
IsEnabled bool `json:"isEnabled"`
*xormadapter.Adapter `xorm:"-" json:"-"` *xormadapter.Adapter `xorm:"-" json:"-"`
} }
@ -149,20 +147,23 @@ func (adapter *Adapter) getTable() string {
} }
} }
func (adapter *Adapter) initAdapter() error { func (adapter *Adapter) InitAdapter() error {
if adapter.Adapter == nil { if adapter.Adapter == nil {
var dataSourceName string var dataSourceName string
if adapter.builtInAdapter() { if adapter.builtInAdapter() {
dataSourceName = conf.GetConfigString("dataSourceName") dataSourceName = conf.GetConfigString("dataSourceName")
if adapter.DatabaseType == "mysql" {
dataSourceName = dataSourceName + adapter.Database
}
} else { } else {
switch adapter.DatabaseType { switch adapter.DatabaseType {
case "mssql": case "mssql":
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", adapter.User, dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", adapter.User,
adapter.Password, adapter.Host, adapter.Port, adapter.Database) adapter.Password, adapter.Host, adapter.Port, adapter.Database)
case "mysql": case "mysql":
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", adapter.User, dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", adapter.User,
adapter.Password, adapter.Host, adapter.Port) adapter.Password, adapter.Host, adapter.Port, adapter.Database)
case "postgres": case "postgres":
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", adapter.User, dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", adapter.User,
adapter.Password, adapter.Host, adapter.Port, adapter.Database) adapter.Password, adapter.Host, adapter.Port, adapter.Database)
@ -181,7 +182,8 @@ func (adapter *Adapter) initAdapter() error {
} }
var err error var err error
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(adapter.DatabaseType, dataSourceName, adapter.Database).Engine, adapter.getTable(), adapter.TableNamePrefix) engine, err := xorm.NewEngine(adapter.DatabaseType, dataSourceName)
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, adapter.getTable(), adapter.TableNamePrefix)
if err != nil { if err != nil {
return err return err
} }
@ -209,116 +211,10 @@ func adapterChangeTrigger(oldName string, newName string) error {
return session.Commit() return session.Commit()
} }
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 GetPolicies(adapter *Adapter) ([]*xormadapter.CasbinRule, error) {
err := adapter.initAdapter()
if err != nil {
return nil, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return nil, err
}
policies := matrixToCasbinRules("p", casbinModel.GetPolicy("p", "p"))
policies = append(policies, matrixToCasbinRules("g", casbinModel.GetPolicy("g", "g"))...)
return policies, nil
}
func UpdatePolicy(oldPolicy, newPolicy []string, adapter *Adapter) (bool, error) {
err := adapter.initAdapter()
if err != nil {
return false, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return false, err
}
affected := casbinModel.UpdatePolicy("p", "p", oldPolicy, newPolicy)
if err != nil {
return affected, err
}
return affected, nil
}
func AddPolicy(policy []string, adapter *Adapter) (bool, error) {
err := adapter.initAdapter()
if err != nil {
return false, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return false, err
}
casbinModel.AddPolicy("p", "p", policy)
return true, nil
}
func RemovePolicy(policy []string, adapter *Adapter) (bool, error) {
err := adapter.initAdapter()
if err != nil {
return false, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return false, err
}
affected := casbinModel.RemovePolicy("p", "p", policy)
if err != nil {
return affected, err
}
return affected, nil
}
func (adapter *Adapter) builtInAdapter() bool { func (adapter *Adapter) builtInAdapter() bool {
if adapter.Owner != "built-in" { if adapter.Owner != "built-in" {
return false return false
} }
return adapter.Name == "permission-adapter-built-in" || adapter.Name == "api-adapter-built-in" return adapter.Name == "user-adapter-built-in" || adapter.Name == "api-adapter-built-in"
}
func getModelDef() model.Model {
casbinModel := model.NewModel()
casbinModel.AddDef("p", "p", "_, _, _, _, _, _")
casbinModel.AddDef("g", "g", "_, _, _, _, _, _")
return casbinModel
} }

View File

@ -58,6 +58,7 @@ type Application struct {
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"` GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"` OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
Tags []string `xorm:"mediumtext" json:"tags"` Tags []string `xorm:"mediumtext" json:"tags"`
InvitationCodes []string `xorm:"varchar(200)" json:"invitationCodes"`
ClientId string `xorm:"varchar(100)" json:"clientId"` ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"` ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
@ -311,6 +312,11 @@ func GetMaskedApplication(application *Application, userId string) *Application
application.OrganizationObj.PasswordSalt = "***" application.OrganizationObj.PasswordSalt = "***"
} }
} }
if application.InvitationCodes != nil {
application.InvitationCodes = []string{"***"}
}
return application return application
} }

View File

@ -124,6 +124,16 @@ func CheckUserSignup(application *Application, organization *Organization, form
} }
} }
if len(application.InvitationCodes) > 0 {
if form.InvitationCode == "" {
return i18n.Translate(lang, "check:Invitation code cannot be blank")
} else {
if !util.InSlice(application.InvitationCodes, form.InvitationCode) {
return i18n.Translate(lang, "check:Invitation code is invalid")
}
}
}
return "" return ""
} }
@ -141,7 +151,7 @@ func checkSigninErrorTimes(user *User, lang string) string {
// reset the error times // reset the error times
user.SigninWrongTimes = 0 user.SigninWrongTimes = 0
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, user.IsGlobalAdmin) UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
} }
return "" return ""
@ -319,7 +329,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
if requestUser == nil { if requestUser == nil {
return false, fmt.Errorf(i18n.Translate(lang, "check:Session outdated, please login again")) return false, fmt.Errorf(i18n.Translate(lang, "check:Session outdated, please login again"))
} }
if requestUser.IsGlobalAdmin { if requestUser.IsGlobalAdmin() {
hasPermission = true hasPermission = true
} else if requestUserId == userId { } else if requestUserId == userId {
hasPermission = true hasPermission = true

View File

@ -42,7 +42,7 @@ func resetUserSigninErrorTimes(user *User) {
return return
} }
user.SigninWrongTimes = 0 user.SigninWrongTimes = 0
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin) UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
} }
func recordSigninErrorInfo(user *User, lang string, options ...bool) string { func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
@ -61,7 +61,7 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
} }
// update user // update user
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin) UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
if leftChances == 0 && enableCaptcha { if leftChances == 0 && enableCaptcha {
return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect")) return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect"))

View File

@ -15,10 +15,12 @@
package object package object
import ( import (
"errors" "fmt"
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
"github.com/xorm-io/core" "github.com/xorm-io/core"
) )
@ -30,10 +32,10 @@ type Enforcer struct {
DisplayName string `xorm:"varchar(100)" json:"displayName"` DisplayName string `xorm:"varchar(100)" json:"displayName"`
Description string `xorm:"varchar(100)" json:"description"` Description string `xorm:"varchar(100)" json:"description"`
Model string `xorm:"varchar(100)" json:"model"` Model string `xorm:"varchar(100)" json:"model"`
Adapter string `xorm:"varchar(100)" json:"adapter"` Adapter string `xorm:"varchar(100)" json:"adapter"`
IsEnabled bool `json:"isEnabled"`
ModelCfg map[string]string `xorm:"-" json:"modelCfg"`
*casbin.Enforcer *casbin.Enforcer
} }
@ -120,44 +122,131 @@ func DeleteEnforcer(enforcer *Enforcer) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func (enforcer *Enforcer) GetId() string {
return fmt.Sprintf("%s/%s", enforcer.Owner, enforcer.Name)
}
func (enforcer *Enforcer) InitEnforcer() error { func (enforcer *Enforcer) InitEnforcer() error {
if enforcer.Enforcer == nil { if enforcer.Enforcer != nil {
if enforcer == nil { return nil
return errors.New("enforcer is nil") }
}
if enforcer.Model == "" || enforcer.Adapter == "" {
return errors.New("missing model or adapter")
}
var err error if enforcer.Model == "" {
var m *Model return fmt.Errorf("the model for enforcer: %s should not be empty", enforcer.GetId())
var a *Adapter }
if enforcer.Adapter == "" {
return fmt.Errorf("the adapter for enforcer: %s should not be empty", enforcer.GetId())
}
if m, err = GetModel(enforcer.Model); err != nil { m, err := GetModel(enforcer.Model)
return err if err != nil {
} else if m == nil { return err
return errors.New("model not found") } else if m == nil {
} return fmt.Errorf("the model: %s for enforcer: %s is not found", enforcer.Model, enforcer.GetId())
if a, err = GetAdapter(enforcer.Adapter); err != nil { }
return err
} else if a == nil {
return errors.New("adapter not found")
}
err = m.initModel() a, err := GetAdapter(enforcer.Adapter)
if err != nil { if err != nil {
return err return err
} } else if a == nil {
err = a.initAdapter() return fmt.Errorf("the adapter: %s for enforcer: %s is not found", enforcer.Adapter, enforcer.GetId())
if err != nil { }
return err
}
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter) err = m.initModel()
if err != nil { if err != nil {
return err return err
} }
enforcer.Enforcer = casbinEnforcer err = a.InitAdapter()
if err != nil {
return err
}
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter)
if err != nil {
return err
}
enforcer.Enforcer = casbinEnforcer
return nil
}
func GetInitializedEnforcer(enforcerId string) (*Enforcer, error) {
enforcer, err := GetEnforcer(enforcerId)
if err != nil {
return nil, err
} else if enforcer == nil {
return nil, fmt.Errorf("the enforcer: %s is not found", enforcerId)
}
err = enforcer.InitEnforcer()
if err != nil {
return nil, err
}
return enforcer, nil
}
func GetPolicies(id string) ([]*xormadapter.CasbinRule, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return nil, err
}
policies := util.MatrixToCasbinRules("p", enforcer.GetPolicy())
if enforcer.GetModel()["g"] != nil {
policies = append(policies, util.MatrixToCasbinRules("g", enforcer.GetGroupingPolicy())...)
}
return policies, nil
}
func UpdatePolicy(id string, oldPolicy, newPolicy []string) (bool, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return false, err
}
return enforcer.UpdatePolicy(oldPolicy, newPolicy)
}
func AddPolicy(id string, policy []string) (bool, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return false, err
}
return enforcer.AddPolicy(policy)
}
func RemovePolicy(id string, policy []string) (bool, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return false, err
}
return enforcer.RemovePolicy(policy)
}
func (enforcer *Enforcer) LoadModelCfg() error {
if enforcer.ModelCfg != nil {
return nil
}
model, err := GetModel(enforcer.Model)
if err != nil {
return err
} else if model == nil {
return fmt.Errorf("the model: %s for enforcer: %s is not found", enforcer.Model, enforcer.GetId())
}
cfg, err := config.NewConfigFromText(model.ModelText)
if err != nil {
return err
}
enforcer.ModelCfg = make(map[string]string)
enforcer.ModelCfg["p"] = cfg.String("policy_definition::p")
if cfg.String("role_definition::g") != "" {
enforcer.ModelCfg["g"] = cfg.String("role_definition::g")
} }
return nil return nil

140
object/get-dashboard.go Normal file
View File

@ -0,0 +1,140 @@
// 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 object
import (
"sync"
"time"
)
type Dashboard struct {
OrganizationCounts []int `json:"organizationCounts"`
UserCounts []int `json:"userCounts"`
ProviderCounts []int `json:"providerCounts"`
ApplicationCounts []int `json:"applicationCounts"`
SubscriptionCounts []int `json:"subscriptionCounts"`
}
func GetDashboard(owner string) (*Dashboard, error) {
dashboard := &Dashboard{
OrganizationCounts: make([]int, 31),
UserCounts: make([]int, 31),
ProviderCounts: make([]int, 31),
ApplicationCounts: make([]int, 31),
SubscriptionCounts: make([]int, 31),
}
var wg sync.WaitGroup
organizations := []Organization{}
users := []User{}
providers := []Provider{}
applications := []Application{}
subscriptions := []Subscription{}
wg.Add(5)
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&organizations, &Organization{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&users, &User{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&providers, &Provider{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&applications, &Application{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&subscriptions, &Subscription{Owner: owner}); err != nil {
panic(err)
}
}()
wg.Wait()
nowTime := time.Now()
for i := 30; i >= 0; i-- {
cutTime := nowTime.AddDate(0, 0, -i)
dashboard.OrganizationCounts[30-i] = countCreatedBefore(organizations, cutTime)
dashboard.UserCounts[30-i] = countCreatedBefore(users, cutTime)
dashboard.ProviderCounts[30-i] = countCreatedBefore(providers, cutTime)
dashboard.ApplicationCounts[30-i] = countCreatedBefore(applications, cutTime)
dashboard.SubscriptionCounts[30-i] = countCreatedBefore(subscriptions, cutTime)
}
return dashboard, nil
}
func countCreatedBefore(objects interface{}, before time.Time) int {
count := 0
switch obj := objects.(type) {
case []Organization:
for _, o := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", o.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []User:
for _, u := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", u.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Provider:
for _, p := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", p.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Application:
for _, a := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", a.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Subscription:
for _, s := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", s.CreatedTime)
if createdTime.Before(before) {
count++
}
}
}
return count
}

View File

@ -214,30 +214,18 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
return treeData return treeData
} }
func RemoveUserFromGroup(owner, name, groupId string) (bool, error) {
user, err := getUser(owner, name)
if err != nil {
return false, err
}
if user == nil {
return false, errors.New("user not exist")
}
user.Groups = util.DeleteVal(user.Groups, groupId)
affected, err := updateUser(user.GetId(), user, []string{"groups"})
if err != nil {
return false, err
}
return affected != 0, err
}
func GetGroupUserCount(groupId string, field, value string) (int64, error) { func GetGroupUserCount(groupId string, field, value string) (int64, error) {
owner, _ := util.GetOwnerAndNameFromId(groupId)
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
if err != nil {
return 0, err
}
if field == "" && value == "" { if field == "" && value == "" {
return ormer.Engine.Where(builder.Like{"`groups`", groupId}). return int64(len(names)), nil
Count(&User{})
} else { } else {
return ormer.Engine.Table("user"). return ormer.Engine.Table("user").
Where(builder.Like{"`groups`", groupId}). Where("owner = ?", owner).In("name", names).
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%"). And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
Count() Count()
} }
@ -245,8 +233,14 @@ func GetGroupUserCount(groupId string, field, value string) (int64, error) {
func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) { func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
users := []*User{} users := []*User{}
owner, _ := util.GetOwnerAndNameFromId(groupId)
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
if err != nil {
return nil, err
}
session := ormer.Engine.Table("user"). session := ormer.Engine.Table("user").
Where(builder.Like{"`groups`", groupId + "\""}) Where("owner = ?", owner).In("name", names)
if offset != -1 && limit != -1 { if offset != -1 && limit != -1 {
session.Limit(limit, offset) session.Limit(limit, offset)
@ -265,7 +259,7 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField))) session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
} }
err := session.Find(&users) err = session.Find(&users)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -275,13 +269,13 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
func GetGroupUsers(groupId string) ([]*User, error) { func GetGroupUsers(groupId string) ([]*User, error) {
users := []*User{} users := []*User{}
err := ormer.Engine.Table("user"). owner, _ := util.GetOwnerAndNameFromId(groupId)
Where(builder.Like{"`groups`", groupId + "\""}). names, err := userEnforcer.GetUserNamesByGroupName(groupId)
Find(&users)
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return users, nil return users, nil
} }

View File

@ -73,7 +73,6 @@ func getBuiltInAccountItems() []*AccountItem {
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, {Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, {Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
@ -145,7 +144,6 @@ func initBuiltInUser() {
Score: 2000, Score: 2000,
Ranking: 1, Ranking: 1,
IsAdmin: true, IsAdmin: true,
IsGlobalAdmin: true,
IsForbidden: false, IsForbidden: false,
IsDeleted: false, IsDeleted: false,
SignupApplication: "app-built-in", SignupApplication: "app-built-in",
@ -318,7 +316,6 @@ func initBuiltInUserModel() {
Name: "user-model-built-in", Name: "user-model-built-in",
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
DisplayName: "Built-in Model", DisplayName: "Built-in Model",
IsEnabled: true,
ModelText: `[request_definition] ModelText: `[request_definition]
r = sub, obj, act r = sub, obj, act
@ -376,7 +373,6 @@ m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
Name: "api-model-built-in", Name: "api-model-built-in",
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
DisplayName: "API Model", DisplayName: "API Model",
IsEnabled: true,
ModelText: modelText, ModelText: modelText,
} }
_, err = AddModel(model) _, err = AddModel(model)
@ -435,7 +431,6 @@ func initBuiltInUserAdapter() {
TableNamePrefix: conf.GetConfigString("tableNamePrefix"), TableNamePrefix: conf.GetConfigString("tableNamePrefix"),
Database: conf.GetConfigString("dbName"), Database: conf.GetConfigString("dbName"),
Table: "casbin_user_rule", Table: "casbin_user_rule",
IsEnabled: true,
} }
_, err = AddAdapter(adapter) _, err = AddAdapter(adapter)
if err != nil { if err != nil {
@ -462,7 +457,6 @@ func initBuiltInApiAdapter() {
TableNamePrefix: conf.GetConfigString("tableNamePrefix"), TableNamePrefix: conf.GetConfigString("tableNamePrefix"),
Database: conf.GetConfigString("dbName"), Database: conf.GetConfigString("dbName"),
Table: "casbin_api_rule", Table: "casbin_api_rule",
IsEnabled: true,
} }
_, err = AddAdapter(adapter) _, err = AddAdapter(adapter)
if err != nil { if err != nil {
@ -484,10 +478,9 @@ func initBuiltInUserEnforcer() {
Owner: "built-in", Owner: "built-in",
Name: "user-enforcer-built-in", Name: "user-enforcer-built-in",
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
DisplayName: "Permission Enforcer", DisplayName: "User Enforcer",
Model: "built-in/user-model-built-in", Model: "built-in/user-model-built-in",
Adapter: "built-in/user-adapter-built-in", Adapter: "built-in/user-adapter-built-in",
IsEnabled: true,
} }
_, err = AddEnforcer(enforcer) _, err = AddEnforcer(enforcer)
@ -513,7 +506,6 @@ func initBuiltInApiEnforcer() {
DisplayName: "API Enforcer", DisplayName: "API Enforcer",
Model: "built-in/api-model-built-in", Model: "built-in/api-model-built-in",
Adapter: "built-in/api-adapter-built-in", Adapter: "built-in/api-adapter-built-in",
IsEnabled: true,
} }
_, err = AddEnforcer(enforcer) _, err = AddEnforcer(enforcer)

View File

@ -41,19 +41,20 @@ type LdapUser struct {
GidNumber string `json:"gidNumber"` GidNumber string `json:"gidNumber"`
// Gcn string // Gcn string
Uuid string `json:"uuid"` Uuid string `json:"uuid"`
UserPrincipalName string `json:"userPrincipalName"`
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
Mail string Mail string
Email string `json:"email"` Email string `json:"email"`
EmailAddress string EmailAddress string
TelephoneNumber string TelephoneNumber string
Mobile string Mobile string `json:"mobile"`
MobileTelephoneNumber string MobileTelephoneNumber string
RegisteredAddress string RegisteredAddress string
PostalAddress string PostalAddress string
GroupId string `json:"groupId"` GroupId string `json:"groupId"`
Phone string `json:"phone"` Address string `json:"address"`
Address string `json:"address"` MemberOf string `json:"memberOf"`
} }
func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) { func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
@ -168,6 +169,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
user.Uuid = attribute.Values[0] user.Uuid = attribute.Values[0]
case "objectGUID": case "objectGUID":
user.Uuid = attribute.Values[0] user.Uuid = attribute.Values[0]
case "userPrincipalName":
user.UserPrincipalName = attribute.Values[0]
case "displayName": case "displayName":
user.DisplayName = attribute.Values[0] user.DisplayName = attribute.Values[0]
case "mail": case "mail":
@ -186,6 +189,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
user.RegisteredAddress = attribute.Values[0] user.RegisteredAddress = attribute.Values[0]
case "postalAddress": case "postalAddress":
user.PostalAddress = attribute.Values[0] user.PostalAddress = attribute.Values[0]
case "memberOf":
user.MemberOf = attribute.Values[0]
} }
} }
ldapUsers = append(ldapUsers, user) ldapUsers = append(ldapUsers, user)
@ -306,18 +311,20 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
} }
newUser := &User{ newUser := &User{
Owner: owner, Owner: owner,
Name: name, Name: name,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
DisplayName: syncUser.buildLdapDisplayName(), DisplayName: syncUser.buildLdapDisplayName(),
Avatar: organization.DefaultAvatar, SignupApplication: organization.DefaultApplication,
Email: syncUser.Email, Type: "normal-user",
Phone: syncUser.Phone, Avatar: organization.DefaultAvatar,
Address: []string{syncUser.Address}, Email: syncUser.Email,
Affiliation: affiliation, Phone: syncUser.Mobile,
Tag: tag, Address: []string{syncUser.Address},
Score: score, Affiliation: affiliation,
Ldap: syncUser.Uuid, Tag: tag,
Score: score,
Ldap: syncUser.Uuid,
} }
affected, err := AddUser(newUser) affected, err := AddUser(newUser)

View File

@ -84,7 +84,7 @@ func MfaRecover(user *User, recoveryCode string) error {
return fmt.Errorf("recovery code not found") return fmt.Errorf("recovery code not found")
} }
_, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, user.IsAdminUser()) _, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, false)
if err != nil { if err != nil {
return err return err
} }
@ -181,7 +181,7 @@ func DisabledMultiFactorAuth(user *User) error {
func SetPreferredMultiFactorAuth(user *User, mfaType string) error { func SetPreferredMultiFactorAuth(user *User, mfaType string) error {
user.PreferredMfaType = mfaType user.PreferredMfaType = mfaType
_, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, user.IsAdminUser()) _, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -17,6 +17,7 @@ package object
import ( import (
"errors" "errors"
"fmt" "fmt"
"time"
"github.com/beego/beego" "github.com/beego/beego"
"github.com/beego/beego/context" "github.com/beego/beego/context"
@ -25,7 +26,10 @@ import (
"github.com/pquerna/otp/totp" "github.com/pquerna/otp/totp"
) )
const MfaTotpSecretSession = "mfa_totp_secret" const (
MfaTotpSecretSession = "mfa_totp_secret"
MfaTotpPeriodInSeconds = 30
)
type TotpMfa struct { type TotpMfa struct {
Config *MfaProps Config *MfaProps
@ -76,7 +80,13 @@ func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
if secret == nil { if secret == nil {
return errors.New("totp secret is missing") return errors.New("totp secret is missing")
} }
result := totp.Validate(passcode, secret.(string))
result, _ := totp.ValidateCustom(passcode, secret.(string), time.Now().UTC(), totp.ValidateOpts{
Period: MfaTotpPeriodInSeconds,
Skew: 1,
Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA1,
})
if result { if result {
return nil return nil
@ -133,7 +143,7 @@ func NewTotpMfaUtil(config *MfaProps) *TotpMfa {
return &TotpMfa{ return &TotpMfa{
Config: config, Config: config,
period: 30, period: MfaTotpPeriodInSeconds,
secretSize: 20, secretSize: 20,
digits: otp.DigitsSix, digits: otp.DigitsSix,
} }

View File

@ -30,7 +30,6 @@ type Model struct {
Description string `xorm:"varchar(100)" json:"description"` Description string `xorm:"varchar(100)" json:"description"`
ModelText string `xorm:"mediumtext" json:"modelText"` ModelText string `xorm:"mediumtext" json:"modelText"`
IsEnabled bool `json:"isEnabled"`
model.Model `xorm:"-" json:"-"` model.Model `xorm:"-" json:"-"`
} }

42
object/notification.go Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2023 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 object
import (
"context"
"github.com/casdoor/casdoor/notification"
"github.com/nikoksr/notify"
)
func getNotificationClient(provider *Provider) (notify.Notifier, error) {
var client notify.Notifier
client, err := notification.GetNotificationProvider(provider.Type, provider.AppId, provider.Receiver, provider.Method, provider.Title)
if err != nil {
return nil, err
}
return client, nil
}
func SendNotification(provider *Provider, content string) error {
client, err := getNotificationClient(provider)
if err != nil {
return err
}
err = client.Send(context.Background(), "", content)
return err
}

View File

@ -103,7 +103,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
SubjectTypesSupported: []string{"public"}, SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256"}, IdTokenSigningAlgValuesSupported: []string{"RS256"},
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"}, ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isGlobalAdmin", "isForbidden", "signupApplication", "ldap"}, ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
RequestParameterSupported: true, RequestParameterSupported: true,
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"}, RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend), EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend),

View File

@ -437,14 +437,6 @@ func organizationChangeTrigger(oldName string, newName string) error {
return err return err
} }
record := new(Record)
record.Owner = newName
record.Organization = newName
_, err = session.Where("organization=?", oldName).Update(record)
if err != nil {
return err
}
resource := new(Resource) resource := new(Resource)
resource.Owner = newName resource.Owner = newName
_, err = session.Where("owner=?", oldName).Update(resource) _, err = session.Where("owner=?", oldName).Update(resource)

View File

@ -16,13 +16,13 @@ package object
import ( import (
"database/sql" "database/sql"
"flag"
"fmt" "fmt"
"runtime" "runtime"
"strings" "strings"
"github.com/beego/beego" "github.com/beego/beego"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3" xormadapter "github.com/casdoor/xorm-adapter/v3"
_ "github.com/denisenkom/go-mssqldb" // db = mssql _ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql _ "github.com/go-sql-driver/mysql" // db = mysql
@ -32,7 +32,24 @@ import (
_ "modernc.org/sqlite" // db = sqlite _ "modernc.org/sqlite" // db = sqlite
) )
var ormer *Ormer var (
ormer *Ormer = nil
isCreateDatabaseDefined = false
createDatabase = true
)
func InitFlag() {
if !isCreateDatabaseDefined {
isCreateDatabaseDefined = true
createDatabase = getCreateDatabaseFlag()
}
}
func getCreateDatabaseFlag() bool {
res := flag.Bool("createDatabase", false, "true if you need to create database")
flag.Parse()
return *res
}
func InitConfig() { func InitConfig() {
err := beego.LoadAppConfig("ini", "../conf/app.conf") err := beego.LoadAppConfig("ini", "../conf/app.conf")
@ -42,12 +59,12 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter(true) InitAdapter()
CreateTables(true) CreateTables()
DoMigration() DoMigration()
} }
func InitAdapter(createDatabase bool) { func InitAdapter() {
if createDatabase { if createDatabase {
err := createDatabaseForPostgres(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName")) err := createDatabaseForPostgres(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
if err != nil { if err != nil {
@ -62,7 +79,7 @@ func InitAdapter(createDatabase bool) {
ormer.Engine.SetTableMapper(tbMapper) ormer.Engine.SetTableMapper(tbMapper)
} }
func CreateTables(createDatabase bool) { func CreateTables() {
if createDatabase { if createDatabase {
err := ormer.CreateDatabase() err := ormer.CreateDatabase()
if err != nil { if err != nil {
@ -229,11 +246,6 @@ func (a *Ormer) createTable() {
panic(err) panic(err)
} }
err = a.Engine.Sync2(new(Record))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Webhook)) err = a.Engine.Sync2(new(Webhook))
if err != nil { if err != nil {
panic(err) panic(err)
@ -294,76 +306,3 @@ func (a *Ormer) createTable() {
panic(err) panic(err)
} }
} }
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
session = session.And("owner=?", owner)
}
if field != "" && value != "" {
if util.FilterField(field) {
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
return session
}
func GetSessionForUser(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
if offset == -1 {
session = session.And("owner=?", owner)
} else {
session = session.And("a.owner=?", owner)
}
}
if field != "" && value != "" {
if util.FilterField(field) {
if offset != -1 {
field = fmt.Sprintf("a.%s", field)
}
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
tableName := tableNamePrefix + "user"
if offset == -1 {
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
} else {
if sortOrder == "ascend" {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Asc("a." + util.SnakeString(sortField))
} else {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Desc("a." + util.SnakeString(sortField))
}
}
return session
}

96
object/ormer_session.go Normal file
View File

@ -0,0 +1,96 @@
// Copyright 2023 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 object
import (
"fmt"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/xorm"
)
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
session = session.And("owner=?", owner)
}
if field != "" && value != "" {
if util.FilterField(field) {
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
return session
}
func GetSessionForUser(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
if offset == -1 {
session = session.And("owner=?", owner)
} else {
session = session.And("a.owner=?", owner)
}
}
if field != "" && value != "" {
if util.FilterField(field) {
if offset != -1 {
field = fmt.Sprintf("a.%s", field)
}
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
tableName := tableNamePrefix + "user"
if offset == -1 {
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
} else {
if sortOrder == "ascend" {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Asc("a." + util.SnakeString(sortField))
} else {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Desc("a." + util.SnakeString(sortField))
}
}
return session
}

View File

@ -58,7 +58,7 @@ type PermissionRule struct {
Id string `xorm:"varchar(100) index not null default ''" json:"id"` Id string `xorm:"varchar(100) index not null default ''" json:"id"`
} }
const builtInAvailableField = 10 const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
func (p *Permission) GetId() string { func (p *Permission) GetId() string {
return util.GetId(p.Owner, p.Name) return util.GetId(p.Owner, p.Name)

View File

@ -309,8 +309,7 @@ func GetAllRoles(userId string) []string {
func GetBuiltInModel(modelText string) (model.Model, error) { func GetBuiltInModel(modelText string) (model.Model, error) {
if modelText == "" { if modelText == "" {
modelText = ` modelText = `[request_definition]
[request_definition]
r = sub, obj, act r = sub, obj, act
[policy_definition] [policy_definition]

View File

@ -33,8 +33,8 @@ func getPermissionMap(owner string) (map[string]*Permission, error) {
return m, err return m, err
} }
func UploadPermissions(owner string, fileId string) (bool, error) { func UploadPermissions(owner string, path string) (bool, error) {
table := xlsx.ReadXlsxFile(fileId) table := xlsx.ReadXlsxFile(path)
oldUserMap, err := getPermissionMap(owner) oldUserMap, err := getPermissionMap(owner)
if err != nil { if err != nil {

View File

@ -28,15 +28,21 @@ type Plan struct {
DisplayName string `xorm:"varchar(100)" json:"displayName"` DisplayName string `xorm:"varchar(100)" json:"displayName"`
Description string `xorm:"varchar(100)" json:"description"` Description string `xorm:"varchar(100)" json:"description"`
PricePerMonth float64 `json:"pricePerMonth"` PricePerMonth float64 `json:"pricePerMonth"`
PricePerYear float64 `json:"pricePerYear"` PricePerYear float64 `json:"pricePerYear"`
Currency string `xorm:"varchar(100)" json:"currency"` Currency string `xorm:"varchar(100)" json:"currency"`
IsEnabled bool `json:"isEnabled"` Product string `json:"product"` // related product id
PaymentProviders []string `xorm:"varchar(100)" json:"paymentProviders"` // payment providers for related product
IsEnabled bool `json:"isEnabled"`
Role string `xorm:"varchar(100)" json:"role"` Role string `xorm:"varchar(100)" json:"role"`
Options []string `xorm:"-" json:"options"` Options []string `xorm:"-" json:"options"`
} }
func (plan *Plan) GetId() string {
return fmt.Sprintf("%s/%s", plan.Owner, plan.Name)
}
func GetPlanCount(owner, field, value string) (int64, error) { func GetPlanCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "") session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Plan{}) return session.Count(&Plan{})
@ -114,38 +120,3 @@ func DeletePlan(plan *Plan) (bool, error) {
} }
return affected != 0, nil return affected != 0, nil
} }
func (plan *Plan) GetId() string {
return fmt.Sprintf("%s/%s", plan.Owner, plan.Name)
}
func Subscribe(owner string, user string, plan string, pricing string) (*Subscription, error) {
selectedPricing, err := GetPricing(fmt.Sprintf("%s/%s", owner, pricing))
if err != nil {
return nil, err
}
valid := selectedPricing != nil && selectedPricing.IsEnabled
if !valid {
return nil, nil
}
planBelongToPricing, err := selectedPricing.HasPlan(owner, plan)
if err != nil {
return nil, err
}
if planBelongToPricing {
newSubscription := NewSubscription(owner, user, plan, selectedPricing.TrialDuration)
affected, err := AddSubscription(newSubscription)
if err != nil {
return nil, err
}
if affected {
return newSubscription, nil
}
}
return nil, nil
}

View File

@ -16,7 +16,6 @@ package object
import ( import (
"fmt" "fmt"
"strings"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/xorm-io/core" "github.com/xorm-io/core"
@ -41,6 +40,26 @@ type Pricing struct {
State string `xorm:"varchar(100)" json:"state"` State string `xorm:"varchar(100)" json:"state"`
} }
func (pricing *Pricing) GetId() string {
return fmt.Sprintf("%s/%s", pricing.Owner, pricing.Name)
}
func (pricing *Pricing) HasPlan(planName string) (bool, error) {
planId := util.GetId(pricing.Owner, planName)
plan, err := GetPlan(planId)
if err != nil {
return false, err
}
if plan == nil {
return false, fmt.Errorf("plan: %s does not exist", planId)
}
if util.InSlice(pricing.Plans, plan.Name) {
return true, nil
}
return false, nil
}
func GetPricingCount(owner, field, value string) (int64, error) { func GetPricingCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "") session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Pricing{}) return session.Count(&Pricing{})
@ -74,7 +93,7 @@ func getPricing(owner, name string) (*Pricing, error) {
pricing := Pricing{Owner: owner, Name: name} pricing := Pricing{Owner: owner, Name: name}
existed, err := ormer.Engine.Get(&pricing) existed, err := ormer.Engine.Get(&pricing)
if err != nil { if err != nil {
return &pricing, err return nil, err
} }
if existed { if existed {
return &pricing, nil return &pricing, nil
@ -88,6 +107,20 @@ func GetPricing(id string) (*Pricing, error) {
return getPricing(owner, name) return getPricing(owner, name)
} }
func GetApplicationDefaultPricing(owner, appName string) (*Pricing, error) {
pricings := make([]*Pricing, 0, 1)
err := ormer.Engine.Asc("created_time").Find(&pricings, &Pricing{Owner: owner, Application: appName})
if err != nil {
return nil, err
}
for _, pricing := range pricings {
if pricing.IsEnabled {
return pricing, nil
}
}
return nil, nil
}
func UpdatePricing(id string, pricing *Pricing) (bool, error) { func UpdatePricing(id string, pricing *Pricing) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id) owner, name := util.GetOwnerAndNameFromId(id)
if p, err := getPricing(owner, name); err != nil { if p, err := getPricing(owner, name); err != nil {
@ -120,28 +153,21 @@ func DeletePricing(pricing *Pricing) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func (pricing *Pricing) GetId() string { func CheckPricingAndPlan(owner, pricingName, planName string) error {
return fmt.Sprintf("%s/%s", pricing.Owner, pricing.Name) pricingId := util.GetId(owner, pricingName)
} pricing, err := GetPricing(pricingId)
if pricing == nil || err != nil {
func (pricing *Pricing) HasPlan(owner string, plan string) (bool, error) { if pricing == nil && err == nil {
selectedPlan, err := GetPlan(fmt.Sprintf("%s/%s", owner, plan)) err = fmt.Errorf("pricing: %s does not exist", pricingName)
if err != nil {
return false, err
}
if selectedPlan == nil {
return false, nil
}
result := false
for _, pricingPlan := range pricing.Plans {
if strings.Contains(pricingPlan, selectedPlan.Name) {
result = true
break
} }
return err
} }
ok, err := pricing.HasPlan(planName)
return result, nil if err != nil {
return err
}
if !ok {
return fmt.Errorf("pricing: %s does not have plan: %s", pricingName, planName)
}
return nil
} }

View File

@ -141,24 +141,24 @@ func (product *Product) isValidProvider(provider *Provider) bool {
return false return false
} }
func (product *Product) getProvider(providerId string) (*Provider, error) { func (product *Product) getProvider(providerName string) (*Provider, error) {
provider, err := getProvider(product.Owner, providerId) provider, err := getProvider(product.Owner, providerName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if provider == nil { if provider == nil {
return nil, fmt.Errorf("the payment provider: %s does not exist", providerId) return nil, fmt.Errorf("the payment provider: %s does not exist", providerName)
} }
if !product.isValidProvider(provider) { if !product.isValidProvider(provider) {
return nil, fmt.Errorf("the payment provider: %s is not valid for the product: %s", providerId, product.Name) return nil, fmt.Errorf("the payment provider: %s is not valid for the product: %s", providerName, product.Name)
} }
return provider, nil return provider, nil
} }
func BuyProduct(id string, providerName string, user *User, host string) (string, string, error) { func BuyProduct(id string, user *User, providerName, pricingName, planName, host string) (string, string, error) {
product, err := GetProduct(id) product, err := GetProduct(id)
if err != nil { if err != nil {
return "", "", err return "", "", err
@ -181,13 +181,24 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
owner := product.Owner owner := product.Owner
productName := product.Name productName := product.Name
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName) payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
paymentName := util.GenerateTimeId() paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
productDisplayName := product.DisplayName productDisplayName := product.DisplayName
originFrontend, originBackend := getOriginFromHost(host) originFrontend, originBackend := getOriginFromHost(host)
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName) returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s", originBackend, owner, paymentName) notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s", originBackend, owner, paymentName)
// Create an Order and get the payUrl if user.Type == "paid-user" {
// Create a subscription for `paid-user`
if pricingName != "" && planName != "" {
sub := NewSubscription(owner, user.Name, pricingName, planName, paymentName)
_, err := AddSubscription(sub)
if err != nil {
return "", "", err
}
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
}
}
// Create an OrderId and get the payUrl
payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl) payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
if err != nil { if err != nil {
return "", "", err return "", "", err
@ -228,7 +239,6 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
if !affected { if !affected {
return "", "", fmt.Errorf("failed to add payment: %s", util.StructToJson(payment)) return "", "", fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
} }
return payUrl, orderId, err return payUrl, orderId, err
} }
@ -252,3 +262,34 @@ func ExtendProductWithProviders(product *Product) error {
return nil return nil
} }
func CreateProductForPlan(plan *Plan) *Product {
product := &Product{
Owner: plan.Owner,
Name: fmt.Sprintf("product_%v", util.GetRandomName()),
DisplayName: fmt.Sprintf("Auto Created Product for Plan %v(%v)", plan.GetId(), plan.DisplayName),
CreatedTime: plan.CreatedTime,
Image: "https://cdn.casbin.org/img/casdoor-logo_1185x256.png", // TODO
Detail: fmt.Sprintf("This Product was auto created for Plan %v(%v)", plan.GetId(), plan.DisplayName),
Description: plan.Description,
Tag: "auto_created_product_for_plan",
Price: plan.PricePerMonth, // TODO
Currency: plan.Currency,
Quantity: 999,
Sold: 0,
Providers: plan.PaymentProviders,
State: "Published",
}
return product
}
func UpdateProductForPlan(plan *Plan, product *Product) {
product.DisplayName = fmt.Sprintf("Auto Created Product for Plan %v(%v)", plan.GetId(), plan.DisplayName)
product.Detail = fmt.Sprintf("This Product was auto created for Plan %v(%v)", plan.GetId(), plan.DisplayName)
product.Price = plan.PricePerMonth // TODO
product.Providers = plan.PaymentProviders
product.Currency = plan.Currency
}

View File

@ -36,7 +36,7 @@ type Provider struct {
Type string `xorm:"varchar(100)" json:"type"` Type string `xorm:"varchar(100)" json:"type"`
SubType string `xorm:"varchar(100)" json:"subType"` SubType string `xorm:"varchar(100)" json:"subType"`
Method string `xorm:"varchar(100)" json:"method"` Method string `xorm:"varchar(100)" json:"method"`
ClientId string `xorm:"varchar(100)" json:"clientId"` ClientId string `xorm:"varchar(200)" json:"clientId"`
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"` ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
ClientId2 string `xorm:"varchar(100)" json:"clientId2"` ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"` ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
@ -254,7 +254,8 @@ func DeleteProvider(provider *Provider) (bool, error) {
func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) { func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
cert := &Cert{} cert := &Cert{}
if p.Cert != "" { if p.Cert != "" {
cert, err := getCert(p.Owner, p.Cert) var err error
cert, err = getCert(p.Owner, p.Cert)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/beego/beego/context" "github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
) )
var logPostOnly bool var logPostOnly bool
@ -30,26 +31,10 @@ func init() {
} }
type Record struct { type Record struct {
Id int `xorm:"int notnull pk autoincr" json:"id"` casvisorsdk.Record
Owner string `xorm:"varchar(100) index" json:"owner"`
Name string `xorm:"varchar(100) index" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
Organization string `xorm:"varchar(100)" json:"organization"`
ClientIp string `xorm:"varchar(100)" json:"clientIp"`
User string `xorm:"varchar(100)" json:"user"`
Method string `xorm:"varchar(100)" json:"method"`
RequestUri string `xorm:"varchar(1000)" json:"requestUri"`
Action string `xorm:"varchar(1000)" json:"action"`
Object string `xorm:"-" json:"object"`
ExtendedUser *User `xorm:"-" json:"extendedUser"`
IsTriggered bool `json:"isTriggered"`
} }
func NewRecord(ctx *context.Context) *Record { func NewRecord(ctx *context.Context) *casvisorsdk.Record {
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1) ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1) action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"}) requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
@ -62,7 +47,7 @@ func NewRecord(ctx *context.Context) *Record {
object = string(ctx.Input.RequestBody) object = string(ctx.Input.RequestBody)
} }
record := Record{ record := casvisorsdk.Record{
Name: util.GenerateId(), Name: util.GenerateId(),
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
ClientIp: ip, ClientIp: ip,
@ -76,7 +61,7 @@ func NewRecord(ctx *context.Context) *Record {
return &record return &record
} }
func AddRecord(record *Record) bool { func AddRecord(record *casvisorsdk.Record) bool {
if logPostOnly { if logPostOnly {
if record.Method == "GET" { if record.Method == "GET" {
return false return false
@ -96,51 +81,19 @@ func AddRecord(record *Record) bool {
fmt.Println(errWebhook) fmt.Println(errWebhook)
} }
affected, err := ormer.Engine.Insert(record) if casvisorsdk.GetClient() == nil {
return false
}
affected, err := casvisorsdk.AddRecord(record)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return affected != 0 return affected
} }
func GetRecordCount(field, value string, filterRecord *Record) (int64, error) { func SendWebhooks(record *casvisorsdk.Record) error {
session := GetSession("", -1, -1, field, value, "", "")
return session.Count(filterRecord)
}
func GetRecords() ([]*Record, error) {
records := []*Record{}
err := ormer.Engine.Desc("id").Find(&records)
if err != nil {
return records, err
}
return records, nil
}
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *Record) ([]*Record, error) {
records := []*Record{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&records, filterRecord)
if err != nil {
return records, err
}
return records, nil
}
func GetRecordsByField(record *Record) ([]*Record, error) {
records := []*Record{}
err := ormer.Engine.Find(&records, record)
if err != nil {
return records, err
}
return records, nil
}
func SendWebhooks(record *Record) error {
webhooks, err := getWebhooksByOrganization(record.Organization) webhooks, err := getWebhooksByOrganization(record.Organization)
if err != nil { if err != nil {
return err return err
@ -160,17 +113,16 @@ func SendWebhooks(record *Record) error {
} }
if matched { if matched {
var user *User
if webhook.IsUserExtended { if webhook.IsUserExtended {
user, err := getUser(record.Organization, record.User) user, err = getUser(record.Organization, record.User)
user, err = GetMaskedUser(user, false, err) user, err = GetMaskedUser(user, false, err)
if err != nil { if err != nil {
return err return err
} }
record.ExtendedUser = user
} }
err := sendWebhook(webhook, record) err = sendWebhook(webhook, record, user)
if err != nil { if err != nil {
return err return err
} }

50
object/record_casvisor.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2023 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 object
import (
"strings"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
)
func getCasvisorApplication() *Application {
applications, err := GetApplications("admin")
if err != nil {
panic(err)
}
for _, application := range applications {
if strings.Contains(strings.ToLower(application.Name), "casvisor") {
return application
}
}
return nil
}
func InitCasvisorConfig() {
application := getCasvisorApplication()
if application == nil {
return
}
casvisorEndpoint := application.HomepageUrl
clientId := application.ClientId
clientSecret := application.ClientSecret
casdoorOrganization := application.Organization
casdoorApplication := application.Name
casvisorsdk.InitConfig(casvisorEndpoint, clientId, clientSecret, casdoorOrganization, casdoorApplication)
}

View File

@ -33,8 +33,8 @@ func getRoleMap(owner string) (map[string]*Role, error) {
return m, nil return m, nil
} }
func UploadRoles(owner string, fileId string) (bool, error) { func UploadRoles(owner string, path string) (bool, error) {
table := xlsx.ReadXlsxFile(fileId) table := xlsx.ReadXlsxFile(path)
oldUserMap, err := getRoleMap(owner) oldUserMap, err := getRoleMap(owner)
if err != nil { if err != nil {

View File

@ -31,8 +31,8 @@ import (
"github.com/RobotsAndPencils/go-saml" "github.com/RobotsAndPencils/go-saml"
"github.com/beevik/etree" "github.com/beevik/etree"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
dsig "github.com/russellhaering/goxmldsig" dsig "github.com/russellhaering/goxmldsig"
uuid "github.com/satori/go.uuid"
) )
// NewSamlResponse // NewSamlResponse
@ -46,7 +46,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
expireTime := time.Now().UTC().Add(time.Hour * 24).Format(time.RFC3339) expireTime := time.Now().UTC().Add(time.Hour * 24).Format(time.RFC3339)
samlResponse.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") samlResponse.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")
samlResponse.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") samlResponse.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion")
arId := uuid.NewV4() arId := uuid.New()
samlResponse.CreateAttr("ID", fmt.Sprintf("_%s", arId)) samlResponse.CreateAttr("ID", fmt.Sprintf("_%s", arId))
samlResponse.CreateAttr("Version", "2.0") samlResponse.CreateAttr("Version", "2.0")
@ -60,7 +60,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
assertion := samlResponse.CreateElement("saml:Assertion") assertion := samlResponse.CreateElement("saml:Assertion")
assertion.CreateAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") assertion.CreateAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
assertion.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema") assertion.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema")
assertion.CreateAttr("ID", fmt.Sprintf("_%s", uuid.NewV4())) assertion.CreateAttr("ID", fmt.Sprintf("_%s", uuid.New()))
assertion.CreateAttr("Version", "2.0") assertion.CreateAttr("Version", "2.0")
assertion.CreateAttr("IssueInstant", now) assertion.CreateAttr("IssueInstant", now)
assertion.CreateElement("saml:Issuer").SetText(host) assertion.CreateElement("saml:Issuer").SetText(host)
@ -82,7 +82,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
} }
authnStatement := assertion.CreateElement("saml:AuthnStatement") authnStatement := assertion.CreateElement("saml:AuthnStatement")
authnStatement.CreateAttr("AuthnInstant", now) authnStatement.CreateAttr("AuthnInstant", now)
authnStatement.CreateAttr("SessionIndex", fmt.Sprintf("_%s", uuid.NewV4())) authnStatement.CreateAttr("SessionIndex", fmt.Sprintf("_%s", uuid.New()))
authnStatement.CreateAttr("SessionNotOnOrAfter", expireTime) authnStatement.CreateAttr("SessionNotOnOrAfter", expireTime)
authnStatement.CreateElement("saml:AuthnContext").CreateElement("saml:AuthnContextClassRef").SetText("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport") authnStatement.CreateElement("saml:AuthnContext").CreateElement("saml:AuthnContextClassRef").SetText("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")
@ -355,7 +355,7 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
samlResponse.CreateAttr("MajorVersion", "1") samlResponse.CreateAttr("MajorVersion", "1")
samlResponse.CreateAttr("MinorVersion", "1") samlResponse.CreateAttr("MinorVersion", "1")
responseID := uuid.NewV4() responseID := uuid.New()
samlResponse.CreateAttr("ResponseID", fmt.Sprintf("_%s", responseID)) samlResponse.CreateAttr("ResponseID", fmt.Sprintf("_%s", responseID))
samlResponse.CreateAttr("InResponseTo", requestID) samlResponse.CreateAttr("InResponseTo", requestID)
@ -371,7 +371,7 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
assertion.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:1.0:assertion") assertion.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:1.0:assertion")
assertion.CreateAttr("MajorVersion", "1") assertion.CreateAttr("MajorVersion", "1")
assertion.CreateAttr("MinorVersion", "1") assertion.CreateAttr("MinorVersion", "1")
assertion.CreateAttr("AssertionID", uuid.NewV4().String()) assertion.CreateAttr("AssertionID", uuid.New().String())
assertion.CreateAttr("Issuer", host) assertion.CreateAttr("Issuer", host)
assertion.CreateAttr("IssueInstant", now) assertion.CreateAttr("IssueInstant", now)

View File

@ -18,47 +18,108 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/xorm-io/core" "github.com/xorm-io/core"
) )
const defaultStatus = "Pending" type SubscriptionState string
const (
SubStatePending SubscriptionState = "Pending"
SubStateError SubscriptionState = "Error"
SubStateSuspended SubscriptionState = "Suspended" // suspended by the admin
SubStateActive SubscriptionState = "Active"
SubStateUpcoming SubscriptionState = "Upcoming"
SubStateExpired SubscriptionState = "Expired"
)
type Subscription struct { type Subscription 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" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"` DisplayName string `xorm:"varchar(100)" json:"displayName"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
Description string `xorm:"varchar(100)" json:"description"`
StartDate time.Time `json:"startDate"` User string `xorm:"varchar(100)" json:"user"`
EndDate time.Time `json:"endDate"` Pricing string `xorm:"varchar(100)" json:"pricing"`
Duration int `json:"duration"` Plan string `xorm:"varchar(100)" json:"plan"`
Description string `xorm:"varchar(100)" json:"description"` Payment string `xorm:"varchar(100)" json:"payment"`
User string `xorm:"mediumtext" json:"user"` StartTime time.Time `json:"startTime"`
Plan string `xorm:"varchar(100)" json:"plan"` EndTime time.Time `json:"endTime"`
Duration int `json:"duration"`
IsEnabled bool `json:"isEnabled"` State SubscriptionState `xorm:"varchar(100)" json:"state"`
Submitter string `xorm:"varchar(100)" json:"submitter"`
Approver string `xorm:"varchar(100)" json:"approver"`
ApproveTime string `xorm:"varchar(100)" json:"approveTime"`
State string `xorm:"varchar(100)" json:"state"`
} }
func NewSubscription(owner string, user string, plan string, duration int) *Subscription { func (sub *Subscription) GetId() string {
return fmt.Sprintf("%s/%s", sub.Owner, sub.Name)
}
func (sub *Subscription) UpdateState() error {
preState := sub.State
// update subscription state by payment state
if sub.State == SubStatePending {
if sub.Payment == "" {
return nil
}
payment, err := GetPayment(util.GetId(sub.Owner, sub.Payment))
if err != nil {
return err
}
if payment == nil {
sub.Description = fmt.Sprintf("payment: %s does not exist", sub.Payment)
sub.State = SubStateError
} else {
if payment.State == pp.PaymentStatePaid {
sub.State = SubStateActive
} else if payment.State != pp.PaymentStateCreated {
// other states: Canceled, Timeout, Error
sub.Description = fmt.Sprintf("payment: %s state is %v", sub.Payment, payment.State)
sub.State = SubStateError
}
}
}
if sub.State == SubStateActive || sub.State == SubStateUpcoming || sub.State == SubStateExpired {
if sub.EndTime.Before(time.Now()) {
sub.State = SubStateExpired
} else if sub.StartTime.After(time.Now()) {
sub.State = SubStateUpcoming
} else {
sub.State = SubStateActive
}
}
if preState != sub.State {
_, err := UpdateSubscription(sub.GetId(), sub)
if err != nil {
return err
}
}
return nil
}
func NewSubscription(owner, userName, pricingName, planName, paymentName string) *Subscription {
id := util.GenerateId()[:6] id := util.GenerateId()[:6]
return &Subscription{ return &Subscription{
Name: "Subscription_" + id,
DisplayName: "New Subscription - " + id,
Owner: owner, Owner: owner,
User: owner + "/" + user, Name: "sub_" + id,
Plan: owner + "/" + plan, DisplayName: "New Subscription - " + id,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
State: defaultStatus,
Duration: duration, User: userName,
StartDate: time.Now(), Pricing: pricingName,
EndDate: time.Now().AddDate(0, 0, duration), Plan: planName,
Payment: paymentName,
StartTime: time.Now(),
EndTime: time.Now().AddDate(0, 0, 30),
Duration: 30, // TODO
State: SubStatePending, // waiting for payment complete
} }
} }
@ -73,7 +134,28 @@ func GetSubscriptions(owner string) ([]*Subscription, error) {
if err != nil { if err != nil {
return subscriptions, err return subscriptions, err
} }
for _, sub := range subscriptions {
err = sub.UpdateState()
if err != nil {
return nil, err
}
}
return subscriptions, nil
}
func GetSubscriptionsByUser(owner, userName string) ([]*Subscription, error) {
subscriptions := []*Subscription{}
err := ormer.Engine.Desc("created_time").Find(&subscriptions, &Subscription{Owner: owner, User: userName})
if err != nil {
return subscriptions, err
}
// update subscription state
for _, sub := range subscriptions {
err = sub.UpdateState()
if err != nil {
return subscriptions, err
}
}
return subscriptions, nil return subscriptions, nil
} }
@ -84,7 +166,12 @@ func GetPaginationSubscriptions(owner string, offset, limit int, field, value, s
if err != nil { if err != nil {
return subscriptions, err return subscriptions, err
} }
for _, sub := range subscriptions {
err = sub.UpdateState()
if err != nil {
return nil, err
}
}
return subscriptions, nil return subscriptions, nil
} }
@ -144,7 +231,3 @@ func DeleteSubscription(subscription *Subscription) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func (subscription *Subscription) GetId() string {
return fmt.Sprintf("%s/%s", subscription.Owner, subscription.Name)
}

View File

@ -25,6 +25,7 @@ type TableColumn struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
CasdoorName string `json:"casdoorName"` CasdoorName string `json:"casdoorName"`
IsKey bool `json:"isKey"`
IsHashed bool `json:"isHashed"` IsHashed bool `json:"isHashed"`
Values []string `json:"values"` Values []string `json:"values"`
} }
@ -44,7 +45,6 @@ type Syncer struct {
DatabaseType string `xorm:"varchar(100)" json:"databaseType"` DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
Database string `xorm:"varchar(100)" json:"database"` Database string `xorm:"varchar(100)" json:"database"`
Table string `xorm:"varchar(100)" json:"table"` Table string `xorm:"varchar(100)" json:"table"`
TablePrimaryKey string `xorm:"varchar(100)" json:"tablePrimaryKey"`
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"` TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
AffiliationTable string `xorm:"varchar(100)" json:"affiliationTable"` AffiliationTable string `xorm:"varchar(100)" json:"affiliationTable"`
AvatarBaseUrl string `xorm:"varchar(100)" json:"avatarBaseUrl"` AvatarBaseUrl string `xorm:"varchar(100)" json:"avatarBaseUrl"`
@ -229,6 +229,27 @@ func (syncer *Syncer) getTable() string {
} }
} }
func (syncer *Syncer) getKey() string {
key := "id"
hasKey := false
hasId := false
for _, tableColumn := range syncer.TableColumns {
if tableColumn.IsKey {
hasKey = true
key = tableColumn.Name
}
if tableColumn.Name == "id" {
hasId = true
}
}
if !hasKey && !hasId {
key = syncer.TableColumns[0].Name
}
return key
}
func RunSyncer(syncer *Syncer) { func RunSyncer(syncer *Syncer) {
syncer.initAdapter() syncer.initAdapter()
syncer.syncUsers() syncer.syncUsers()

View File

@ -20,16 +20,24 @@ import (
) )
func (syncer *Syncer) syncUsers() { func (syncer *Syncer) syncUsers() {
if len(syncer.TableColumns) == 0 {
return
}
fmt.Printf("Running syncUsers()..\n") fmt.Printf("Running syncUsers()..\n")
users, userMap, userNameMap := syncer.getUserMap() users, _, _ := syncer.getUserMap()
oUsers, oUserMap, err := syncer.getOriginalUserMap() oUsers, oUserMap, err := syncer.getOriginalUserMap()
if err != nil { if err != nil {
fmt.Printf(err.Error()) fmt.Printf(err.Error())
timestamp := time.Now().Format("2006-01-02 15:04:05") timestamp := time.Now().Format("2006-01-02 15:04:05")
line := fmt.Sprintf("[%s] %s\n", timestamp, err.Error()) line := fmt.Sprintf("[%s] %s\n", timestamp, err.Error())
updateSyncerErrorText(syncer, line) _, err = updateSyncerErrorText(syncer, line)
if err != nil {
panic(err)
}
return return
} }
@ -40,48 +48,68 @@ func (syncer *Syncer) syncUsers() {
_, affiliationMap, err = syncer.getAffiliationMap() _, affiliationMap, err = syncer.getAffiliationMap()
} }
key := syncer.getKey()
myUsers := map[string]*User{}
for _, m := range users {
myUsers[syncer.getUserValue(m, key)] = m
}
newUsers := []*User{} newUsers := []*User{}
for _, oUser := range oUsers { for _, oUser := range oUsers {
id := oUser.Id primary := syncer.getUserValue(oUser, key)
if _, ok := userMap[id]; !ok {
if _, ok := userNameMap[oUser.Name]; !ok {
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
fmt.Printf("New user: %v\n", newUser)
newUsers = append(newUsers, newUser)
}
} else {
user := userMap[id]
oHash := syncer.calculateHash(oUser)
if _, ok := myUsers[primary]; !ok {
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
fmt.Printf("New user: %v\n", newUser)
newUsers = append(newUsers, newUser)
} else {
user := myUsers[primary]
oHash := syncer.calculateHash(oUser)
if user.Hash == user.PreHash { if user.Hash == user.PreHash {
if user.Hash != oHash { if user.Hash != oHash {
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap) updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
updatedUser.Hash = oHash updatedUser.Hash = oHash
updatedUser.PreHash = oHash updatedUser.PreHash = oHash
syncer.updateUserForOriginalFields(updatedUser) _, err = syncer.updateUserForOriginalByFields(updatedUser, key)
if err != nil {
panic(err)
}
fmt.Printf("Update from oUser to user: %v\n", updatedUser) fmt.Printf("Update from oUser to user: %v\n", updatedUser)
} }
} else { } else {
if user.PreHash == oHash { if user.PreHash == oHash {
if !syncer.IsReadOnly { if !syncer.IsReadOnly {
updatedOUser := syncer.createOriginalUserFromUser(user) updatedOUser := syncer.createOriginalUserFromUser(user)
syncer.updateUser(updatedOUser) _, err = syncer.updateUser(updatedOUser)
if err != nil {
panic(err)
}
fmt.Printf("Update from user to oUser: %v\n", updatedOUser) fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
} }
// update preHash // update preHash
user.PreHash = user.Hash user.PreHash = user.Hash
SetUserField(user, "pre_hash", user.PreHash) _, err = SetUserField(user, "pre_hash", user.PreHash)
if err != nil {
panic(err)
}
} else { } else {
if user.Hash == oHash { if user.Hash == oHash {
// update preHash // update preHash
user.PreHash = user.Hash user.PreHash = user.Hash
SetUserField(user, "pre_hash", user.PreHash) _, err = SetUserField(user, "pre_hash", user.PreHash)
if err != nil {
panic(err)
}
} else { } else {
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap) updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
updatedUser.Hash = oHash updatedUser.Hash = oHash
updatedUser.PreHash = oHash updatedUser.PreHash = oHash
syncer.updateUserForOriginalFields(updatedUser) _, err = syncer.updateUserForOriginalByFields(updatedUser, key)
if err != nil {
panic(err)
}
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser) fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
} }
} }

View File

@ -80,16 +80,6 @@ func (syncer *Syncer) addUser(user *OriginalUser) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
/*func (syncer *Syncer) getOriginalColumns() []string {
res := []string{}
for _, tableColumn := range syncer.TableColumns {
if tableColumn.CasdoorName != "Id" {
res = append(res, tableColumn.Name)
}
}
return res
}*/
func (syncer *Syncer) getCasdoorColumns() []string { func (syncer *Syncer) getCasdoorColumns() []string {
res := []string{} res := []string{}
for _, tableColumn := range syncer.TableColumns { for _, tableColumn := range syncer.TableColumns {
@ -102,12 +92,14 @@ func (syncer *Syncer) getCasdoorColumns() []string {
} }
func (syncer *Syncer) updateUser(user *OriginalUser) (bool, error) { func (syncer *Syncer) updateUser(user *OriginalUser) (bool, error) {
key := syncer.getKey()
m := syncer.getMapFromOriginalUser(user) m := syncer.getMapFromOriginalUser(user)
pkValue := m[syncer.TablePrimaryKey] pkValue := m[key]
delete(m, syncer.TablePrimaryKey) delete(m, key)
setString := syncer.getSqlSetStringFromMap(m) setString := syncer.getSqlSetStringFromMap(m)
sql := fmt.Sprintf("update %s set %s where %s = %s", syncer.getTable(), setString, syncer.TablePrimaryKey, pkValue) sql := fmt.Sprintf("update %s set %s where %s = %s", syncer.getTable(), setString, key, pkValue)
res, err := syncer.Ormer.Engine.Exec(sql) res, err := syncer.Ormer.Engine.Exec(sql)
if err != nil { if err != nil {
return false, err return false, err
@ -142,6 +134,34 @@ func (syncer *Syncer) updateUserForOriginalFields(user *User) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
return affected != 0, nil
}
func (syncer *Syncer) updateUserForOriginalByFields(user *User, key string) (bool, error) {
var err error
oldUser := User{}
existed, err := ormer.Engine.Where(key+" = ? and owner = ?", syncer.getUserValue(user, key), user.Owner).Get(&oldUser)
if err != nil {
return false, err
}
if !existed {
return false, nil
}
if user.Avatar != oldUser.Avatar && user.Avatar != "" {
user.PermanentAvatar, err = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, true)
if err != nil {
return false, err
}
}
columns := syncer.getCasdoorColumns()
columns = append(columns, "affiliation", "hash", "pre_hash")
affected, err := ormer.Engine.Where(key+" = ? and owner = ?", syncer.getUserValue(&oldUser, key), oldUser.Owner).Cols(columns...).Update(user)
if err != nil {
return false, err
}
return affected != 0, nil return affected != 0, nil
} }

View File

@ -17,6 +17,7 @@ package object
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -153,14 +154,45 @@ func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) {
user.IsOnline = util.ParseBool(value) user.IsOnline = util.ParseBool(value)
case "IsAdmin": case "IsAdmin":
user.IsAdmin = util.ParseBool(value) user.IsAdmin = util.ParseBool(value)
case "IsGlobalAdmin":
user.IsGlobalAdmin = util.ParseBool(value)
case "IsForbidden": case "IsForbidden":
user.IsForbidden = util.ParseBool(value) user.IsForbidden = util.ParseBool(value)
case "IsDeleted": case "IsDeleted":
user.IsDeleted = util.ParseBool(value) user.IsDeleted = util.ParseBool(value)
case "CreatedIp": case "CreatedIp":
user.CreatedIp = value user.CreatedIp = value
case "PreferredMfaType":
user.PreferredMfaType = value
case "TotpSecret":
user.TotpSecret = value
case "SignupApplication":
user.SignupApplication = value
}
}
func (syncer *Syncer) getUserValue(user *User, key string) string {
jsonData, _ := json.Marshal(user)
var mapData map[string]interface{}
if err := json.Unmarshal(jsonData, &mapData); err != nil {
fmt.Println("conversion failed:", err)
return user.Id
}
value := mapData[util.SnakeToCamel(key)]
if str, ok := value.(string); ok {
return str
} else {
if value != nil {
valType := reflect.TypeOf(value)
typeName := valType.Name()
switch typeName {
case "bool":
return strconv.FormatBool(value.(bool))
case "int":
return strconv.Itoa(value.(int))
}
}
return user.Id
} }
} }
@ -261,10 +293,12 @@ func (syncer *Syncer) getMapFromOriginalUser(user *OriginalUser) map[string]stri
m["IsDefaultAvatar"] = util.BoolToString(user.IsDefaultAvatar) m["IsDefaultAvatar"] = util.BoolToString(user.IsDefaultAvatar)
m["IsOnline"] = util.BoolToString(user.IsOnline) m["IsOnline"] = util.BoolToString(user.IsOnline)
m["IsAdmin"] = util.BoolToString(user.IsAdmin) m["IsAdmin"] = util.BoolToString(user.IsAdmin)
m["IsGlobalAdmin"] = util.BoolToString(user.IsGlobalAdmin)
m["IsForbidden"] = util.BoolToString(user.IsForbidden) m["IsForbidden"] = util.BoolToString(user.IsForbidden)
m["IsDeleted"] = util.BoolToString(user.IsDeleted) m["IsDeleted"] = util.BoolToString(user.IsDeleted)
m["CreatedIp"] = user.CreatedIp m["CreatedIp"] = user.CreatedIp
m["PreferredMfaType"] = user.PreferredMfaType
m["TotpSecret"] = user.TotpSecret
m["SignupApplication"] = user.SignupApplication
m2 := map[string]string{} m2 := map[string]string{}
for _, tableColumn := range syncer.TableColumns { for _, tableColumn := range syncer.TableColumns {

View File

@ -824,7 +824,6 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
Type: "normal-user", Type: "normal-user",
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false, IsDeleted: false,
Properties: map[string]string{ Properties: map[string]string{

View File

@ -123,7 +123,7 @@ var stToServiceResponse sync.Map
// pgt is short for proxy granting ticket // pgt is short for proxy granting ticket
var pgtToServiceResponse sync.Map var pgtToServiceResponse sync.Map
func CheckCasRestrict(application *Application, lang string, service string) error { func CheckCasLogin(application *Application, lang string, service string) error {
if len(application.RedirectUris) > 0 && !application.IsRedirectUriValid(service) { if len(application.RedirectUris) > 0 && !application.IsRedirectUriValid(service) {
return fmt.Errorf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), service) return fmt.Errorf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), service)
} }

View File

@ -73,7 +73,6 @@ type UserWithoutThirdIdp struct {
IsDefaultAvatar bool `json:"isDefaultAvatar"` IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"` IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"` IsAdmin bool `json:"isAdmin"`
IsGlobalAdmin bool `json:"isGlobalAdmin"`
IsForbidden bool `json:"isForbidden"` IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"` IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"` SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
@ -154,7 +153,6 @@ func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp {
IsDefaultAvatar: user.IsDefaultAvatar, IsDefaultAvatar: user.IsDefaultAvatar,
IsOnline: user.IsOnline, IsOnline: user.IsOnline,
IsAdmin: user.IsAdmin, IsAdmin: user.IsAdmin,
IsGlobalAdmin: user.IsGlobalAdmin,
IsForbidden: user.IsForbidden, IsForbidden: user.IsForbidden,
IsDeleted: user.IsDeleted, IsDeleted: user.IsDeleted,
SignupApplication: user.SignupApplication, SignupApplication: user.SignupApplication,

View File

@ -29,6 +29,19 @@ const (
UserPropertiesWechatOpenId = "wechatOpenId" UserPropertiesWechatOpenId = "wechatOpenId"
) )
const UserEnforcerId = "built-in/user-enforcer-built-in"
var userEnforcer *UserGroupEnforcer
func InitUserManager() {
enforcer, err := GetInitializedEnforcer(UserEnforcerId)
if err != nil {
panic(err)
}
userEnforcer = NewUserGroupEnforcer(enforcer.Enforcer)
}
type User struct { type User 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" json:"name"`
@ -70,7 +83,6 @@ type User struct {
IsDefaultAvatar bool `json:"isDefaultAvatar"` IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"` IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"` IsAdmin bool `json:"isAdmin"`
IsGlobalAdmin bool `json:"isGlobalAdmin"`
IsForbidden bool `json:"isForbidden"` IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"` IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"` SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
@ -157,6 +169,7 @@ type User struct {
Yandex string `xorm:"yandex varchar(100)" json:"yandex"` Yandex string `xorm:"yandex varchar(100)" json:"yandex"`
Zoom string `xorm:"zoom varchar(100)" json:"zoom"` Zoom string `xorm:"zoom varchar(100)" json:"zoom"`
MetaMask string `xorm:"metamask varchar(100)" json:"metamask"` MetaMask string `xorm:"metamask varchar(100)" json:"metamask"`
Web3Onboard string `xorm:"web3onboard varchar(100)" json:"web3onboard"`
Custom string `xorm:"custom varchar(100)" json:"custom"` Custom string `xorm:"custom varchar(100)" json:"custom"`
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"` WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
@ -516,19 +529,26 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
columns = []string{ columns = []string{
"owner", "display_name", "avatar", "owner", "display_name", "avatar",
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application", "location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret", "signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs", "github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon", "baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox", "auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup", "eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud", "microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo", "spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom", "yammer", "yandex", "zoom", "custom",
} }
} }
if isAdmin { if isAdmin {
columns = append(columns, "name", "email", "phone", "country_code") columns = append(columns, "name", "email", "phone", "country_code", "type")
}
if util.ContainsString(columns, "groups") {
_, err := userEnforcer.UpdateGroupsForUser(user.GetId(), user.Groups)
if err != nil {
return false, err
}
} }
affected, err := updateUser(id, user, columns) affected, err := updateUser(id, user, columns)
@ -778,6 +798,10 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
return return
} }
func DeleteGroupForUser(user string, group string) (bool, error) {
return userEnforcer.DeleteGroupForUser(user, group)
}
func userChangeTrigger(oldName string, newName string) error { func userChangeTrigger(oldName string, newName string) error {
session := ormer.Engine.NewSession() session := ormer.Engine.NewSession()
defer session.Close() defer session.Close()
@ -866,5 +890,13 @@ func (user *User) IsApplicationAdmin(application *Application) bool {
return false return false
} }
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin()
}
func (user *User) IsGlobalAdmin() bool {
if user == nil {
return false
}
return user.Owner == "built-in"
} }

View File

@ -18,6 +18,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"mime"
"net/http" "net/http"
"strings" "strings"
@ -58,22 +59,15 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
if strings.Contains(contentType, "text/html") { if strings.Contains(contentType, "text/html") {
fileExtension = ".html" fileExtension = ".html"
} else { } else {
switch contentType { fileExtensions, err := mime.ExtensionsByType(contentType)
case "image/jpeg": if err != nil {
fileExtension = ".jpg" return nil, "", err
case "image/png":
fileExtension = ".png"
case "image/gif":
fileExtension = ".gif"
case "image/vnd.microsoft.icon":
fileExtension = ".ico"
case "image/x-icon":
fileExtension = ".ico"
case "image/svg+xml":
fileExtension = ".svg"
default:
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
} }
if fileExtensions == nil {
return nil, "", fmt.Errorf("fileExtensions is nil")
}
fileExtension = fileExtensions[0]
} }
// Save the image to a bytes.Buffer // Save the image to a bytes.Buffer

143
object/user_enforcer.go Normal file
View File

@ -0,0 +1,143 @@
package object
import (
"fmt"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/errors"
"github.com/casdoor/casdoor/util"
)
type UserGroupEnforcer struct {
// use rbac model implement use group, the enforcer can also implement user role
enforcer *casbin.Enforcer
}
func NewUserGroupEnforcer(enforcer *casbin.Enforcer) *UserGroupEnforcer {
return &UserGroupEnforcer{
enforcer: enforcer,
}
}
func (e *UserGroupEnforcer) checkModel() error {
if _, ok := e.enforcer.GetModel()["g"]; !ok {
return fmt.Errorf("The Casbin model used by enforcer doesn't support RBAC (\"[role_definition]\" section not found), please use a RBAC enabled Casbin model for the enforcer")
}
return nil
}
func (e *UserGroupEnforcer) AddGroupForUser(user string, group string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
return e.enforcer.AddRoleForUser(user, GetGroupWithPrefix(group))
}
func (e *UserGroupEnforcer) AddGroupsForUser(user string, groups []string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
g := make([]string, len(groups))
for i, group := range groups {
g[i] = GetGroupWithPrefix(group)
}
return e.enforcer.AddRolesForUser(user, g)
}
func (e *UserGroupEnforcer) DeleteGroupForUser(user string, group string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
return e.enforcer.DeleteRoleForUser(user, GetGroupWithPrefix(group))
}
func (e *UserGroupEnforcer) DeleteGroupsForUser(user string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
return e.enforcer.DeleteRolesForUser(user)
}
func (e *UserGroupEnforcer) GetGroupsForUser(user string) ([]string, error) {
err := e.checkModel()
if err != nil {
return nil, err
}
groups, err := e.enforcer.GetRolesForUser(user)
for i, group := range groups {
groups[i] = GetGroupWithoutPrefix(group)
}
return groups, err
}
func (e *UserGroupEnforcer) GetAllUsersByGroup(group string) ([]string, error) {
err := e.checkModel()
if err != nil {
return nil, err
}
users, err := e.enforcer.GetUsersForRole(GetGroupWithPrefix(group))
if err != nil {
if err == errors.ERR_NAME_NOT_FOUND {
return []string{}, nil
}
return nil, err
}
return users, nil
}
func GetGroupWithPrefix(group string) string {
return "group:" + group
}
func GetGroupWithoutPrefix(group string) string {
return group[len("group:"):]
}
func (e *UserGroupEnforcer) GetUserNamesByGroupName(groupName string) ([]string, error) {
err := e.checkModel()
if err != nil {
return nil, err
}
userIds, err := e.GetAllUsersByGroup(groupName)
if err != nil {
return nil, err
}
names := []string{}
for _, userId := range userIds {
_, name := util.GetOwnerAndNameFromIdNoCheck(userId)
names = append(names, name)
}
return names, nil
}
func (e *UserGroupEnforcer) UpdateGroupsForUser(user string, groups []string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
_, err = e.DeleteGroupsForUser(user)
if err != nil {
return false, err
}
affected, err := e.AddGroupsForUser(user, groups)
if err != nil {
return false, err
}
return affected, nil
}

View File

@ -73,8 +73,8 @@ func parseListItem(lines *[]string, i int) []string {
return trimmedItems return trimmedItems
} }
func UploadUsers(owner string, fileId string) (bool, error) { func UploadUsers(owner string, path string) (bool, error) {
table := xlsx.ReadXlsxFile(fileId) table := xlsx.ReadXlsxFile(path)
oldUserMap, err := getUserMap(owner) oldUserMap, err := getUserMap(owner)
if err != nil { if err != nil {
@ -124,15 +124,14 @@ func UploadUsers(owner string, fileId string) (bool, error) {
IsDefaultAvatar: false, IsDefaultAvatar: false,
IsOnline: parseLineItemBool(&line, 31), IsOnline: parseLineItemBool(&line, 31),
IsAdmin: parseLineItemBool(&line, 32), IsAdmin: parseLineItemBool(&line, 32),
IsGlobalAdmin: parseLineItemBool(&line, 33), IsForbidden: parseLineItemBool(&line, 33),
IsForbidden: parseLineItemBool(&line, 34), IsDeleted: parseLineItemBool(&line, 34),
IsDeleted: parseLineItemBool(&line, 35), SignupApplication: parseLineItem(&line, 35),
SignupApplication: parseLineItem(&line, 36),
Hash: "", Hash: "",
PreHash: "", PreHash: "",
CreatedIp: parseLineItem(&line, 37), CreatedIp: parseLineItem(&line, 36),
LastSigninTime: parseLineItem(&line, 38), LastSigninTime: parseLineItem(&line, 37),
LastSigninIp: parseLineItem(&line, 39), LastSigninIp: parseLineItem(&line, 38),
Ldap: "", Ldap: "",
Properties: map[string]string{}, Properties: map[string]string{},
} }

View File

@ -310,10 +310,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
item := GetAccountItemByName("Is admin", organization) item := GetAccountItemByName("Is admin", organization)
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)
} }
if oldUser.IsGlobalAdmin != newUser.IsGlobalAdmin {
item := GetAccountItemByName("Is global admin", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsForbidden != newUser.IsForbidden { if oldUser.IsForbidden != newUser.IsForbidden {
item := GetAccountItemByName("Is forbidden", organization) item := GetAccountItemByName("Is forbidden", organization)
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)
@ -351,5 +348,5 @@ func (user *User) IsAdminUser() bool {
return false return false
} }
return user.IsAdmin || user.IsGlobalAdmin return user.IsAdmin || user.IsGlobalAdmin()
} }

View File

@ -19,12 +19,22 @@ import (
"strings" "strings"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
) )
func sendWebhook(webhook *Webhook, record *Record) error { func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *User) error {
client := &http.Client{} client := &http.Client{}
body := strings.NewReader(util.StructToJson(record)) type RecordEx struct {
casvisorsdk.Record
ExtendedUser *User `xorm:"-" json:"extendedUser"`
}
recordEx := &RecordEx{
Record: *record,
ExtendedUser: extendedUser,
}
body := strings.NewReader(util.StructToJson(recordEx))
req, err := http.NewRequest(webhook.Method, webhook.Url, body) req, err := http.NewRequest(webhook.Method, webhook.Url, body)
if err != nil { if err != nil {

View File

@ -90,7 +90,7 @@ func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, auth
ProductName: productName, ProductName: productName,
ProductDisplayName: productDisplayName, ProductDisplayName: productDisplayName,
ProviderName: providerName, ProviderName: providerName,
OutOrderId: orderId, OrderId: orderId,
PaymentStatus: PaymentStatePaid, PaymentStatus: PaymentStatePaid,
Price: price, Price: price,
PaymentName: paymentName, PaymentName: paymentName,

View File

@ -255,7 +255,7 @@ func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorit
ProductName: productName, ProductName: productName,
ProductDisplayName: productDisplayName, ProductDisplayName: productDisplayName,
ProviderName: providerName, ProviderName: providerName,
OutOrderId: orderId, OrderId: orderId,
Price: price, Price: price,
PaymentStatus: PaymentStatePaid, PaymentStatus: PaymentStatePaid,
PaymentName: paymentName, PaymentName: paymentName,

View File

@ -52,13 +52,12 @@ func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentPro
func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) { func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
// https://github.com/go-pay/gopay/blob/main/doc/paypal.md // https://github.com/go-pay/gopay/blob/main/doc/paypal.md
priceStr := strconv.FormatFloat(price, 'f', 2, 64)
units := make([]*paypal.PurchaseUnit, 0, 1) units := make([]*paypal.PurchaseUnit, 0, 1)
unit := &paypal.PurchaseUnit{ unit := &paypal.PurchaseUnit{
ReferenceId: util.GetRandomString(16), ReferenceId: util.GetRandomString(16),
Amount: &paypal.Amount{ Amount: &paypal.Amount{
CurrencyCode: currency, // e.g."USD" CurrencyCode: currency, // e.g."USD"
Value: priceStr, // e.g."100.00" Value: priceFloat64ToString(price), // e.g."100.00"
}, },
Description: joinAttachString([]string{productDisplayName, productName, providerName}), Description: joinAttachString([]string{productDisplayName, productName, providerName}),
} }
@ -115,8 +114,8 @@ func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, auth
if err != nil { if err != nil {
return nil, err return nil, err
} }
if captureRsp.Code != paypal.Success { if detailRsp.Code != paypal.Success {
errDetail := captureRsp.ErrorResponse.Details[0] errDetail := detailRsp.ErrorResponse.Details[0]
switch errDetail.Issue { switch errDetail.Issue {
case "ORDER_NOT_APPROVED": case "ORDER_NOT_APPROVED":
notifyResult.PaymentStatus = PaymentStateCanceled notifyResult.PaymentStatus = PaymentStateCanceled
@ -147,16 +146,15 @@ func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, auth
paymentStatus = PaymentStateError paymentStatus = PaymentStateError
} }
notifyResult = &NotifyResult{ notifyResult = &NotifyResult{
PaymentStatus: paymentStatus, PaymentStatus: paymentStatus,
PaymentName: paymentName, PaymentName: paymentName,
ProductName: productName, ProductName: productName,
ProductDisplayName: productDisplayName, ProductDisplayName: productDisplayName,
ProviderName: providerName, ProviderName: providerName,
Price: price, Price: price,
Currency: currency, Currency: currency,
OutOrderId: orderId, OrderId: orderId,
} }
return notifyResult, nil return notifyResult, nil
} }

View File

@ -24,6 +24,7 @@ const (
PaymentStatePaid PaymentState = "Paid" PaymentStatePaid PaymentState = "Paid"
PaymentStateCreated PaymentState = "Created" PaymentStateCreated PaymentState = "Created"
PaymentStateCanceled PaymentState = "Canceled" PaymentStateCanceled PaymentState = "Canceled"
PaymentStateTimeout PaymentState = "Timeout"
PaymentStateError PaymentState = "Error" PaymentStateError PaymentState = "Error"
) )
@ -32,13 +33,13 @@ type NotifyResult struct {
PaymentStatus PaymentState PaymentStatus PaymentState
NotifyMessage string NotifyMessage string
ProviderName string
ProductName string ProductName string
ProductDisplayName string ProductDisplayName string
ProviderName string
Price float64 Price float64
Currency string Currency string
OutOrderId string OrderId string
} }
type PaymentProvider interface { type PaymentProvider interface {
@ -75,6 +76,12 @@ func GetPaymentProvider(typ string, clientId string, clientSecret string, host s
return nil, err return nil, err
} }
return pp, nil return pp, nil
} else if typ == "Stripe" {
pp, err := NewStripePaymentProvider(clientId, clientSecret)
if err != nil {
return nil, err
}
return pp, nil
} }
return nil, nil return nil, nil

166
pp/stripe.go Normal file
View File

@ -0,0 +1,166 @@
// Copyright 2023 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 pp
import (
"fmt"
"net/http"
"time"
"github.com/casdoor/casdoor/conf"
"github.com/stripe/stripe-go/v74"
stripeCheckout "github.com/stripe/stripe-go/v74/checkout/session"
stripeIntent "github.com/stripe/stripe-go/v74/paymentintent"
stripePrice "github.com/stripe/stripe-go/v74/price"
stripeProduct "github.com/stripe/stripe-go/v74/product"
)
type StripePaymentProvider struct {
PublishableKey string
SecretKey string
isProd bool
}
func NewStripePaymentProvider(PublishableKey, SecretKey string) (*StripePaymentProvider, error) {
isProd := false
if conf.GetConfigString("runmode") == "prod" {
isProd = true
}
pp := &StripePaymentProvider{
PublishableKey: PublishableKey,
SecretKey: SecretKey,
isProd: isProd,
}
stripe.Key = pp.SecretKey
return pp, nil
}
func (pp *StripePaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (payUrl string, orderId string, err error) {
// Create a temp product
description := joinAttachString([]string{productName, productDisplayName, providerName})
productParams := &stripe.ProductParams{
Name: stripe.String(productDisplayName),
Description: stripe.String(description),
DefaultPriceData: &stripe.ProductDefaultPriceDataParams{
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
Currency: stripe.String(currency),
},
}
sProduct, err := stripeProduct.New(productParams)
if err != nil {
return "", "", err
}
// Create a price for an existing product
priceParams := &stripe.PriceParams{
Currency: stripe.String(currency),
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
Product: stripe.String(sProduct.ID),
}
sPrice, err := stripePrice.New(priceParams)
if err != nil {
return "", "", err
}
// Create a Checkout Session
checkoutParams := &stripe.CheckoutSessionParams{
LineItems: []*stripe.CheckoutSessionLineItemParams{
{
Price: stripe.String(sPrice.ID),
Quantity: stripe.Int64(1),
},
},
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
SuccessURL: stripe.String(returnUrl),
CancelURL: stripe.String(returnUrl),
ClientReferenceID: stripe.String(paymentName),
ExpiresAt: stripe.Int64(time.Now().Add(30 * time.Minute).Unix()),
}
checkoutParams.AddMetadata("product_description", description)
sCheckout, err := stripeCheckout.New(checkoutParams)
if err != nil {
return "", "", err
}
return sCheckout.URL, sCheckout.ID, nil
}
func (pp *StripePaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
notifyResult := &NotifyResult{}
sCheckout, err := stripeCheckout.Get(orderId, nil)
if err != nil {
return nil, err
}
switch sCheckout.Status {
case "open":
// The checkout session is still in progress. Payment processing has not started
notifyResult.PaymentStatus = PaymentStateCreated
return notifyResult, nil
case "complete":
// The checkout session is complete. Payment processing may still be in progress
case "expired":
// The checkout session has expired. No further processing will occur
notifyResult.PaymentStatus = PaymentStateTimeout
return notifyResult, nil
default:
notifyResult.PaymentStatus = PaymentStateError
notifyResult.NotifyMessage = fmt.Sprintf("unexpected stripe checkout status: %v", sCheckout.Status)
return notifyResult, nil
}
switch sCheckout.PaymentStatus {
case "paid":
// Skip
case "unpaid":
notifyResult.PaymentStatus = PaymentStateCreated
return notifyResult, nil
default:
notifyResult.PaymentStatus = PaymentStateError
notifyResult.NotifyMessage = fmt.Sprintf("unexpected stripe checkout payment status: %v", sCheckout.PaymentStatus)
return notifyResult, nil
}
// Once payment is successful, the Checkout Session will contain a reference to the successful `PaymentIntent`
sIntent, err := stripeIntent.Get(sCheckout.PaymentIntent.ID, nil)
var (
productName string
productDisplayName string
providerName string
)
if description, ok := sCheckout.Metadata["product_description"]; ok {
productName, productDisplayName, providerName, _ = parseAttachString(description)
}
notifyResult = &NotifyResult{
PaymentName: sCheckout.ClientReferenceID,
PaymentStatus: PaymentStatePaid,
ProductName: productName,
ProductDisplayName: productDisplayName,
ProviderName: providerName,
Price: priceInt64ToFloat64(sIntent.Amount),
Currency: string(sIntent.Currency),
OrderId: orderId,
}
return notifyResult, nil
}
func (pp *StripePaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
return "", nil
}
func (pp *StripePaymentProvider) GetResponseError(err error) string {
if err == nil {
return "success"
} else {
return "fail"
}
}

View File

@ -16,6 +16,8 @@ package pp
import ( import (
"fmt" "fmt"
"math"
"strconv"
"strings" "strings"
) )
@ -35,3 +37,15 @@ func parseAttachString(s string) (string, string, string, error) {
} }
return tokens[0], tokens[1], tokens[2], nil return tokens[0], tokens[1], tokens[2], nil
} }
func priceInt64ToFloat64(price int64) float64 {
return float64(price) / 100
}
func priceFloat64ToInt64(price float64) int64 {
return int64(math.Round(price * 100))
}
func priceFloat64ToString(price float64) string {
return strconv.FormatFloat(price, 'f', 2, 64)
}

View File

@ -113,7 +113,7 @@ func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, auth
ProductName: productName, ProductName: productName,
ProductDisplayName: productDisplayName, ProductDisplayName: productDisplayName,
ProviderName: providerName, ProviderName: providerName,
OutOrderId: orderId, OrderId: orderId,
Price: price, Price: price,
PaymentStatus: PaymentStatePaid, PaymentStatus: PaymentStatePaid,
PaymentName: paymentName, PaymentName: paymentName,

View File

@ -51,6 +51,7 @@ func initAPI() {
beego.Router("/api/signup", &controllers.ApiController{}, "POST:Signup") beego.Router("/api/signup", &controllers.ApiController{}, "POST:Signup")
beego.Router("/api/login", &controllers.ApiController{}, "POST:Login") beego.Router("/api/login", &controllers.ApiController{}, "POST:Login")
beego.Router("/api/get-app-login", &controllers.ApiController{}, "GET:GetApplicationLogin") beego.Router("/api/get-app-login", &controllers.ApiController{}, "GET:GetApplicationLogin")
beego.Router("/api/get-dashboard", &controllers.ApiController{}, "GET:GetDashboard")
beego.Router("/api/logout", &controllers.ApiController{}, "GET,POST:Logout") beego.Router("/api/logout", &controllers.ApiController{}, "GET,POST:Logout")
beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount") beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount")
beego.Router("/api/userinfo", &controllers.ApiController{}, "GET:GetUserinfo") beego.Router("/api/userinfo", &controllers.ApiController{}, "GET:GetUserinfo")
@ -180,9 +181,6 @@ func initAPI() {
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken") beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken") beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken") beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter")
beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord")
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions") beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession") beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession")
@ -247,6 +245,7 @@ func initAPI() {
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail") beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms") beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
beego.Router("/api/send-notification", &controllers.ApiController{}, "POST:SendNotification")
beego.Router("/api/webauthn/signup/begin", &controllers.ApiController{}, "Get:WebAuthnSignupBegin") beego.Router("/api/webauthn/signup/begin", &controllers.ApiController{}, "Get:WebAuthnSignupBegin")
beego.Router("/api/webauthn/signup/finish", &controllers.ApiController{}, "Post:WebAuthnSignupFinish") beego.Router("/api/webauthn/signup/finish", &controllers.ApiController{}, "Post:WebAuthnSignupFinish")

View File

@ -55,12 +55,6 @@ func StaticFilter(ctx *context.Context) {
path += urlPath path += urlPath
} }
path2 := strings.TrimPrefix(path, "web/build/images/")
if util.FileExist(path2) {
makeGzipResponse(ctx.ResponseWriter, ctx.Request, path2)
return
}
if !util.FileExist(path) { if !util.FileExist(path) {
path = "web/build/index.html" path = "web/build/index.html"
} }

31
storage/google_cloud.go Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2023 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 (
"github.com/casdoor/oss"
"github.com/casdoor/oss/googlecloud"
)
func NewGoogleCloudStorageProvider(clientId string, clientSecret string, bucket string, endpoint string) oss.StorageInterface {
sp, _ := googlecloud.New(&googlecloud.Config{
AccessID: clientId,
AccessKey: clientSecret,
Bucket: bucket,
Endpoint: endpoint,
})
return sp
}

32
storage/qiniu_cloud.go Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2023 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 (
"github.com/casdoor/oss"
"github.com/casdoor/oss/qiniu"
)
func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
sp := qiniu.New(&qiniu.Config{
AccessID: clientId,
AccessKey: clientSecret,
Region: region,
Bucket: bucket,
Endpoint: endpoint,
})
return sp
}

View File

@ -30,6 +30,10 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint) return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Azure Blob": case "Azure Blob":
return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint) return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Qiniu Cloud Kodo":
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Google Cloud Storage":
return NewGoogleCloudStorageProvider(clientId, clientSecret, bucket, endpoint)
} }
return nil return nil

View File

@ -131,34 +131,6 @@
} }
} }
}, },
"/api/add-chat": {
"post": {
"tags": [
"Chat API"
],
"description": "add chat",
"operationId": "ApiController.AddChat",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the chat",
"required": true,
"schema": {
"$ref": "#/definitions/object.Chat"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-enforcer": { "/api/add-enforcer": {
"post": { "post": {
"tags": [ "tags": [
@ -243,34 +215,6 @@
} }
} }
}, },
"/api/add-message": {
"post": {
"tags": [
"Message API"
],
"description": "add message",
"operationId": "ApiController.AddMessage",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the message",
"required": true,
"schema": {
"$ref": "#/definitions/object.Message"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-model": { "/api/add-model": {
"post": { "post": {
"tags": [ "tags": [
@ -1077,34 +1021,6 @@
} }
} }
}, },
"/api/delete-chat": {
"post": {
"tags": [
"Chat API"
],
"description": "delete chat",
"operationId": "ApiController.DeleteChat",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the chat",
"required": true,
"schema": {
"$ref": "#/definitions/object.Chat"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/delete-enforcer": { "/api/delete-enforcer": {
"post": { "post": {
"tags": [ "tags": [
@ -1189,34 +1105,6 @@
} }
} }
}, },
"/api/delete-message": {
"post": {
"tags": [
"Message API"
],
"description": "delete message",
"operationId": "ApiController.DeleteMessage",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the message",
"required": true,
"schema": {
"$ref": "#/definitions/object.Message"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/delete-mfa/": { "/api/delete-mfa/": {
"post": { "post": {
"tags": [ "tags": [
@ -1964,56 +1852,18 @@
} }
} }
}, },
"/api/get-chat": { "/api/get-dashboard": {
"get": { "get": {
"tags": [ "tags": [
"Chat API" "GetDashboard API"
],
"description": "get chat",
"operationId": "ApiController.GetChat",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the chat",
"required": true,
"type": "string"
}
], ],
"description": "get information of dashboard",
"operationId": "ApiController.GetDashboard",
"responses": { "responses": {
"200": { "200": {
"description": "The Response object", "description": "The Response object",
"schema": { "schema": {
"$ref": "#/definitions/object.Chat" "$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/get-chats": {
"get": {
"tags": [
"Chat API"
],
"description": "get chats",
"operationId": "ApiController.GetChats",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "The owner of chats",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Chat"
}
} }
} }
} }
@ -2319,87 +2169,6 @@
} }
} }
}, },
"/api/get-message": {
"get": {
"tags": [
"Message API"
],
"description": "get message",
"operationId": "ApiController.GetMessage",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the message",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.Message"
}
}
}
}
},
"/api/get-message-answer": {
"get": {
"tags": [
"Message API"
],
"description": "get message answer",
"operationId": "ApiController.GetMessageAnswer",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the message",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.Message"
}
}
}
}
},
"/api/get-messages": {
"get": {
"tags": [
"Message API"
],
"description": "get messages",
"operationId": "ApiController.GetMessages",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "The owner of messages",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Message"
}
}
}
}
}
},
"/api/get-model": { "/api/get-model": {
"get": { "get": {
"tags": [ "tags": [
@ -4481,41 +4250,6 @@
} }
} }
}, },
"/api/update-chat": {
"post": {
"tags": [
"Chat API"
],
"description": "update chat",
"operationId": "ApiController.UpdateChat",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the chat",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The details of the chat",
"required": true,
"schema": {
"$ref": "#/definitions/object.Chat"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/update-enforcer": { "/api/update-enforcer": {
"post": { "post": {
"tags": [ "tags": [
@ -4614,41 +4348,6 @@
} }
} }
}, },
"/api/update-message": {
"post": {
"tags": [
"Message API"
],
"description": "update message",
"operationId": "ApiController.UpdateMessage",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the message",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The details of the message",
"required": true,
"schema": {
"$ref": "#/definitions/object.Message"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/update-model": { "/api/update-model": {
"post": { "post": {
"tags": [ "tags": [
@ -5431,6 +5130,14 @@
} }
}, },
"definitions": { "definitions": {
"1183.0xc000639290.false": {
"title": "false",
"type": "object"
},
"1217.0xc0006392c0.false": {
"title": "false",
"type": "object"
},
"LaravelResponse": { "LaravelResponse": {
"title": "LaravelResponse", "title": "LaravelResponse",
"type": "object" "type": "object"
@ -5484,16 +5191,10 @@
"type": "object", "type": "object",
"properties": { "properties": {
"data": { "data": {
"additionalProperties": { "$ref": "#/definitions/1183.0xc000639290.false"
"description": "support string, struct or []struct",
"type": "string"
}
}, },
"data2": { "data2": {
"additionalProperties": { "$ref": "#/definitions/1217.0xc0006392c0.false"
"description": "support string, struct or []struct",
"type": "string"
}
}, },
"msg": { "msg": {
"type": "string" "type": "string"
@ -5531,10 +5232,18 @@
"title": "JSONWebKey", "title": "JSONWebKey",
"type": "object" "type": "object"
}, },
"model.Model": {
"title": "Model",
"type": "object"
},
"object": { "object": {
"title": "object", "title": "object",
"type": "object" "type": "object"
}, },
"object.\u0026{197049 0xc000a2cd50 false}": {
"title": "\u0026{197049 0xc000a2cd50 false}",
"type": "object"
},
"object.AccountItem": { "object.AccountItem": {
"title": "AccountItem", "title": "AccountItem",
"type": "object", "type": "object",
@ -5557,16 +5266,41 @@
"title": "Adapter", "title": "Adapter",
"type": "object", "type": "object",
"properties": { "properties": {
"Engine": { "createdTime": {
"$ref": "#/definitions/xorm.Engine"
},
"dataSourceName": {
"type": "string" "type": "string"
}, },
"dbName": { "database": {
"type": "string" "type": "string"
}, },
"driverName": { "databaseType": {
"type": "string"
},
"host": {
"type": "string"
},
"name": {
"type": "string"
},
"owner": {
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"type": "integer",
"format": "int64"
},
"table": {
"type": "string"
},
"tableNamePrefix": {
"type": "string"
},
"type": {
"type": "string"
},
"user": {
"type": "string" "type": "string"
} }
} }
@ -5728,7 +5462,7 @@
"title": "CasbinRequest", "title": "CasbinRequest",
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/object.CasbinRequest" "$ref": "#/definitions/object.\u0026{197049 0xc000a2cd50 false}"
} }
}, },
"object.Cert": { "object.Cert": {
@ -5778,52 +5512,6 @@
} }
} }
}, },
"object.Chat": {
"title": "Chat",
"type": "object",
"properties": {
"category": {
"type": "string"
},
"createdTime": {
"type": "string"
},
"displayName": {
"type": "string"
},
"messageCount": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"organization": {
"type": "string"
},
"owner": {
"type": "string"
},
"type": {
"type": "string"
},
"updatedTime": {
"type": "string"
},
"user1": {
"type": "string"
},
"user2": {
"type": "string"
},
"users": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"object.Enforce": { "object.Enforce": {
"title": "Enforce", "title": "Enforce",
"type": "object" "type": "object"
@ -5844,12 +5532,14 @@
"displayName": { "displayName": {
"type": "string" "type": "string"
}, },
"isEnabled": {
"type": "boolean"
},
"model": { "model": {
"type": "string" "type": "string"
}, },
"modelCfg": {
"additionalProperties": {
"type": "string"
}
},
"name": { "name": {
"type": "string" "type": "string"
}, },
@ -6084,36 +5774,6 @@
} }
} }
}, },
"object.Message": {
"title": "Message",
"type": "object",
"properties": {
"author": {
"type": "string"
},
"chat": {
"type": "string"
},
"createdTime": {
"type": "string"
},
"name": {
"type": "string"
},
"organization": {
"type": "string"
},
"owner": {
"type": "string"
},
"replyTo": {
"type": "string"
},
"text": {
"type": "string"
}
}
},
"object.MfaItem": { "object.MfaItem": {
"title": "MfaItem", "title": "MfaItem",
"type": "object", "type": "object",
@ -6169,9 +5829,6 @@
"displayName": { "displayName": {
"type": "string" "type": "string"
}, },
"isEnabled": {
"type": "boolean"
},
"modelText": { "modelText": {
"type": "string" "type": "string"
}, },
@ -6349,6 +6006,24 @@
} }
} }
}, },
"object.Ormer": {
"title": "Ormer",
"type": "object",
"properties": {
"Engine": {
"$ref": "#/definitions/xorm.Engine"
},
"dataSourceName": {
"type": "string"
},
"dbName": {
"type": "string"
},
"driverName": {
"type": "string"
}
}
},
"object.Payment": { "object.Payment": {
"title": "Payment", "title": "Payment",
"type": "object", "type": "object",
@ -6386,7 +6061,7 @@
"name": { "name": {
"type": "string" "type": "string"
}, },
"organization": { "outOrderId": {
"type": "string" "type": "string"
}, },
"owner": { "owner": {
@ -6424,7 +6099,7 @@
"type": "string" "type": "string"
}, },
"state": { "state": {
"type": "string" "$ref": "#/definitions/pp.PaymentState"
}, },
"tag": { "tag": {
"type": "string" "type": "string"
@ -7124,9 +6799,6 @@
"$ref": "#/definitions/object.TableColumn" "$ref": "#/definitions/object.TableColumn"
} }
}, },
"tablePrimaryKey": {
"type": "string"
},
"type": { "type": {
"type": "string" "type": "string"
}, },
@ -7145,6 +6817,9 @@
"isHashed": { "isHashed": {
"type": "boolean" "type": "boolean"
}, },
"isKey": {
"type": "boolean"
},
"name": { "name": {
"type": "string" "type": "string"
}, },
@ -7464,9 +7139,6 @@
"isForbidden": { "isForbidden": {
"type": "boolean" "type": "boolean"
}, },
"isGlobalAdmin": {
"type": "boolean"
},
"isOnline": { "isOnline": {
"type": "boolean" "type": "boolean"
}, },
@ -7692,6 +7364,9 @@
"vk": { "vk": {
"type": "string" "type": "string"
}, },
"web3onboard": {
"type": "string"
},
"webauthnCredentials": { "webauthnCredentials": {
"type": "array", "type": "array",
"items": { "items": {
@ -7811,6 +7486,18 @@
} }
} }
}, },
"pp.PaymentState": {
"title": "PaymentState",
"type": "string",
"enum": [
"PaymentStatePaid = \"Paid\"",
"PaymentStateCreated = \"Created\"",
"PaymentStateCanceled = \"Canceled\"",
"PaymentStateTimeout = \"Timeout\"",
"PaymentStateError = \"Error\""
],
"example": "Paid"
},
"protocol.CredentialAssertion": { "protocol.CredentialAssertion": {
"title": "CredentialAssertion", "title": "CredentialAssertion",
"type": "object" "type": "object"
@ -7871,6 +7558,10 @@
"xorm.Engine": { "xorm.Engine": {
"title": "Engine", "title": "Engine",
"type": "object" "type": "object"
},
"xormadapter.Adapter": {
"title": "Adapter",
"type": "object"
} }
}, },
"securityDefinitions": { "securityDefinitions": {
@ -7879,9 +7570,5 @@
"name": "Authorization", "name": "Authorization",
"in": "header" "in": "header"
} }
},
"externalDocs": {
"description": "Find out more about Casdoor",
"url": "https://casdoor.org/"
} }
} }

View File

@ -85,24 +85,6 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/add-chat:
post:
tags:
- Chat API
description: add chat
operationId: ApiController.AddChat
parameters:
- in: body
name: body
description: The details of the chat
required: true
schema:
$ref: '#/definitions/object.Chat'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-enforcer: /api/add-enforcer:
post: post:
tags: tags:
@ -157,24 +139,6 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/add-message:
post:
tags:
- Message API
description: add message
operationId: ApiController.AddMessage
parameters:
- in: body
name: body
description: The details of the message
required: true
schema:
$ref: '#/definitions/object.Message'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-model: /api/add-model:
post: post:
tags: tags:
@ -696,24 +660,6 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/delete-chat:
post:
tags:
- Chat API
description: delete chat
operationId: ApiController.DeleteChat
parameters:
- in: body
name: body
description: The details of the chat
required: true
schema:
$ref: '#/definitions/object.Chat'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-enforcer: /api/delete-enforcer:
post: post:
tags: tags:
@ -768,24 +714,6 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/delete-message:
post:
tags:
- Message API
description: delete message
operationId: ApiController.DeleteMessage
parameters:
- in: body
name: body
description: The details of the message
required: true
schema:
$ref: '#/definitions/object.Message'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-mfa/: /api/delete-mfa/:
post: post:
tags: tags:
@ -1271,42 +1199,17 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/object.Cert' $ref: '#/definitions/object.Cert'
/api/get-chat: /api/get-dashboard:
get: get:
tags: tags:
- Chat API - GetDashboard API
description: get chat description: get information of dashboard
operationId: ApiController.GetChat operationId: ApiController.GetDashboard
parameters:
- in: query
name: id
description: The id ( owner/name ) of the chat
required: true
type: string
responses: responses:
"200": "200":
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/object.Chat' $ref: '#/definitions/controllers.Response'
/api/get-chats:
get:
tags:
- Chat API
description: get chats
operationId: ApiController.GetChats
parameters:
- in: query
name: owner
description: The owner of chats
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Chat'
/api/get-default-application: /api/get-default-application:
get: get:
tags: tags:
@ -1503,59 +1406,6 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/object.Ldap' $ref: '#/definitions/object.Ldap'
/api/get-message:
get:
tags:
- Message API
description: get message
operationId: ApiController.GetMessage
parameters:
- in: query
name: id
description: The id ( owner/name ) of the message
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.Message'
/api/get-message-answer:
get:
tags:
- Message API
description: get message answer
operationId: ApiController.GetMessageAnswer
parameters:
- in: query
name: id
description: The id ( owner/name ) of the message
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.Message'
/api/get-messages:
get:
tags:
- Message API
description: get messages
operationId: ApiController.GetMessages
parameters:
- in: query
name: owner
description: The owner of messages
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Message'
/api/get-model: /api/get-model:
get: get:
tags: tags:
@ -2925,29 +2775,6 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/update-chat:
post:
tags:
- Chat API
description: update chat
operationId: ApiController.UpdateChat
parameters:
- in: query
name: id
description: The id ( owner/name ) of the chat
required: true
type: string
- in: body
name: body
description: The details of the chat
required: true
schema:
$ref: '#/definitions/object.Chat'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-enforcer: /api/update-enforcer:
post: post:
tags: tags:
@ -3012,29 +2839,6 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/update-message:
post:
tags:
- Message API
description: update message
operationId: ApiController.UpdateMessage
parameters:
- in: query
name: id
description: The id ( owner/name ) of the message
required: true
type: string
- in: body
name: body
description: The details of the message
required: true
schema:
$ref: '#/definitions/object.Message'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-model: /api/update-model:
post: post:
tags: tags:
@ -3549,6 +3353,12 @@ paths:
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
definitions: definitions:
1183.0xc000639290.false:
title: "false"
type: object
1217.0xc0006392c0.false:
title: "false"
type: object
LaravelResponse: LaravelResponse:
title: LaravelResponse title: LaravelResponse
type: object type: object
@ -3588,13 +3398,9 @@ definitions:
type: object type: object
properties: properties:
data: data:
additionalProperties: $ref: '#/definitions/1183.0xc000639290.false'
description: support string, struct or []struct
type: string
data2: data2:
additionalProperties: $ref: '#/definitions/1217.0xc0006392c0.false'
description: support string, struct or []struct
type: string
msg: msg:
type: string type: string
name: name:
@ -3618,9 +3424,15 @@ definitions:
jose.JSONWebKey: jose.JSONWebKey:
title: JSONWebKey title: JSONWebKey
type: object type: object
model.Model:
title: Model
type: object
object: object:
title: object title: object
type: object type: object
object.&{197049 0xc000a2cd50 false}:
title: '&{197049 0xc000a2cd50 false}'
type: object
object.AccountItem: object.AccountItem:
title: AccountItem title: AccountItem
type: object type: object
@ -3637,13 +3449,30 @@ definitions:
title: Adapter title: Adapter
type: object type: object
properties: properties:
Engine: createdTime:
$ref: '#/definitions/xorm.Engine'
dataSourceName:
type: string type: string
dbName: database:
type: string type: string
driverName: databaseType:
type: string
host:
type: string
name:
type: string
owner:
type: string
password:
type: string
port:
type: integer
format: int64
table:
type: string
tableNamePrefix:
type: string
type:
type: string
user:
type: string type: string
object.Application: object.Application:
title: Application title: Application
@ -3752,7 +3581,7 @@ definitions:
title: CasbinRequest title: CasbinRequest
type: array type: array
items: items:
$ref: '#/definitions/object.CasbinRequest' $ref: '#/definitions/object.&{197049 0xc000a2cd50 false}'
object.Cert: object.Cert:
title: Cert title: Cert
type: object type: object
@ -3785,37 +3614,6 @@ definitions:
type: string type: string
type: type:
type: string type: string
object.Chat:
title: Chat
type: object
properties:
category:
type: string
createdTime:
type: string
displayName:
type: string
messageCount:
type: integer
format: int64
name:
type: string
organization:
type: string
owner:
type: string
type:
type: string
updatedTime:
type: string
user1:
type: string
user2:
type: string
users:
type: array
items:
type: string
object.Enforce: object.Enforce:
title: Enforce title: Enforce
type: object type: object
@ -3831,10 +3629,11 @@ definitions:
type: string type: string
displayName: displayName:
type: string type: string
isEnabled:
type: boolean
model: model:
type: string type: string
modelCfg:
additionalProperties:
type: string
name: name:
type: string type: string
owner: owner:
@ -3992,26 +3791,6 @@ definitions:
type: string type: string
username: username:
type: string type: string
object.Message:
title: Message
type: object
properties:
author:
type: string
chat:
type: string
createdTime:
type: string
name:
type: string
organization:
type: string
owner:
type: string
replyTo:
type: string
text:
type: string
object.MfaItem: object.MfaItem:
title: MfaItem title: MfaItem
type: object type: object
@ -4050,8 +3829,6 @@ definitions:
type: string type: string
displayName: displayName:
type: string type: string
isEnabled:
type: boolean
modelText: modelText:
type: string type: string
name: name:
@ -4169,6 +3946,18 @@ definitions:
$ref: '#/definitions/object.ThemeData' $ref: '#/definitions/object.ThemeData'
websiteUrl: websiteUrl:
type: string type: string
object.Ormer:
title: Ormer
type: object
properties:
Engine:
$ref: '#/definitions/xorm.Engine'
dataSourceName:
type: string
dbName:
type: string
driverName:
type: string
object.Payment: object.Payment:
title: Payment title: Payment
type: object type: object
@ -4195,7 +3984,7 @@ definitions:
type: string type: string
name: name:
type: string type: string
organization: outOrderId:
type: string type: string
owner: owner:
type: string type: string
@ -4221,7 +4010,7 @@ definitions:
returnUrl: returnUrl:
type: string type: string
state: state:
type: string $ref: '#/definitions/pp.PaymentState'
tag: tag:
type: string type: string
type: type:
@ -4692,8 +4481,6 @@ definitions:
type: array type: array
items: items:
$ref: '#/definitions/object.TableColumn' $ref: '#/definitions/object.TableColumn'
tablePrimaryKey:
type: string
type: type:
type: string type: string
user: user:
@ -4706,6 +4493,8 @@ definitions:
type: string type: string
isHashed: isHashed:
type: boolean type: boolean
isKey:
type: boolean
name: name:
type: string type: string
type: type:
@ -4920,8 +4709,6 @@ definitions:
type: boolean type: boolean
isForbidden: isForbidden:
type: boolean type: boolean
isGlobalAdmin:
type: boolean
isOnline: isOnline:
type: boolean type: boolean
kakao: kakao:
@ -5073,6 +4860,8 @@ definitions:
type: string type: string
vk: vk:
type: string type: string
web3onboard:
type: string
webauthnCredentials: webauthnCredentials:
type: array type: array
items: items:
@ -5151,6 +4940,16 @@ definitions:
type: string type: string
url: url:
type: string type: string
pp.PaymentState:
title: PaymentState
type: string
enum:
- PaymentStatePaid = "Paid"
- PaymentStateCreated = "Created"
- PaymentStateCanceled = "Canceled"
- PaymentStateTimeout = "Timeout"
- PaymentStateError = "Error"
example: Paid
protocol.CredentialAssertion: protocol.CredentialAssertion:
title: CredentialAssertion title: CredentialAssertion
type: object type: object
@ -5195,11 +4994,11 @@ definitions:
xorm.Engine: xorm.Engine:
title: Engine title: Engine
type: object type: object
xormadapter.Adapter:
title: Adapter
type: object
securityDefinitions: securityDefinitions:
AccessToken: AccessToken:
type: apiKey type: apiKey
name: Authorization name: Authorization
in: header in: header
externalDocs:
description: Find out more about Casdoor
url: https://casdoor.org/

63
util/casbin.go Normal file
View File

@ -0,0 +1,63 @@
// 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/casdoor/xorm-adapter/v3"
func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
s := []string{
casbinRule.V0,
casbinRule.V1,
casbinRule.V2,
casbinRule.V3,
casbinRule.V4,
casbinRule.V5,
}
// remove empty strings from end, for update model policy map
for i := len(s) - 1; i >= 0; i-- {
if s[i] != "" {
s = s[:i+1]
break
}
}
return s
}
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
}

View File

@ -34,16 +34,6 @@ func GetPath(path string) string {
return filepath.Dir(path) return filepath.Dir(path)
} }
func EnsureFileFolderExists(path string) {
p := GetPath(path)
if !FileExist(p) {
err := os.MkdirAll(p, os.ModePerm)
if err != nil {
panic(err)
}
}
}
func ListFiles(path string) []string { func ListFiles(path string) []string {
res := []string{} res := []string{}

View File

@ -14,8 +14,13 @@
package util package util
import "fmt" import "io/ioutil"
func GetUploadXlsxPath(fileId string) string { func GetUploadXlsxPath(fileId string) string {
return fmt.Sprintf("tmpFiles/%s.xlsx", fileId) file, err := ioutil.TempFile("", fileId)
if err != nil {
panic(err)
}
return file.Name()
} }

Some files were not shown because too many files have changed in this diff Show More