mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-17 04:03:23 +08:00
Compare commits
75 Commits
Author | SHA1 | Date | |
---|---|---|---|
7f298efebc | |||
0fc48bb6cd | |||
c3b3840994 | |||
eacc3fae5a | |||
ce7a2e924b | |||
ece060d03d | |||
1276da4daa | |||
616629ef99 | |||
b633ecdcf2 | |||
a12ba7fb85 | |||
08a0092974 | |||
bb04b10e8b | |||
ea1414dfd0 | |||
32a8a028d5 | |||
0fe34c2f53 | |||
dc57c476b7 | |||
a7cb202ee9 | |||
e5e264628e | |||
8d4127f744 | |||
1305899060 | |||
411a85c7ab | |||
f39358e122 | |||
a84752bbb5 | |||
e9d8ab8cdb | |||
d12088e8e7 | |||
c62588f9bc | |||
16cd09d175 | |||
7318ee6e3a | |||
3459ef1479 | |||
ca6b27f922 | |||
e528e8883b | |||
b7cd604e56 | |||
3c2fd574a6 | |||
a9de7d3aef | |||
9820801634 | |||
c6e422c3a8 | |||
bc8e9cfd64 | |||
c1eae9fcd8 | |||
6dae6e4954 | |||
559a91e8ee | |||
b0aaf09ef1 | |||
7e2f67c49a | |||
e584a6a111 | |||
6700d2e244 | |||
0c5c308071 | |||
0b859197da | |||
3078409343 | |||
bbf2db2e00 | |||
0c7b911ce7 | |||
2cc55715ac | |||
c829bf1769 | |||
ec956c12ca | |||
d3d4646c56 | |||
669ac7c618 | |||
6715efd781 | |||
953be4a7b6 | |||
943cc43427 | |||
1e5ce7a045 | |||
7a85b74573 | |||
7e349c1768 | |||
b19be2df88 | |||
fc3866db1c | |||
bf2bb31e41 | |||
ec8bd6f01d | |||
98722fd681 | |||
221c55aa93 | |||
988b26b3c2 | |||
7e3c361ce7 | |||
a637707e77 | |||
7970edeaa7 | |||
9da2f0775f | |||
739a9bcd0d | |||
fb0949b9ed | |||
27ed901167 | |||
ceab662b88 |
@ -64,7 +64,6 @@ COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
|
|||||||
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
|
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
|
||||||
COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
|
COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
|
||||||
COPY --from=FRONT /web/build ./web/build
|
COPY --from=FRONT /web/build ./web/build
|
||||||
RUN mkdir tempFiles
|
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/bash"]
|
ENTRYPOINT ["/bin/bash"]
|
||||||
CMD ["/docker-entrypoint.sh"]
|
CMD ["/docker-entrypoint.sh"]
|
||||||
|
@ -46,6 +46,7 @@ p, *, *, POST, /api/login, *, *
|
|||||||
p, *, *, GET, /api/get-app-login, *, *
|
p, *, *, GET, /api/get-app-login, *, *
|
||||||
p, *, *, POST, /api/logout, *, *
|
p, *, *, POST, /api/logout, *, *
|
||||||
p, *, *, GET, /api/logout, *, *
|
p, *, *, GET, /api/logout, *, *
|
||||||
|
p, *, *, POST, /api/callback, *, *
|
||||||
p, *, *, GET, /api/get-account, *, *
|
p, *, *, GET, /api/get-account, *, *
|
||||||
p, *, *, GET, /api/userinfo, *, *
|
p, *, *, GET, /api/userinfo, *, *
|
||||||
p, *, *, GET, /api/user, *, *
|
p, *, *, GET, /api/user, *, *
|
||||||
@ -88,6 +89,7 @@ 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-subscription, *, *
|
||||||
|
p, *, *, GET, /api/get-provider, *, *
|
||||||
p, *, *, GET, /api/get-organization-names, *, *
|
p, *, *, GET, /api/get-organization-names, *, *
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -120,6 +122,10 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if subOwner == "app" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if user != nil && user.IsAdmin && (subOwner == objOwner || (objOwner == "admin")) {
|
if user != nil && user.IsAdmin && (subOwner == objOwner || (objOwner == "admin")) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -140,13 +140,6 @@ func (c *ApiController) Signup() {
|
|||||||
username = id
|
username = id
|
||||||
}
|
}
|
||||||
|
|
||||||
password := authForm.Password
|
|
||||||
msg = object.CheckPasswordComplexityByOrg(organization, password)
|
|
||||||
if msg != "" {
|
|
||||||
c.ResponseError(msg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
initScore, err := organization.GetInitScore()
|
initScore, err := organization.GetInitScore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
||||||
|
@ -62,13 +62,13 @@ func (c *ApiController) GetApplications() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
app, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
application, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
applications := object.GetMaskedApplications(app, userId)
|
applications := object.GetMaskedApplications(application, userId)
|
||||||
c.ResponseOk(applications, paginator.Nums())
|
c.ResponseOk(applications, paginator.Nums())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,13 +84,33 @@ func (c *ApiController) GetApplication() {
|
|||||||
userId := c.GetSessionUsername()
|
userId := c.GetSessionUsername()
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
app, err := object.GetApplication(id)
|
application, err := object.GetApplication(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetMaskedApplication(app, userId))
|
if c.Input().Get("withKey") != "" && application != nil && application.Cert != "" {
|
||||||
|
cert, err := object.GetCert(util.GetId(application.Owner, application.Cert))
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert == nil {
|
||||||
|
cert, err = object.GetCert(util.GetId(application.Organization, application.Cert))
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert != nil {
|
||||||
|
application.CertPublicKey = cert.Certificate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(object.GetMaskedApplication(application, userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserApplication
|
// GetUserApplication
|
||||||
@ -164,13 +184,13 @@ func (c *ApiController) GetOrganizationApplications() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
app, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
application, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
applications := object.GetMaskedApplications(app, userId)
|
applications := object.GetMaskedApplications(application, userId)
|
||||||
c.ResponseOk(applications, paginator.Nums())
|
c.ResponseOk(applications, paginator.Nums())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -59,7 +60,7 @@ func tokenToResponse(token *object.Token) *Response {
|
|||||||
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) {
|
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) {
|
||||||
userId := user.GetId()
|
userId := user.GetId()
|
||||||
|
|
||||||
allowed, err := object.CheckAccessPermission(userId, application)
|
allowed, err := object.CheckLoginPermission(userId, application)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error(), nil)
|
c.ResponseError(err.Error(), nil)
|
||||||
return
|
return
|
||||||
@ -896,3 +897,16 @@ func (c *ApiController) GetCaptchaStatus() {
|
|||||||
}
|
}
|
||||||
c.ResponseOk(captchaEnabled)
|
c.ResponseOk(captchaEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callback
|
||||||
|
// @Title Callback
|
||||||
|
// @Tag Callback API
|
||||||
|
// @Description Get Login Error Counts
|
||||||
|
// @router /api/Callback [post]
|
||||||
|
func (c *ApiController) Callback() {
|
||||||
|
code := c.GetString("code")
|
||||||
|
state := c.GetString("state")
|
||||||
|
|
||||||
|
frontendCallbackUrl := fmt.Sprintf("/callback?code=%s&state=%s", code, state)
|
||||||
|
c.Ctx.Redirect(http.StatusFound, frontendCallbackUrl)
|
||||||
|
}
|
||||||
|
@ -61,6 +61,10 @@ func (c *ApiController) IsAdminOrSelf(user2 *object.User) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user == nil || user2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if user.Owner == user2.Owner && user.Name == user2.Name {
|
if user.Owner == user2.Owner && user.Name == user2.Name {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,11 @@ const (
|
|||||||
UnauthorizedService string = "UNAUTHORIZED_SERVICE"
|
UnauthorizedService string = "UNAUTHORIZED_SERVICE"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func queryUnescape(service string) string {
|
||||||
|
s, _ := url.QueryUnescape(service)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func (c *RootController) CasValidate() {
|
func (c *RootController) CasValidate() {
|
||||||
ticket := c.Input().Get("ticket")
|
ticket := c.Input().Get("ticket")
|
||||||
service := c.Input().Get("service")
|
service := c.Input().Get("service")
|
||||||
@ -60,24 +65,25 @@ func (c *RootController) CasServiceValidate() {
|
|||||||
if !strings.HasPrefix(ticket, "ST") {
|
if !strings.HasPrefix(ticket, "ST") {
|
||||||
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
|
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
|
||||||
}
|
}
|
||||||
c.CasP3ServiceAndProxyValidate()
|
c.CasP3ProxyValidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RootController) CasProxyValidate() {
|
func (c *RootController) CasProxyValidate() {
|
||||||
|
// https://apereo.github.io/cas/6.6.x/protocol/CAS-Protocol-Specification.html#26-proxyvalidate-cas-20
|
||||||
|
// "/proxyValidate" should accept both service tickets and proxy tickets.
|
||||||
|
c.CasP3ProxyValidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RootController) CasP3ServiceValidate() {
|
||||||
ticket := c.Input().Get("ticket")
|
ticket := c.Input().Get("ticket")
|
||||||
format := c.Input().Get("format")
|
format := c.Input().Get("format")
|
||||||
if !strings.HasPrefix(ticket, "PT") {
|
if !strings.HasPrefix(ticket, "ST") {
|
||||||
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
|
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
|
||||||
}
|
}
|
||||||
c.CasP3ServiceAndProxyValidate()
|
c.CasP3ProxyValidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryUnescape(service string) string {
|
func (c *RootController) CasP3ProxyValidate() {
|
||||||
s, _ := url.QueryUnescape(service)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RootController) CasP3ServiceAndProxyValidate() {
|
|
||||||
ticket := c.Input().Get("ticket")
|
ticket := c.Input().Get("ticket")
|
||||||
format := c.Input().Get("format")
|
format := c.Input().Get("format")
|
||||||
service := c.Input().Get("service")
|
service := c.Input().Get("service")
|
||||||
@ -115,15 +121,17 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
|
|||||||
pgtiou := serviceResponse.Success.ProxyGrantingTicket
|
pgtiou := serviceResponse.Success.ProxyGrantingTicket
|
||||||
// todo: check whether it is https
|
// todo: check whether it is https
|
||||||
pgtUrlObj, err := url.Parse(pgtUrl)
|
pgtUrlObj, err := url.Parse(pgtUrl)
|
||||||
|
if err != nil {
|
||||||
|
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, err.Error(), format)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if pgtUrlObj.Scheme != "https" {
|
if pgtUrlObj.Scheme != "https" {
|
||||||
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, "callback is not https", format)
|
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, "callback is not https", format)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a request to pgturl passing pgt and pgtiou
|
// make a request to pgturl passing pgt and pgtiou
|
||||||
if err != nil {
|
|
||||||
c.sendCasAuthenticationResponseErr(InternalError, err.Error(), format)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
param := pgtUrlObj.Query()
|
param := pgtUrlObj.Query()
|
||||||
param.Add("pgtId", pgt)
|
param.Add("pgtId", pgt)
|
||||||
param.Add("pgtIou", pgtiou)
|
param.Add("pgtIou", pgtiou)
|
||||||
@ -263,7 +271,6 @@ func (c *RootController) sendCasAuthenticationResponseErr(code, msg, format stri
|
|||||||
Message: msg,
|
Message: msg,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == "json" {
|
if format == "json" {
|
||||||
c.Data["json"] = serviceResponse
|
c.Data["json"] = serviceResponse
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
|
@ -83,7 +83,7 @@ func (c *ApiController) GetEnforcer() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if loadModelCfg == "true" {
|
if loadModelCfg == "true" && enforcer.Model != "" {
|
||||||
err := enforcer.LoadModelCfg()
|
err := enforcer.LoadModelCfg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -176,11 +176,10 @@ func (c *ApiController) DeletePayment() {
|
|||||||
func (c *ApiController) NotifyPayment() {
|
func (c *ApiController) NotifyPayment() {
|
||||||
owner := c.Ctx.Input.Param(":owner")
|
owner := c.Ctx.Input.Param(":owner")
|
||||||
paymentName := c.Ctx.Input.Param(":payment")
|
paymentName := c.Ctx.Input.Param(":payment")
|
||||||
orderId := c.Ctx.Input.Param("order")
|
|
||||||
|
|
||||||
body := c.Ctx.Input.RequestBody
|
body := c.Ctx.Input.RequestBody
|
||||||
|
|
||||||
payment, err := object.NotifyPayment(c.Ctx.Request, body, owner, paymentName, orderId)
|
payment, err := object.NotifyPayment(body, owner, paymentName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@ -114,7 +114,7 @@ func (c *ApiController) GetPlan() {
|
|||||||
// @router /update-plan [post]
|
// @router /update-plan [post]
|
||||||
func (c *ApiController) UpdatePlan() {
|
func (c *ApiController) UpdatePlan() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
owner := util.GetOwnerFromId(id)
|
||||||
var plan object.Plan
|
var plan object.Plan
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &plan)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &plan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -122,17 +122,19 @@ func (c *ApiController) UpdatePlan() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if plan.Product != "" {
|
if plan.Product != "" {
|
||||||
planId := util.GetId(plan.Owner, plan.Product)
|
productId := util.GetId(owner, plan.Product)
|
||||||
product, err := object.GetProduct(planId)
|
product, err := object.GetProduct(productId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
object.UpdateProductForPlan(&plan, product)
|
if product != nil {
|
||||||
_, err = object.UpdateProduct(planId, product)
|
object.UpdateProductForPlan(&plan, product)
|
||||||
if err != nil {
|
_, err = object.UpdateProduct(productId, product)
|
||||||
c.ResponseError(err.Error())
|
if err != nil {
|
||||||
return
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.Data["json"] = wrapActionResponse(object.UpdatePlan(id, &plan))
|
c.Data["json"] = wrapActionResponse(object.UpdatePlan(id, &plan))
|
||||||
|
@ -187,11 +187,11 @@ func (c *ApiController) BuyProduct() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
payUrl, orderId, err := object.BuyProduct(id, user, providerName, pricingName, planName, host)
|
payment, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(payUrl, orderId)
|
c.ResponseOk(payment)
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,11 @@ func (c *ApiController) RunSyncer() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
object.RunSyncer(syncer)
|
err = object.RunSyncer(syncer)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.ResponseOk()
|
c.ResponseOk()
|
||||||
}
|
}
|
||||||
|
@ -47,19 +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 != "" {
|
||||||
|
c.ResponseOk(versionInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
versionInfo, err = util.GetVersionInfoFromFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if versionInfo.Version == "" {
|
|
||||||
versionInfo, err = util.GetVersionInfoFromFile()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
c.ResponseError(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ResponseOk(versionInfo)
|
c.ResponseOk(versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ func (c *ApiController) DeleteToken() {
|
|||||||
// @Success 200 {object} object.TokenWrapper The Response object
|
// @Success 200 {object} object.TokenWrapper The Response object
|
||||||
// @Success 400 {object} object.TokenError The Response object
|
// @Success 400 {object} object.TokenError The Response object
|
||||||
// @Success 401 {object} object.TokenError The Response object
|
// @Success 401 {object} object.TokenError The Response object
|
||||||
// @router /login/oauth/access_token [post]
|
// @router api/login/oauth/access_token [post]
|
||||||
func (c *ApiController) GetOAuthToken() {
|
func (c *ApiController) GetOAuthToken() {
|
||||||
grantType := c.Input().Get("grant_type")
|
grantType := c.Input().Get("grant_type")
|
||||||
refreshToken := c.Input().Get("refresh_token")
|
refreshToken := c.Input().Get("refresh_token")
|
||||||
|
@ -258,6 +258,13 @@ func (c *ApiController) UpdateUser() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Input().Get("allowEmpty") == "" {
|
||||||
|
if user.DisplayName == "" {
|
||||||
|
c.ResponseError(c.T("user:Display name cannot be empty"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" {
|
if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(msg)
|
||||||
return
|
return
|
||||||
@ -441,12 +448,25 @@ func (c *ApiController) SetPassword() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetUser, err := object.GetUser(userId)
|
targetUser, err := object.GetUser(userId)
|
||||||
|
if targetUser == nil {
|
||||||
|
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldPassword != "" {
|
isAdmin := c.IsAdmin()
|
||||||
|
if isAdmin {
|
||||||
|
if oldPassword != "" {
|
||||||
|
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||||
|
if msg != "" {
|
||||||
|
c.ResponseError(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(msg)
|
||||||
|
227
email/azure_acs.go
Normal file
227
email/azure_acs.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
// 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 email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
importanceNormal = "normal"
|
||||||
|
sendEmailEndpoint = "/emails:send"
|
||||||
|
apiVersion = "2023-03-31"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Email struct {
|
||||||
|
Recipients Recipients `json:"recipients"`
|
||||||
|
SenderAddress string `json:"senderAddress"`
|
||||||
|
Content Content `json:"content"`
|
||||||
|
Headers []CustomHeader `json:"headers"`
|
||||||
|
Tracking bool `json:"disableUserEngagementTracking"`
|
||||||
|
Importance string `json:"importance"`
|
||||||
|
ReplyTo []EmailAddress `json:"replyTo"`
|
||||||
|
Attachments []Attachment `json:"attachments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Recipients struct {
|
||||||
|
To []EmailAddress `json:"to"`
|
||||||
|
CC []EmailAddress `json:"cc"`
|
||||||
|
BCC []EmailAddress `json:"bcc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmailAddress struct {
|
||||||
|
DisplayName string `json:"displayName"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Content struct {
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
HTML string `json:"html"`
|
||||||
|
PlainText string `json:"plainText"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomHeader struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attachment struct {
|
||||||
|
Content string `json:"contentBytesBase64"`
|
||||||
|
AttachmentType string `json:"attachmentType"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Error CommunicationError `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommunicationError contains the error code and message
|
||||||
|
type CommunicationError struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AzureACSEmailProvider struct {
|
||||||
|
AccessKey string
|
||||||
|
Endpoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAzureACSEmailProvider(accessKey string, endpoint string) *AzureACSEmailProvider {
|
||||||
|
return &AzureACSEmailProvider{
|
||||||
|
AccessKey: accessKey,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEmail(fromAddress string, toAddress string, subject string, content string) *Email {
|
||||||
|
return &Email{
|
||||||
|
Recipients: Recipients{
|
||||||
|
To: []EmailAddress{
|
||||||
|
{
|
||||||
|
DisplayName: toAddress,
|
||||||
|
Address: toAddress,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SenderAddress: fromAddress,
|
||||||
|
Content: Content{
|
||||||
|
Subject: subject,
|
||||||
|
HTML: content,
|
||||||
|
},
|
||||||
|
Importance: importanceNormal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AzureACSEmailProvider) sendEmail(e *Email) error {
|
||||||
|
postBody, err := json.Marshal(e)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("email JSON marshall failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyBuffer := bytes.NewBuffer(postBody)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", a.Endpoint+sendEmailEndpoint+"?api-version="+apiVersion, bodyBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating AzureACS API request: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign the request using the AzureACS access key and HMAC-SHA256
|
||||||
|
err = signRequestHMAC(a.AccessKey, req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error signing AzureACS API request: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// Some important header
|
||||||
|
req.Header.Set("repeatability-request-id", uuid.New().String())
|
||||||
|
req.Header.Set("repeatability-first-sent", time.Now().UTC().Format(http.TimeFormat))
|
||||||
|
|
||||||
|
// Send request
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error sending AzureACS API request: %s", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Response error Handling
|
||||||
|
if resp.StatusCode == http.StatusBadRequest {
|
||||||
|
commError := ErrorResponse{}
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&commError)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("error sending email: %s", commError.Error.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusAccepted {
|
||||||
|
return fmt.Errorf("error sending email: status: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func signRequestHMAC(secret string, req *http.Request) error {
|
||||||
|
method := req.Method
|
||||||
|
host := req.URL.Host
|
||||||
|
pathAndQuery := req.URL.Path
|
||||||
|
|
||||||
|
if req.URL.RawQuery != "" {
|
||||||
|
pathAndQuery = pathAndQuery + "?" + req.URL.RawQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
var content []byte
|
||||||
|
var err error
|
||||||
|
if req.Body != nil {
|
||||||
|
content, err = io.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
// return err
|
||||||
|
content = []byte{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Body = io.NopCloser(bytes.NewBuffer(content))
|
||||||
|
|
||||||
|
key, err := base64.StdEncoding.DecodeString(secret)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error decoding secret: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := time.Now().UTC().Format(http.TimeFormat)
|
||||||
|
contentHash := GetContentHashBase64(content)
|
||||||
|
stringToSign := fmt.Sprintf("%s\n%s\n%s;%s;%s", strings.ToUpper(method), pathAndQuery, timestamp, host, contentHash)
|
||||||
|
signature := GetHmac(stringToSign, key)
|
||||||
|
|
||||||
|
req.Header.Set("x-ms-content-sha256", contentHash)
|
||||||
|
req.Header.Set("x-ms-date", timestamp)
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature="+signature)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetContentHashBase64(content []byte) string {
|
||||||
|
hasher := sha256.New()
|
||||||
|
hasher.Write(content)
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHmac(content string, key []byte) string {
|
||||||
|
hmac := hmac.New(sha256.New, key)
|
||||||
|
hmac.Write([]byte(content))
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(hmac.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AzureACSEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||||
|
e := newEmail(fromAddress, toAddress, subject, content)
|
||||||
|
|
||||||
|
return a.sendEmail(e)
|
||||||
|
}
|
27
email/provider.go
Normal file
27
email/provider.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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 email
|
||||||
|
|
||||||
|
type EmailProvider interface {
|
||||||
|
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEmailProvider(typ string, clientId string, clientSecret string, appId string, host string, port int, disableSsl bool) EmailProvider {
|
||||||
|
if typ == "Azure ACS" {
|
||||||
|
return NewAzureACSEmailProvider(appId, host)
|
||||||
|
} else {
|
||||||
|
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
||||||
|
}
|
||||||
|
}
|
49
email/smtp.go
Normal file
49
email/smtp.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// 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 email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/casdoor/gomail/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmtpEmailProvider struct {
|
||||||
|
Dialer *gomail.Dialer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSmtpEmailProvider(userName string, password string, host string, port int, typ string, disableSsl bool) *SmtpEmailProvider {
|
||||||
|
dialer := &gomail.Dialer{}
|
||||||
|
dialer = gomail.NewDialer(host, port, userName, password)
|
||||||
|
if typ == "SUBMAIL" {
|
||||||
|
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer.SSL = !disableSsl
|
||||||
|
|
||||||
|
return &SmtpEmailProvider{Dialer: dialer}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmtpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||||
|
message := gomail.NewMessage()
|
||||||
|
|
||||||
|
message.SetAddressHeader("From", fromAddress, fromName)
|
||||||
|
message.SetHeader("To", toAddress)
|
||||||
|
message.SetHeader("Subject", subject)
|
||||||
|
message.SetBody("text/html", content)
|
||||||
|
|
||||||
|
message.SkipUsernameCheck = true
|
||||||
|
return s.Dialer.DialAndSend(message)
|
||||||
|
}
|
19
go.mod
19
go.mod
@ -6,14 +6,14 @@ require (
|
|||||||
github.com/Masterminds/squirrel v1.5.3
|
github.com/Masterminds/squirrel v1.5.3
|
||||||
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
|
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
|
||||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.188 // indirect
|
github.com/aws/aws-sdk-go v1.45.5
|
||||||
github.com/aws/aws-sdk-go v1.44.4
|
|
||||||
github.com/beego/beego v1.12.12
|
github.com/beego/beego v1.12.12
|
||||||
github.com/beevik/etree v1.1.0
|
github.com/beevik/etree v1.1.0
|
||||||
github.com/casbin/casbin v1.9.1 // indirect
|
github.com/casbin/casbin v1.9.1 // indirect
|
||||||
github.com/casbin/casbin/v2 v2.30.1
|
github.com/casbin/casbin/v2 v2.37.0
|
||||||
github.com/casdoor/go-sms-sender v0.12.0
|
github.com/casdoor/go-sms-sender v0.14.0
|
||||||
github.com/casdoor/gomail/v2 v2.0.1
|
github.com/casdoor/gomail/v2 v2.0.1
|
||||||
|
github.com/casdoor/notify v0.43.0
|
||||||
github.com/casdoor/oss v1.3.0
|
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/casvisor/casvisor-go-sdk v1.0.3
|
||||||
@ -30,14 +30,13 @@ require (
|
|||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
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/google/uuid v1.3.0
|
github.com/google/uuid v1.3.1
|
||||||
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.9
|
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
|
||||||
@ -60,10 +59,12 @@ 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.11.0
|
golang.org/x/crypto v0.12.0
|
||||||
golang.org/x/net v0.13.0
|
golang.org/x/net v0.14.0
|
||||||
golang.org/x/oauth2 v0.10.0
|
golang.org/x/oauth2 v0.11.0
|
||||||
|
google.golang.org/api v0.138.0
|
||||||
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
|
||||||
|
maunium.net/go/mautrix v0.16.0
|
||||||
modernc.org/sqlite v1.18.2
|
modernc.org/sqlite v1.18.2
|
||||||
)
|
)
|
||||||
|
@ -38,7 +38,13 @@ func TestGenerateI18nFrontend(t *testing.T) {
|
|||||||
applyToOtherLanguage("frontend", "tr", data)
|
applyToOtherLanguage("frontend", "tr", data)
|
||||||
applyToOtherLanguage("frontend", "ar", data)
|
applyToOtherLanguage("frontend", "ar", data)
|
||||||
applyToOtherLanguage("frontend", "he", data)
|
applyToOtherLanguage("frontend", "he", data)
|
||||||
|
applyToOtherLanguage("frontend", "nl", data)
|
||||||
|
applyToOtherLanguage("frontend", "pl", data)
|
||||||
applyToOtherLanguage("frontend", "fi", data)
|
applyToOtherLanguage("frontend", "fi", data)
|
||||||
|
applyToOtherLanguage("frontend", "sv", data)
|
||||||
|
applyToOtherLanguage("frontend", "uk", data)
|
||||||
|
applyToOtherLanguage("frontend", "kk", data)
|
||||||
|
applyToOtherLanguage("frontend", "fa", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateI18nBackend(t *testing.T) {
|
func TestGenerateI18nBackend(t *testing.T) {
|
||||||
@ -60,5 +66,11 @@ func TestGenerateI18nBackend(t *testing.T) {
|
|||||||
applyToOtherLanguage("backend", "tr", data)
|
applyToOtherLanguage("backend", "tr", data)
|
||||||
applyToOtherLanguage("backend", "ar", data)
|
applyToOtherLanguage("backend", "ar", data)
|
||||||
applyToOtherLanguage("backend", "he", data)
|
applyToOtherLanguage("backend", "he", data)
|
||||||
|
applyToOtherLanguage("backend", "nl", data)
|
||||||
|
applyToOtherLanguage("backend", "pl", data)
|
||||||
applyToOtherLanguage("backend", "fi", data)
|
applyToOtherLanguage("backend", "fi", data)
|
||||||
|
applyToOtherLanguage("backend", "sv", data)
|
||||||
|
applyToOtherLanguage("backend", "uk", data)
|
||||||
|
applyToOtherLanguage("backend", "kk", data)
|
||||||
|
applyToOtherLanguage("backend", "fa", data)
|
||||||
}
|
}
|
||||||
|
142
i18n/locales/fa/data.json
Normal file
142
i18n/locales/fa/data.json
Normal 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/kk/data.json
Normal file
142
i18n/locales/kk/data.json
Normal 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/nl/data.json
Normal file
142
i18n/locales/nl/data.json
Normal 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/pl/data.json
Normal file
142
i18n/locales/pl/data.json
Normal 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/sv/data.json
Normal file
142
i18n/locales/sv/data.json
Normal 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/uk/data.json
Normal file
142
i18n/locales/uk/data.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
@ -72,13 +72,13 @@ type FacebookCheckToken struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FacebookCheckTokenData
|
// FacebookCheckTokenData
|
||||||
// Get more detail via: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken
|
// Get more detail via: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow#checktoken
|
||||||
type FacebookCheckTokenData struct {
|
type FacebookCheckTokenData struct {
|
||||||
UserId string `json:"user_id"`
|
UserId string `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
||||||
// get more detail via: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#confirm
|
// get more detail via: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow#confirm
|
||||||
func (idp *FacebookIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
func (idp *FacebookIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Add("client_id", idp.Config.ClientID)
|
params.Add("client_id", idp.Config.ClientID)
|
||||||
|
10
idp/goth.go
10
idp/goth.go
@ -19,6 +19,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -97,6 +98,9 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
|||||||
Session: &amazon.Session{},
|
Session: &amazon.Session{},
|
||||||
}
|
}
|
||||||
case "Apple":
|
case "Apple":
|
||||||
|
if !strings.Contains(redirectUrl, "/api/callback") {
|
||||||
|
redirectUrl = strings.Replace(redirectUrl, "/callback", "/api/callback", 1)
|
||||||
|
}
|
||||||
idp = GothIdProvider{
|
idp = GothIdProvider{
|
||||||
Provider: apple.New(clientId, clientSecret, redirectUrl, nil),
|
Provider: apple.New(clientId, clientSecret, redirectUrl, nil),
|
||||||
Session: &apple.Session{},
|
Session: &apple.Session{},
|
||||||
@ -392,7 +396,9 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
|||||||
// Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required
|
// Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required
|
||||||
func (idp *GothIdProvider) SetHttpClient(client *http.Client) {
|
func (idp *GothIdProvider) SetHttpClient(client *http.Client) {
|
||||||
idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient")
|
idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient")
|
||||||
idpClient.Set(reflect.ValueOf(client))
|
if idpClient.IsValid() {
|
||||||
|
idpClient.Set(reflect.ValueOf(client))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||||
@ -468,6 +474,8 @@ func getUser(gothUser goth.User, provider string) *UserInfo {
|
|||||||
if provider == "steam" {
|
if provider == "steam" {
|
||||||
user.Username = user.Id
|
user.Username = user.Id
|
||||||
user.Email = ""
|
user.Email = ""
|
||||||
|
} else if provider == "apple" {
|
||||||
|
user.Username = util.GetUsernameFromEmail(user.Email)
|
||||||
}
|
}
|
||||||
return &user
|
return &user
|
||||||
}
|
}
|
||||||
|
@ -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", "DZ", "IL", "PH"],
|
"countryCodes": ["US", "GB", "ES", "FR", "DE", "CN", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR", "DZ", "IL", "PH", "NL", "PL", "FI", "SE", "UA", "KZ"],
|
||||||
"defaultAvatar": "",
|
"defaultAvatar": "",
|
||||||
"defaultApplication": "",
|
"defaultApplication": "",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "fi"],
|
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "nl", "pl", "fi", "sv", "uk", "kk", "fa"],
|
||||||
"masterPassword": "",
|
"masterPassword": "",
|
||||||
"initScore": 2000,
|
"initScore": 2000,
|
||||||
"enableSoftDeletion": false,
|
"enableSoftDeletion": false,
|
||||||
|
29
notification/bark.go
Normal file
29
notification/bark.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/bark"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewBarkProvider(deviceKey string) (notify.Notifier, error) {
|
||||||
|
barkSrv := bark.New(deviceKey)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(barkSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
33
notification/dingtalk.go
Normal file
33
notification/dingtalk.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/dingding"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDingTalkProvider(token string, secret string) (notify.Notifier, error) {
|
||||||
|
cfg := dingding.Config{
|
||||||
|
Token: token,
|
||||||
|
Secret: secret,
|
||||||
|
}
|
||||||
|
dingtalkSrv := dingding.New(&cfg)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(dingtalkSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
37
notification/discord.go
Normal file
37
notification/discord.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/discord"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDiscordProvider(token string, channelId string) (*notify.Notify, error) {
|
||||||
|
discordSrv := discord.New()
|
||||||
|
|
||||||
|
err := discordSrv.AuthenticateWithBotToken(token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
discordSrv.SetHttpClient(proxy.ProxyHttpClient)
|
||||||
|
discordSrv.AddReceivers(channelId)
|
||||||
|
|
||||||
|
notifier := notify.NewWithServices(discordSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
53
notification/google_chat.go
Normal file
53
notification/google_chat.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/googlechat"
|
||||||
|
"google.golang.org/api/chat/v1"
|
||||||
|
"google.golang.org/api/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewGoogleChatProvider(credentials string) (*notify.Notify, error) {
|
||||||
|
withCred := option.WithCredentialsJSON([]byte(credentials))
|
||||||
|
withSpacesScope := option.WithScopes("https://www.googleapis.com/auth/chat.spaces")
|
||||||
|
|
||||||
|
listSvc, err := chat.NewService(context.Background(), withCred, withSpacesScope)
|
||||||
|
spaces, err := listSvc.Spaces.List().Do()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
receivers := make([]string, 0)
|
||||||
|
for _, space := range spaces.Spaces {
|
||||||
|
name := strings.Replace(space.Name, "spaces/", "", 1)
|
||||||
|
receivers = append(receivers, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
googleChatSrv, err := googlechat.New(withCred)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
googleChatSrv.AddReceivers(receivers...)
|
||||||
|
|
||||||
|
notifier := notify.NewWithServices(googleChatSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
29
notification/lark.go
Normal file
29
notification/lark.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/lark"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewLarkProvider(webhookURL string) (notify.Notifier, error) {
|
||||||
|
larkSrv := lark.NewWebhookService(webhookURL)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(larkSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
32
notification/line.go
Normal file
32
notification/line.go
Normal 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 notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/line"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewLineProvider(channelSecret string, accessToken string, receiver string) (*notify.Notify, error) {
|
||||||
|
lineSrv, _ := line.NewWithHttpClient(channelSecret, accessToken, proxy.ProxyHttpClient)
|
||||||
|
|
||||||
|
lineSrv.AddReceivers(receiver)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(lineSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
36
notification/matrix.go
Normal file
36
notification/matrix.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/matrix"
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMatrixProvider(userId string, roomId string, accessToken string, homeServer string) (*notify.Notify, error) {
|
||||||
|
matrixSrv, err := matrix.New(id.UserID(userId), id.RoomID(roomId), homeServer, accessToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matrixSrv.SetHttpClient(proxy.ProxyHttpClient)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(matrixSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
31
notification/microsoft_teams.go
Normal file
31
notification/microsoft_teams.go
Normal 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 notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/msteams"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMicrosoftTeamsProvider(webhookURL string) (notify.Notifier, error) {
|
||||||
|
msTeamsSrv := msteams.New()
|
||||||
|
|
||||||
|
msTeamsSrv.AddReceivers(webhookURL)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(msTeamsSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
@ -14,13 +14,45 @@
|
|||||||
|
|
||||||
package notification
|
package notification
|
||||||
|
|
||||||
import "github.com/nikoksr/notify"
|
import "github.com/casdoor/notify"
|
||||||
|
|
||||||
func GetNotificationProvider(typ string, appId string, receiver string, method string, title string) (notify.Notifier, error) {
|
func GetNotificationProvider(typ string, clientId string, clientSecret string, clientId2 string, clientSecret2 string, appId string, receiver string, method string, title string, metaData string) (notify.Notifier, error) {
|
||||||
if typ == "Telegram" {
|
if typ == "Telegram" {
|
||||||
return NewTelegramProvider(appId, receiver)
|
return NewTelegramProvider(appId, receiver)
|
||||||
} else if typ == "Custom HTTP" {
|
} else if typ == "Custom HTTP" {
|
||||||
return NewCustomHttpProvider(receiver, method, title)
|
return NewCustomHttpProvider(receiver, method, title)
|
||||||
|
} else if typ == "DingTalk" {
|
||||||
|
return NewDingTalkProvider(appId, receiver)
|
||||||
|
} else if typ == "Lark" {
|
||||||
|
return NewLarkProvider(receiver)
|
||||||
|
} else if typ == "Microsoft Teams" {
|
||||||
|
return NewMicrosoftTeamsProvider(receiver)
|
||||||
|
} else if typ == "Bark" {
|
||||||
|
return NewBarkProvider(receiver)
|
||||||
|
} else if typ == "Pushover" {
|
||||||
|
return NewPushoverProvider(appId, receiver)
|
||||||
|
} else if typ == "Pushbullet" {
|
||||||
|
return NewPushbulletProvider(appId, receiver)
|
||||||
|
} else if typ == "Slack" {
|
||||||
|
return NewSlackProvider(appId, receiver)
|
||||||
|
} else if typ == "Webpush" {
|
||||||
|
return NewWebpushProvider(clientId, clientSecret, receiver)
|
||||||
|
} else if typ == "Discord" {
|
||||||
|
return NewDiscordProvider(appId, receiver)
|
||||||
|
} else if typ == "Google Chat" {
|
||||||
|
return NewGoogleChatProvider(metaData)
|
||||||
|
} else if typ == "Line" {
|
||||||
|
return NewLineProvider(clientSecret, appId, receiver)
|
||||||
|
} else if typ == "Matrix" {
|
||||||
|
return NewMatrixProvider(clientId, clientSecret, appId, receiver)
|
||||||
|
} else if typ == "Twitter" {
|
||||||
|
return NewTwitterProvider(clientId, clientSecret, clientId2, clientSecret2, receiver)
|
||||||
|
} else if typ == "Reddit" {
|
||||||
|
return NewRedditProvider(clientId, clientSecret, clientId2, clientSecret2, receiver)
|
||||||
|
} else if typ == "Rocket Chat" {
|
||||||
|
return NewRocketChatProvider(clientId, clientSecret, appId, receiver)
|
||||||
|
} else if typ == "Viber" {
|
||||||
|
return NewViberProvider(clientId, clientSecret, appId, receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
31
notification/pushbullet.go
Normal file
31
notification/pushbullet.go
Normal 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 notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/pushbullet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPushbulletProvider(apiToken string, deviceNickname string) (notify.Notifier, error) {
|
||||||
|
pushbulletSrv := pushbullet.New(apiToken)
|
||||||
|
|
||||||
|
pushbulletSrv.AddReceivers(deviceNickname)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(pushbulletSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
31
notification/pushover.go
Normal file
31
notification/pushover.go
Normal 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 notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/pushover"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPushoverProvider(appToken string, recipientID string) (notify.Notifier, error) {
|
||||||
|
pushoverSrv := pushover.New(appToken)
|
||||||
|
|
||||||
|
pushoverSrv.AddReceivers(recipientID)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(pushoverSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
34
notification/reddit.go
Normal file
34
notification/reddit.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/reddit"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRedditProvider(clientId string, clientSecret string, username string, password string, recipient string) (notify.Notifier, error) {
|
||||||
|
redditSrv, err := reddit.New(clientId, clientSecret, username, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
redditSrv.AddReceivers(recipient)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(redditSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
47
notification/rocket_chat.go
Normal file
47
notification/rocket_chat.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/rocketchat"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRocketChatProvider(clientId string, clientSecret string, endpoint string, channelName string) (notify.Notifier, error) {
|
||||||
|
parts := strings.Split(endpoint, "://")
|
||||||
|
|
||||||
|
var scheme, serverURL string
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
scheme = parts[0]
|
||||||
|
serverURL = parts[1]
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("parse endpoint error")
|
||||||
|
}
|
||||||
|
|
||||||
|
rocketChatSrv, err := rocketchat.New(serverURL, scheme, clientId, clientSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rocketChatSrv.AddReceivers(channelName)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(rocketChatSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
30
notification/slack.go
Normal file
30
notification/slack.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/slack"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewSlackProvider(apiToken string, channelID string) (*notify.Notify, error) {
|
||||||
|
slackSrv := slack.New(apiToken)
|
||||||
|
slackSrv.AddReceivers(channelID)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(slackSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
@ -18,9 +18,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/telegram"
|
||||||
api "github.com/go-telegram-bot-api/telegram-bot-api"
|
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) {
|
func NewTelegramProvider(apiToken string, chatIdStr string) (notify.Notifier, error) {
|
||||||
@ -28,15 +28,18 @@ func NewTelegramProvider(apiToken string, chatIdStr string) (notify.Notifier, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t := &telegram.Telegram{}
|
telegramSrv := &telegram.Telegram{}
|
||||||
t.SetClient(client)
|
telegramSrv.SetClient(client)
|
||||||
|
|
||||||
chatId, err := strconv.ParseInt(chatIdStr, 10, 64)
|
chatId, err := strconv.ParseInt(chatIdStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.AddReceivers(chatId)
|
telegramSrv.AddReceivers(chatId)
|
||||||
|
|
||||||
return t, nil
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(telegramSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
}
|
}
|
||||||
|
41
notification/twitter.go
Normal file
41
notification/twitter.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/twitter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTwitterProvider(consumerKey string, consumerSecret string, accessToken string, accessTokenSecret string, twitterId string) (*notify.Notify, error) {
|
||||||
|
credentials := twitter.Credentials{
|
||||||
|
ConsumerKey: consumerKey,
|
||||||
|
ConsumerSecret: consumerSecret,
|
||||||
|
AccessToken: accessToken,
|
||||||
|
AccessTokenSecret: accessTokenSecret,
|
||||||
|
}
|
||||||
|
twitterSrv, err := twitter.NewWithHttpClient(credentials, proxy.ProxyHttpClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
twitterSrv.AddReceivers(twitterId)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(twitterSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
36
notification/viber.go
Normal file
36
notification/viber.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/viber"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewViberProvider(senderName string, appKey string, webhookURL string, receiverId string) (notify.Notifier, error) {
|
||||||
|
viberSrv := viber.New(appKey, senderName, "")
|
||||||
|
|
||||||
|
err := viberSrv.SetWebhook(webhookURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
viberSrv.AddReceivers(receiverId)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(viberSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
33
notification/webpush.go
Normal file
33
notification/webpush.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/casdoor/notify"
|
||||||
|
"github.com/casdoor/notify/service/webpush"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewWebpushProvider(publicKey string, privateKey string, endpoint string) (*notify.Notify, error) {
|
||||||
|
webpushSrv := webpush.New(publicKey, privateKey)
|
||||||
|
|
||||||
|
subscription := webpush.Subscription{
|
||||||
|
Endpoint: endpoint,
|
||||||
|
}
|
||||||
|
webpushSrv.AddReceivers(subscription)
|
||||||
|
|
||||||
|
notifier := notify.NewWithServices(webpushSrv)
|
||||||
|
|
||||||
|
return notifier, nil
|
||||||
|
}
|
@ -151,7 +151,7 @@ func (adapter *Adapter) InitAdapter() error {
|
|||||||
if adapter.Adapter == nil {
|
if adapter.Adapter == nil {
|
||||||
var dataSourceName string
|
var dataSourceName string
|
||||||
|
|
||||||
if adapter.builtInAdapter() {
|
if adapter.isBuiltIn() {
|
||||||
dataSourceName = conf.GetConfigString("dataSourceName")
|
dataSourceName = conf.GetConfigString("dataSourceName")
|
||||||
if adapter.DatabaseType == "mysql" {
|
if adapter.DatabaseType == "mysql" {
|
||||||
dataSourceName = dataSourceName + adapter.Database
|
dataSourceName = dataSourceName + adapter.Database
|
||||||
@ -183,6 +183,14 @@ func (adapter *Adapter) InitAdapter() error {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
engine, err := xorm.NewEngine(adapter.DatabaseType, dataSourceName)
|
engine, err := xorm.NewEngine(adapter.DatabaseType, dataSourceName)
|
||||||
|
|
||||||
|
if adapter.isBuiltIn() && adapter.DatabaseType == "postgres" {
|
||||||
|
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
|
||||||
|
if schema != "" {
|
||||||
|
engine.SetSchema(schema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, adapter.getTable(), adapter.TableNamePrefix)
|
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, adapter.getTable(), adapter.TableNamePrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -211,7 +219,7 @@ func adapterChangeTrigger(oldName string, newName string) error {
|
|||||||
return session.Commit()
|
return session.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (adapter *Adapter) builtInAdapter() bool {
|
func (adapter *Adapter) isBuiltIn() bool {
|
||||||
if adapter.Owner != "built-in" {
|
if adapter.Owner != "built-in" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ type Application struct {
|
|||||||
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
|
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
|
||||||
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
||||||
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
||||||
|
CertPublicKey string `xorm:"-" json:"certPublicKey"`
|
||||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||||
InvitationCodes []string `xorm:"varchar(200)" json:"invitationCodes"`
|
InvitationCodes []string `xorm:"varchar(200)" json:"invitationCodes"`
|
||||||
|
|
||||||
@ -427,15 +428,14 @@ func (application *Application) GetId() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (application *Application) IsRedirectUriValid(redirectUri string) bool {
|
func (application *Application) IsRedirectUriValid(redirectUri string) bool {
|
||||||
isValid := false
|
redirectUris := append([]string{"http://localhost:"}, application.RedirectUris...)
|
||||||
for _, targetUri := range application.RedirectUris {
|
for _, targetUri := range redirectUris {
|
||||||
targetUriRegex := regexp.MustCompile(targetUri)
|
targetUriRegex := regexp.MustCompile(targetUri)
|
||||||
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
|
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
|
||||||
isValid = true
|
return true
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return isValid
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsOriginAllowed(origin string) (bool, error) {
|
func IsOriginAllowed(origin string) (bool, error) {
|
||||||
|
@ -33,10 +33,8 @@ type Cert struct {
|
|||||||
BitSize int `json:"bitSize"`
|
BitSize int `json:"bitSize"`
|
||||||
ExpireInYears int `json:"expireInYears"`
|
ExpireInYears int `json:"expireInYears"`
|
||||||
|
|
||||||
Certificate string `xorm:"mediumtext" json:"certificate"`
|
Certificate string `xorm:"mediumtext" json:"certificate"`
|
||||||
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
||||||
AuthorityPublicKey string `xorm:"mediumtext" json:"authorityPublicKey"`
|
|
||||||
AuthorityRootPublicKey string `xorm:"mediumtext" json:"authorityRootPublicKey"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaskedCert(cert *Cert) *Cert {
|
func GetMaskedCert(cert *Cert) *Cert {
|
||||||
|
BIN
object/cert.go~
Normal file
BIN
object/cert.go~
Normal file
Binary file not shown.
@ -66,8 +66,11 @@ func CheckUserSignup(application *Application, organization *Organization, form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(form.Password) <= 5 {
|
if application.IsSignupItemVisible("Password") {
|
||||||
return i18n.Translate(lang, "check:Password must have at least 6 characters")
|
msg := CheckPasswordComplexityByOrg(organization, form.Password)
|
||||||
|
if msg != "" {
|
||||||
|
return msg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if application.IsSignupItemVisible("Email") {
|
if application.IsSignupItemVisible("Email") {
|
||||||
@ -126,7 +129,9 @@ func CheckUserSignup(application *Application, organization *Organization, form
|
|||||||
|
|
||||||
if len(application.InvitationCodes) > 0 {
|
if len(application.InvitationCodes) > 0 {
|
||||||
if form.InvitationCode == "" {
|
if form.InvitationCode == "" {
|
||||||
return i18n.Translate(lang, "check:Invitation code cannot be blank")
|
if application.IsSignupItemRequired("Invitation code") {
|
||||||
|
return i18n.Translate(lang, "check:Invitation code cannot be blank")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if !util.InSlice(application.InvitationCodes, form.InvitationCode) {
|
if !util.InSlice(application.InvitationCodes, form.InvitationCode) {
|
||||||
return i18n.Translate(lang, "check:Invitation code is invalid")
|
return i18n.Translate(lang, "check:Invitation code is invalid")
|
||||||
@ -345,7 +350,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
|
|||||||
return hasPermission, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
return hasPermission, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckAccessPermission(userId string, application *Application) (bool, error) {
|
func CheckLoginPermission(userId string, application *Application) (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
if userId == "built-in/admin" {
|
if userId == "built-in/admin" {
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -356,32 +361,40 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
allowed := true
|
allowCount := 0
|
||||||
|
denyCount := 0
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
if !permission.IsEnabled {
|
if !permission.IsEnabled || permission.ResourceType != "Application" || !permission.isResourceHit(application.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
isHit := false
|
if permission.isUserHit(userId) {
|
||||||
for _, resource := range permission.Resources {
|
allowCount += 1
|
||||||
if application.Name == resource {
|
|
||||||
isHit = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if isHit {
|
enforcer := getPermissionEnforcer(permission)
|
||||||
containsAsterisk := ContainsAsterisk(userId, permission.Users)
|
|
||||||
if containsAsterisk {
|
var isAllowed bool
|
||||||
return true, err
|
isAllowed, err = enforcer.Enforce(userId, application.Name, "Read")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAllowed {
|
||||||
|
if permission.Effect == "Allow" {
|
||||||
|
allowCount += 1
|
||||||
}
|
}
|
||||||
enforcer := getPermissionEnforcer(permission)
|
} else {
|
||||||
if allowed, err = enforcer.Enforce(userId, application.Name, "read"); allowed {
|
if permission.Effect == "Deny" {
|
||||||
return allowed, err
|
denyCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return allowed, err
|
|
||||||
|
if denyCount > 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckUsername(username string, lang string) string {
|
func CheckUsername(username string, lang string) string {
|
||||||
@ -401,10 +414,6 @@ func CheckUsername(username string, lang string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckUpdateUser(oldUser, user *User, lang string) string {
|
func CheckUpdateUser(oldUser, user *User, lang string) string {
|
||||||
if user.DisplayName == "" {
|
|
||||||
return i18n.Translate(lang, "user:Display name cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if oldUser.Name != user.Name {
|
if oldUser.Name != user.Name {
|
||||||
if msg := CheckUsername(user.Name, lang); msg != "" {
|
if msg := CheckUsername(user.Name, lang); msg != "" {
|
||||||
return msg
|
return msg
|
||||||
@ -414,7 +423,7 @@ func CheckUpdateUser(oldUser, user *User, lang string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if oldUser.Email != user.Email {
|
if oldUser.Email != user.Email {
|
||||||
if HasUserByField(user.Name, "email", user.Email) {
|
if HasUserByField(user.Owner, "email", user.Email) {
|
||||||
return i18n.Translate(lang, "check:Email already exists")
|
return i18n.Translate(lang, "check:Email already exists")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/email"
|
||||||
"github.com/casdoor/gomail/v2"
|
"github.com/casdoor/gomail/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,9 +36,7 @@ func getDialer(provider *Provider) *gomail.Dialer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
|
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
|
||||||
dialer := getDialer(provider)
|
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.AppId, provider.Host, provider.Port, provider.DisableSsl)
|
||||||
|
|
||||||
message := gomail.NewMessage()
|
|
||||||
|
|
||||||
fromAddress := provider.ClientId2
|
fromAddress := provider.ClientId2
|
||||||
if fromAddress == "" {
|
if fromAddress == "" {
|
||||||
@ -49,14 +48,7 @@ func SendEmail(provider *Provider, title string, content string, dest string, se
|
|||||||
fromName = sender
|
fromName = sender
|
||||||
}
|
}
|
||||||
|
|
||||||
message.SetAddressHeader("From", fromAddress, fromName)
|
return emailProvider.Send(fromAddress, fromName, dest, title, content)
|
||||||
message.SetHeader("To", dest)
|
|
||||||
message.SetHeader("Subject", title)
|
|
||||||
message.SetBody("text/html", content)
|
|
||||||
|
|
||||||
message.SkipUsernameCheck = true
|
|
||||||
|
|
||||||
return dialer.DialAndSend(message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DailSmtpServer Dail Smtp server
|
// DailSmtpServer Dail Smtp server
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/beego/beego"
|
|
||||||
"github.com/beego/beego/context"
|
"github.com/beego/beego/context"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pquerna/otp"
|
"github.com/pquerna/otp"
|
||||||
@ -39,10 +38,11 @@ type TotpMfa struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mfa *TotpMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
|
func (mfa *TotpMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
|
||||||
issuer := beego.AppConfig.String("appname")
|
//issuer := beego.AppConfig.String("appname")
|
||||||
if issuer == "" {
|
//if issuer == "" {
|
||||||
issuer = "casdoor"
|
// issuer = "casdoor"
|
||||||
}
|
//}
|
||||||
|
issuer := "casdoor"
|
||||||
|
|
||||||
key, err := totp.Generate(totp.GenerateOpts{
|
key, err := totp.Generate(totp.GenerateOpts{
|
||||||
Issuer: issuer,
|
Issuer: issuer,
|
||||||
@ -81,12 +81,15 @@ func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
|
|||||||
return errors.New("totp secret is missing")
|
return errors.New("totp secret is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, _ := totp.ValidateCustom(passcode, secret.(string), time.Now().UTC(), totp.ValidateOpts{
|
result, err := totp.ValidateCustom(passcode, secret.(string), time.Now().UTC(), totp.ValidateOpts{
|
||||||
Period: MfaTotpPeriodInSeconds,
|
Period: MfaTotpPeriodInSeconds,
|
||||||
Skew: 1,
|
Skew: 1,
|
||||||
Digits: otp.DigitsSix,
|
Digits: otp.DigitsSix,
|
||||||
Algorithm: otp.AlgorithmSHA1,
|
Algorithm: otp.AlgorithmSHA1,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if result {
|
if result {
|
||||||
return nil
|
return nil
|
||||||
@ -125,7 +128,15 @@ func (mfa *TotpMfa) Enable(ctx *context.Context, user *User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mfa *TotpMfa) Verify(passcode string) error {
|
func (mfa *TotpMfa) Verify(passcode string) error {
|
||||||
result := totp.Validate(passcode, mfa.Config.Secret)
|
result, err := totp.ValidateCustom(passcode, mfa.Config.Secret, time.Now().UTC(), totp.ValidateOpts{
|
||||||
|
Period: MfaTotpPeriodInSeconds,
|
||||||
|
Skew: 1,
|
||||||
|
Digits: otp.DigitsSix,
|
||||||
|
Algorithm: otp.AlgorithmSHA1,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if result {
|
if result {
|
||||||
return nil
|
return nil
|
||||||
|
@ -18,12 +18,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/notification"
|
"github.com/casdoor/casdoor/notification"
|
||||||
"github.com/nikoksr/notify"
|
"github.com/casdoor/notify"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getNotificationClient(provider *Provider) (notify.Notifier, error) {
|
func getNotificationClient(provider *Provider) (notify.Notifier, error) {
|
||||||
var client notify.Notifier
|
var client notify.Notifier
|
||||||
client, err := notification.GetNotificationProvider(provider.Type, provider.AppId, provider.Receiver, provider.Method, provider.Title)
|
client, err := notification.GetNotificationProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.ClientId2, provider.ClientSecret2, provider.AppId, provider.Receiver, provider.Method, provider.Title, provider.Metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,14 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
"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
|
||||||
@ -65,6 +68,17 @@ func InitConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InitAdapter() {
|
func InitAdapter() {
|
||||||
|
if conf.GetConfigString("driverName") == "" {
|
||||||
|
if !util.FileExist("conf/app.conf") {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dir = strings.ReplaceAll(dir, "\\", "/")
|
||||||
|
panic(fmt.Sprintf("The Casdoor config file: \"app.conf\" was not found, it should be placed at: \"%s/conf/app.conf\"", dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@ -122,9 +136,14 @@ func NewAdapter(driverName string, dataSourceName string, dbName string) *Ormer
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refineDataSourceNameForPostgres(dataSourceName string) string {
|
||||||
|
reg := regexp.MustCompile(`dbname=[^ ]+\s*`)
|
||||||
|
return reg.ReplaceAllString(dataSourceName, "")
|
||||||
|
}
|
||||||
|
|
||||||
func createDatabaseForPostgres(driverName string, dataSourceName string, dbName string) error {
|
func createDatabaseForPostgres(driverName string, dataSourceName string, dbName string) error {
|
||||||
if driverName == "postgres" {
|
if driverName == "postgres" {
|
||||||
db, err := sql.Open(driverName, dataSourceName)
|
db, err := sql.Open(driverName, refineDataSourceNameForPostgres(dataSourceName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -136,6 +155,21 @@ func createDatabaseForPostgres(driverName string, dataSourceName string, dbName
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
|
||||||
|
if schema != "" {
|
||||||
|
db, err = sql.Open(driverName, dataSourceName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s;", schema))
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "already exists") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
@ -168,6 +202,12 @@ func (a *Ormer) open() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
if a.driverName == "postgres" {
|
||||||
|
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
|
||||||
|
if schema != "" {
|
||||||
|
engine.SetSchema(schema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a.Engine = engine
|
a.Engine = engine
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/pp"
|
"github.com/casdoor/casdoor/pp"
|
||||||
|
|
||||||
@ -55,6 +54,7 @@ type Payment struct {
|
|||||||
// Order Info
|
// Order Info
|
||||||
OutOrderId string `xorm:"varchar(100)" json:"outOrderId"`
|
OutOrderId string `xorm:"varchar(100)" json:"outOrderId"`
|
||||||
PayUrl string `xorm:"varchar(2000)" json:"payUrl"`
|
PayUrl string `xorm:"varchar(2000)" json:"payUrl"`
|
||||||
|
SuccessUrl string `xorm:"varchar(2000)" json:"successUrl""` // `successUrl` is redirected from `payUrl` after pay success
|
||||||
State pp.PaymentState `xorm:"varchar(100)" json:"state"`
|
State pp.PaymentState `xorm:"varchar(100)" json:"state"`
|
||||||
Message string `xorm:"varchar(2000)" json:"message"`
|
Message string `xorm:"varchar(2000)" json:"message"`
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ func DeletePayment(payment *Payment) (bool, error) {
|
|||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func notifyPayment(request *http.Request, body []byte, owner string, paymentName string, orderId string) (*Payment, *pp.NotifyResult, error) {
|
func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp.NotifyResult, error) {
|
||||||
payment, err := getPayment(owner, paymentName)
|
payment, err := getPayment(owner, paymentName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -166,7 +166,7 @@ func notifyPayment(request *http.Request, body []byte, owner string, paymentName
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
pProvider, cert, err := provider.getPaymentProvider()
|
pProvider, err := GetPaymentProvider(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -180,11 +180,7 @@ func notifyPayment(request *http.Request, body []byte, owner string, paymentName
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if orderId == "" {
|
notifyResult, err := pProvider.Notify(body, payment.OutOrderId)
|
||||||
orderId = payment.OutOrderId
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyResult, err := pProvider.Notify(request, body, cert.AuthorityPublicKey, orderId)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return payment, nil, err
|
return payment, nil, err
|
||||||
}
|
}
|
||||||
@ -205,8 +201,8 @@ func notifyPayment(request *http.Request, body []byte, owner string, paymentName
|
|||||||
return payment, notifyResult, nil
|
return payment, notifyResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotifyPayment(request *http.Request, body []byte, owner string, paymentName string, orderId string) (*Payment, error) {
|
func NotifyPayment(body []byte, owner string, paymentName string) (*Payment, error) {
|
||||||
payment, notifyResult, err := notifyPayment(request, body, owner, paymentName, orderId)
|
payment, notifyResult, err := notifyPayment(body, owner, paymentName)
|
||||||
if payment != nil {
|
if payment != nil {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
payment.State = pp.PaymentStateError
|
payment.State = pp.PaymentStateError
|
||||||
@ -234,7 +230,7 @@ func invoicePayment(payment *Payment) (string, error) {
|
|||||||
return "", fmt.Errorf("the payment provider: %s does not exist", payment.Provider)
|
return "", fmt.Errorf("the payment provider: %s does not exist", payment.Provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
pProvider, _, err := provider.getPaymentProvider()
|
pProvider, err := GetPaymentProvider(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ type Permission struct {
|
|||||||
Description string `xorm:"varchar(100)" json:"description"`
|
Description string `xorm:"varchar(100)" json:"description"`
|
||||||
|
|
||||||
Users []string `xorm:"mediumtext" json:"users"`
|
Users []string `xorm:"mediumtext" json:"users"`
|
||||||
|
Groups []string `xorm:"mediumtext" json:"groups"`
|
||||||
Roles []string `xorm:"mediumtext" json:"roles"`
|
Roles []string `xorm:"mediumtext" json:"roles"`
|
||||||
Domains []string `xorm:"mediumtext" json:"domains"`
|
Domains []string `xorm:"mediumtext" json:"domains"`
|
||||||
|
|
||||||
@ -60,10 +61,6 @@ type PermissionRule struct {
|
|||||||
|
|
||||||
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||||
|
|
||||||
func (p *Permission) GetId() string {
|
|
||||||
return util.GetId(p.Owner, p.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPermissionCount(owner, field, value string) (int64, error) {
|
func GetPermissionCount(owner, field, value string) (int64, error) {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
return session.Count(&Permission{})
|
return session.Count(&Permission{})
|
||||||
@ -345,20 +342,6 @@ func GetPermissionsByModel(owner string, model string) ([]*Permission, error) {
|
|||||||
return permissions, nil
|
return permissions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ContainsAsterisk(userId string, users []string) bool {
|
|
||||||
containsAsterisk := false
|
|
||||||
group, _ := util.GetOwnerAndNameFromId(userId)
|
|
||||||
for _, user := range users {
|
|
||||||
permissionGroup, permissionUserName := util.GetOwnerAndNameFromId(user)
|
|
||||||
if permissionGroup == group && permissionUserName == "*" {
|
|
||||||
containsAsterisk = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return containsAsterisk
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMaskedPermissions(permissions []*Permission) []*Permission {
|
func GetMaskedPermissions(permissions []*Permission) []*Permission {
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
permission.Users = nil
|
permission.Users = nil
|
||||||
@ -388,3 +371,27 @@ func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]stri
|
|||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Permission) GetId() string {
|
||||||
|
return util.GetId(p.Owner, p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Permission) isUserHit(name string) bool {
|
||||||
|
targetOrg, _ := util.GetOwnerAndNameFromId(name)
|
||||||
|
for _, user := range p.Users {
|
||||||
|
userOrg, userName := util.GetOwnerAndNameFromId(user)
|
||||||
|
if userOrg == targetOrg && userName == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Permission) isResourceHit(name string) bool {
|
||||||
|
for _, resource := range p.Resources {
|
||||||
|
if name == resource {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -79,9 +79,7 @@ func (p *Permission) setEnforcerAdapter(enforcer *casbin.Enforcer) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
driverName := conf.GetConfigString("driverName")
|
adapter, err := xormadapter.NewAdapterByEngineWithTableName(ormer.Engine, tableName, tableNamePrefix)
|
||||||
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
|
|
||||||
adapter, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, tableName, tableNamePrefix, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
@ -28,10 +29,10 @@ 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"`
|
Price float64 `json:"price"`
|
||||||
PricePerYear float64 `json:"pricePerYear"`
|
|
||||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||||
Product string `json:"product"` // related product id
|
Period string `xorm:"varchar(100)" json:"period"`
|
||||||
|
Product string `xorm:"varchar(100)" json:"product"`
|
||||||
PaymentProviders []string `xorm:"varchar(100)" json:"paymentProviders"` // payment providers for related product
|
PaymentProviders []string `xorm:"varchar(100)" json:"paymentProviders"` // payment providers for related product
|
||||||
IsEnabled bool `json:"isEnabled"`
|
IsEnabled bool `json:"isEnabled"`
|
||||||
|
|
||||||
@ -39,10 +40,28 @@ type Plan struct {
|
|||||||
Options []string `xorm:"-" json:"options"`
|
Options []string `xorm:"-" json:"options"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PeriodMonthly = "Monthly"
|
||||||
|
PeriodYearly = "Yearly"
|
||||||
|
)
|
||||||
|
|
||||||
func (plan *Plan) GetId() string {
|
func (plan *Plan) GetId() string {
|
||||||
return fmt.Sprintf("%s/%s", plan.Owner, plan.Name)
|
return fmt.Sprintf("%s/%s", plan.Owner, plan.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDuration(period string) (startTime time.Time, endTime time.Time) {
|
||||||
|
if period == PeriodYearly {
|
||||||
|
startTime = time.Now()
|
||||||
|
endTime = startTime.AddDate(1, 0, 0)
|
||||||
|
} else if period == PeriodMonthly {
|
||||||
|
startTime = time.Now()
|
||||||
|
endTime = startTime.AddDate(0, 1, 0)
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("invalid period: %s", period))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
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{})
|
||||||
|
@ -32,12 +32,6 @@ type Pricing struct {
|
|||||||
IsEnabled bool `json:"isEnabled"`
|
IsEnabled bool `json:"isEnabled"`
|
||||||
TrialDuration int `json:"trialDuration"`
|
TrialDuration int `json:"trialDuration"`
|
||||||
Application string `xorm:"varchar(100)" json:"application"`
|
Application string `xorm:"varchar(100)" json:"application"`
|
||||||
|
|
||||||
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 (pricing *Pricing) GetId() string {
|
func (pricing *Pricing) GetId() string {
|
||||||
|
@ -37,7 +37,7 @@ type Product struct {
|
|||||||
Price float64 `json:"price"`
|
Price float64 `json:"price"`
|
||||||
Quantity int `json:"quantity"`
|
Quantity int `json:"quantity"`
|
||||||
Sold int `json:"sold"`
|
Sold int `json:"sold"`
|
||||||
Providers []string `xorm:"varchar(100)" json:"providers"`
|
Providers []string `xorm:"varchar(255)" json:"providers"`
|
||||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||||
|
|
||||||
State string `xorm:"varchar(100)" json:"state"`
|
State string `xorm:"varchar(100)" json:"state"`
|
||||||
@ -158,24 +158,23 @@ func (product *Product) getProvider(providerName string) (*Provider, error) {
|
|||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuyProduct(id string, user *User, providerName, pricingName, planName, host string) (string, string, error) {
|
func BuyProduct(id string, user *User, providerName, pricingName, planName, host string) (*Payment, error) {
|
||||||
product, err := GetProduct(id)
|
product, err := GetProduct(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if product == nil {
|
if product == nil {
|
||||||
return "", "", fmt.Errorf("the product: %s does not exist", id)
|
return nil, fmt.Errorf("the product: %s does not exist", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := product.getProvider(providerName)
|
provider, err := product.getProvider(providerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pProvider, _, err := provider.getPaymentProvider()
|
pProvider, err := GetPaymentProvider(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := product.Owner
|
owner := product.Owner
|
||||||
@ -190,10 +189,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
if user.Type == "paid-user" {
|
if user.Type == "paid-user" {
|
||||||
// Create a subscription for `paid-user`
|
// Create a subscription for `paid-user`
|
||||||
if pricingName != "" && planName != "" {
|
if pricingName != "" && planName != "" {
|
||||||
sub := NewSubscription(owner, user.Name, pricingName, planName, paymentName)
|
plan, err := GetPlan(util.GetId(owner, planName))
|
||||||
_, err := AddSubscription(sub)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
|
}
|
||||||
|
if plan == nil {
|
||||||
|
return nil, fmt.Errorf("the plan: %s does not exist", planName)
|
||||||
|
}
|
||||||
|
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
||||||
|
_, err = AddSubscription(sub)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
||||||
}
|
}
|
||||||
@ -201,10 +207,10 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
// Create an OrderId and get the payUrl
|
// 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 nil, err
|
||||||
}
|
}
|
||||||
// Create a Payment linked with Product and Order
|
// Create a Payment linked with Product and Order
|
||||||
payment := Payment{
|
payment := &Payment{
|
||||||
Owner: product.Owner,
|
Owner: product.Owner,
|
||||||
Name: paymentName,
|
Name: paymentName,
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
@ -223,6 +229,7 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
|
|
||||||
User: user.Name,
|
User: user.Name,
|
||||||
PayUrl: payUrl,
|
PayUrl: payUrl,
|
||||||
|
SuccessUrl: returnUrl,
|
||||||
State: pp.PaymentStateCreated,
|
State: pp.PaymentStateCreated,
|
||||||
OutOrderId: orderId,
|
OutOrderId: orderId,
|
||||||
}
|
}
|
||||||
@ -231,15 +238,15 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
payment.State = pp.PaymentStatePaid
|
payment.State = pp.PaymentStatePaid
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := AddPayment(&payment)
|
affected, err := AddPayment(payment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !affected {
|
if !affected {
|
||||||
return "", "", fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
return nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||||
}
|
}
|
||||||
return payUrl, orderId, err
|
return payment, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtendProductWithProviders(product *Product) error {
|
func ExtendProductWithProviders(product *Product) error {
|
||||||
@ -267,14 +274,14 @@ func CreateProductForPlan(plan *Plan) *Product {
|
|||||||
product := &Product{
|
product := &Product{
|
||||||
Owner: plan.Owner,
|
Owner: plan.Owner,
|
||||||
Name: fmt.Sprintf("product_%v", util.GetRandomName()),
|
Name: fmt.Sprintf("product_%v", util.GetRandomName()),
|
||||||
DisplayName: fmt.Sprintf("Auto Created Product for Plan %v(%v)", plan.GetId(), plan.DisplayName),
|
DisplayName: fmt.Sprintf("Product for Plan %v/%v/%v", plan.Name, plan.DisplayName, plan.Period),
|
||||||
CreatedTime: plan.CreatedTime,
|
CreatedTime: plan.CreatedTime,
|
||||||
|
|
||||||
Image: "https://cdn.casbin.org/img/casdoor-logo_1185x256.png", // TODO
|
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),
|
Detail: fmt.Sprintf("This product was auto created for plan %v(%v), subscription period is %v", plan.Name, plan.DisplayName, plan.Period),
|
||||||
Description: plan.Description,
|
Description: plan.Description,
|
||||||
Tag: "auto_created_product_for_plan",
|
Tag: "auto_created_product_for_plan",
|
||||||
Price: plan.PricePerMonth, // TODO
|
Price: plan.Price,
|
||||||
Currency: plan.Currency,
|
Currency: plan.Currency,
|
||||||
|
|
||||||
Quantity: 999,
|
Quantity: 999,
|
||||||
@ -283,13 +290,17 @@ func CreateProductForPlan(plan *Plan) *Product {
|
|||||||
Providers: plan.PaymentProviders,
|
Providers: plan.PaymentProviders,
|
||||||
State: "Published",
|
State: "Published",
|
||||||
}
|
}
|
||||||
|
if product.Providers == nil {
|
||||||
|
product.Providers = []string{}
|
||||||
|
}
|
||||||
return product
|
return product
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateProductForPlan(plan *Plan, product *Product) {
|
func UpdateProductForPlan(plan *Plan, product *Product) {
|
||||||
product.DisplayName = fmt.Sprintf("Auto Created Product for Plan %v(%v)", plan.GetId(), plan.DisplayName)
|
product.Owner = plan.Owner
|
||||||
product.Detail = fmt.Sprintf("This Product was auto created for Plan %v(%v)", plan.GetId(), plan.DisplayName)
|
product.DisplayName = fmt.Sprintf("Product for Plan %v/%v/%v", plan.Name, plan.DisplayName, plan.Period)
|
||||||
product.Price = plan.PricePerMonth // TODO
|
product.Detail = fmt.Sprintf("This product was auto created for plan %v(%v), subscription period is %v", plan.Name, plan.DisplayName, plan.Period)
|
||||||
product.Providers = plan.PaymentProviders
|
product.Price = plan.Price
|
||||||
product.Currency = plan.Currency
|
product.Currency = plan.Currency
|
||||||
|
product.Providers = plan.PaymentProviders
|
||||||
}
|
}
|
||||||
|
@ -17,31 +17,24 @@
|
|||||||
|
|
||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
//func TestProduct(t *testing.T) {
|
||||||
"testing"
|
// InitConfig()
|
||||||
|
//
|
||||||
"github.com/casdoor/casdoor/pp"
|
// product, _ := GetProduct("admin/product_123")
|
||||||
"github.com/casdoor/casdoor/util"
|
// provider, _ := getProvider(product.Owner, "provider_pay_alipay")
|
||||||
)
|
// cert, _ := getCert(product.Owner, "cert-pay-alipay")
|
||||||
|
// pProvider, err := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey, provider.ClientId2)
|
||||||
func TestProduct(t *testing.T) {
|
// if err != nil {
|
||||||
InitConfig()
|
// panic(err)
|
||||||
|
// }
|
||||||
product, _ := GetProduct("admin/product_123")
|
//
|
||||||
provider, _ := getProvider(product.Owner, "provider_pay_alipay")
|
// paymentName := util.GenerateTimeId()
|
||||||
cert, _ := getCert(product.Owner, "cert-pay-alipay")
|
// returnUrl := ""
|
||||||
pProvider, err := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey, provider.ClientId2)
|
// notifyUrl := ""
|
||||||
if err != nil {
|
// payUrl, _, err := pProvider.Pay(provider.Name, product.Name, "alice", paymentName, product.DisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
||||||
panic(err)
|
// if err != nil {
|
||||||
}
|
// panic(err)
|
||||||
|
// }
|
||||||
paymentName := util.GenerateTimeId()
|
//
|
||||||
returnUrl := ""
|
// println(payUrl)
|
||||||
notifyUrl := ""
|
//}
|
||||||
payUrl, _, err := pProvider.Pay(provider.Name, product.Name, "alice", paymentName, product.DisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
println(payUrl)
|
|
||||||
}
|
|
||||||
|
@ -251,30 +251,69 @@ func DeleteProvider(provider *Provider) (bool, error) {
|
|||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
|
func GetPaymentProvider(p *Provider) (pp.PaymentProvider, error) {
|
||||||
cert := &Cert{}
|
cert := &Cert{}
|
||||||
if p.Cert != "" {
|
if p.Cert != "" {
|
||||||
var err error
|
var err error
|
||||||
cert, err = getCert(p.Owner, p.Cert)
|
cert, err = GetCert(util.GetId(p.Owner, p.Cert))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cert == nil {
|
if cert == nil {
|
||||||
return nil, nil, fmt.Errorf("the cert: %s does not exist", p.Cert)
|
return nil, fmt.Errorf("the cert: %s does not exist", p.Cert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
typ := p.Type
|
||||||
pProvider, err := pp.GetPaymentProvider(p.Type, p.ClientId, p.ClientSecret, p.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey, p.ClientId2)
|
if typ == "Dummy" {
|
||||||
if err != nil {
|
pp, err := pp.NewDummyPaymentProvider()
|
||||||
return nil, cert, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
|
} else if typ == "Alipay" {
|
||||||
|
if p.Metadata != "" {
|
||||||
|
// alipay provider store rootCert's name in metadata
|
||||||
|
rootCert, err := GetCert(util.GetId(p.Owner, p.Metadata))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rootCert == nil {
|
||||||
|
return nil, fmt.Errorf("the cert: %s does not exist", p.Metadata)
|
||||||
|
}
|
||||||
|
pp, err := pp.NewAlipayPaymentProvider(p.ClientId, cert.Certificate, cert.PrivateKey, rootCert.Certificate, rootCert.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("the metadata of alipay provider is empty")
|
||||||
|
}
|
||||||
|
} else if typ == "GC" {
|
||||||
|
return pp.NewGcPaymentProvider(p.ClientId, p.ClientSecret, p.Host), nil
|
||||||
|
} else if typ == "WeChat Pay" {
|
||||||
|
pp, err := pp.NewWechatPaymentProvider(p.ClientId, p.ClientSecret, p.ClientId2, cert.Certificate, cert.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
|
} else if typ == "PayPal" {
|
||||||
|
pp, err := pp.NewPaypalPaymentProvider(p.ClientId, p.ClientSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
|
} else if typ == "Stripe" {
|
||||||
|
pp, err := pp.NewStripePaymentProvider(p.ClientId, p.ClientSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pProvider == nil {
|
return nil, nil
|
||||||
return nil, cert, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pProvider, cert, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) GetId() string {
|
func (p *Provider) GetId() string {
|
||||||
|
@ -72,7 +72,7 @@ func GetTruncatedPath(provider *Provider, fullFilePath string, limit int) string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
||||||
escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath))
|
escapedPath := util.UrlJoin(provider.PathPrefix, fullFilePath)
|
||||||
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
||||||
|
|
||||||
host := ""
|
host := ""
|
||||||
|
@ -50,7 +50,7 @@ type Subscription struct {
|
|||||||
|
|
||||||
StartTime time.Time `json:"startTime"`
|
StartTime time.Time `json:"startTime"`
|
||||||
EndTime time.Time `json:"endTime"`
|
EndTime time.Time `json:"endTime"`
|
||||||
Duration int `json:"duration"`
|
Period string `xorm:"varchar(100)" json:"period"`
|
||||||
State SubscriptionState `xorm:"varchar(100)" json:"state"`
|
State SubscriptionState `xorm:"varchar(100)" json:"state"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,8 @@ func (sub *Subscription) UpdateState() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSubscription(owner, userName, pricingName, planName, paymentName string) *Subscription {
|
func NewSubscription(owner, userName, planName, paymentName, period string) *Subscription {
|
||||||
|
startTime, endTime := GetDuration(period)
|
||||||
id := util.GenerateId()[:6]
|
id := util.GenerateId()[:6]
|
||||||
return &Subscription{
|
return &Subscription{
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
@ -112,13 +113,12 @@ func NewSubscription(owner, userName, pricingName, planName, paymentName string)
|
|||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
|
|
||||||
User: userName,
|
User: userName,
|
||||||
Pricing: pricingName,
|
|
||||||
Plan: planName,
|
Plan: planName,
|
||||||
Payment: paymentName,
|
Payment: paymentName,
|
||||||
|
|
||||||
StartTime: time.Now(),
|
StartTime: startTime,
|
||||||
EndTime: time.Now().AddDate(0, 0, 30),
|
EndTime: endTime,
|
||||||
Duration: 30, // TODO
|
Period: period,
|
||||||
State: SubStatePending, // waiting for payment complete
|
State: SubStatePending, // waiting for payment complete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,13 @@ type Syncer struct {
|
|||||||
|
|
||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
Type string `xorm:"varchar(100)" json:"type"`
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
|
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
|
||||||
|
SslMode string `xorm:"varchar(100)" json:"sslMode"`
|
||||||
|
|
||||||
Host string `xorm:"varchar(100)" json:"host"`
|
Host string `xorm:"varchar(100)" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
User string `xorm:"varchar(100)" json:"user"`
|
User string `xorm:"varchar(100)" json:"user"`
|
||||||
Password string `xorm:"varchar(100)" json:"password"`
|
Password string `xorm:"varchar(100)" json:"password"`
|
||||||
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"`
|
||||||
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
|
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
|
||||||
@ -250,7 +251,7 @@ func (syncer *Syncer) getKey() string {
|
|||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunSyncer(syncer *Syncer) {
|
func RunSyncer(syncer *Syncer) error {
|
||||||
syncer.initAdapter()
|
syncer.initAdapter()
|
||||||
syncer.syncUsers()
|
return syncer.syncUsers()
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,14 @@ func addSyncerJob(syncer *Syncer) error {
|
|||||||
|
|
||||||
syncer.initAdapter()
|
syncer.initAdapter()
|
||||||
|
|
||||||
syncer.syncUsers()
|
err := syncer.syncUsers()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
schedule := fmt.Sprintf("@every %ds", syncer.SyncInterval)
|
schedule := fmt.Sprintf("@every %ds", syncer.SyncInterval)
|
||||||
cron := getCronMap(syncer.Name)
|
cron := getCronMap(syncer.Name)
|
||||||
_, err := cron.AddFunc(schedule, syncer.syncUsers)
|
_, err = cron.AddFunc(schedule, syncer.syncUsersNoError)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (syncer *Syncer) syncUsers() {
|
func (syncer *Syncer) syncUsers() error {
|
||||||
if len(syncer.TableColumns) == 0 {
|
if len(syncer.TableColumns) == 0 {
|
||||||
return
|
return fmt.Errorf("The syncer table columns should not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Running syncUsers()..\n")
|
fmt.Printf("Running syncUsers()..\n")
|
||||||
|
|
||||||
users, _, _ := syncer.getUserMap()
|
users, _, _ := syncer.getUserMap()
|
||||||
oUsers, oUserMap, err := syncer.getOriginalUserMap()
|
oUsers, _, err := syncer.getOriginalUserMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf(err.Error())
|
fmt.Printf(err.Error())
|
||||||
|
|
||||||
@ -35,10 +35,8 @@ func (syncer *Syncer) syncUsers() {
|
|||||||
line := fmt.Sprintf("[%s] %s\n", timestamp, err.Error())
|
line := fmt.Sprintf("[%s] %s\n", timestamp, err.Error())
|
||||||
_, err = updateSyncerErrorText(syncer, line)
|
_, err = updateSyncerErrorText(syncer, line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Users: %d, oUsers: %d\n", len(users), len(oUsers))
|
fmt.Printf("Users: %d, oUsers: %d\n", len(users), len(oUsers))
|
||||||
@ -55,6 +53,11 @@ func (syncer *Syncer) syncUsers() {
|
|||||||
myUsers[syncer.getUserValue(m, key)] = m
|
myUsers[syncer.getUserValue(m, key)] = m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myOUsers := map[string]*User{}
|
||||||
|
for _, m := range oUsers {
|
||||||
|
myOUsers[syncer.getUserValue(m, key)] = m
|
||||||
|
}
|
||||||
|
|
||||||
newUsers := []*User{}
|
newUsers := []*User{}
|
||||||
for _, oUser := range oUsers {
|
for _, oUser := range oUsers {
|
||||||
primary := syncer.getUserValue(oUser, key)
|
primary := syncer.getUserValue(oUser, key)
|
||||||
@ -71,28 +74,30 @@ func (syncer *Syncer) syncUsers() {
|
|||||||
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
|
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
|
||||||
updatedUser.Hash = oHash
|
updatedUser.Hash = oHash
|
||||||
updatedUser.PreHash = oHash
|
updatedUser.PreHash = oHash
|
||||||
|
|
||||||
|
fmt.Printf("Update from oUser to user: %v\n", updatedUser)
|
||||||
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
|
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
|
||||||
|
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
||||||
_, err = syncer.updateUser(updatedOUser)
|
_, err = syncer.updateUser(updatedOUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update preHash
|
// update preHash
|
||||||
user.PreHash = user.Hash
|
user.PreHash = user.Hash
|
||||||
_, err = SetUserField(user, "pre_hash", user.PreHash)
|
_, err = SetUserField(user, "pre_hash", user.PreHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if user.Hash == oHash {
|
if user.Hash == oHash {
|
||||||
@ -100,17 +105,18 @@ func (syncer *Syncer) syncUsers() {
|
|||||||
user.PreHash = user.Hash
|
user.PreHash = user.Hash
|
||||||
_, err = SetUserField(user, "pre_hash", user.PreHash)
|
_, err = SetUserField(user, "pre_hash", user.PreHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return 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
|
||||||
|
|
||||||
|
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
|
||||||
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
|
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,20 +124,30 @@ func (syncer *Syncer) syncUsers() {
|
|||||||
}
|
}
|
||||||
_, err = AddUsersInBatch(newUsers)
|
_, err = AddUsersInBatch(newUsers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !syncer.IsReadOnly {
|
if !syncer.IsReadOnly {
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
id := user.Id
|
primary := syncer.getUserValue(user, key)
|
||||||
if _, ok := oUserMap[id]; !ok {
|
if _, ok := myOUsers[primary]; !ok {
|
||||||
newOUser := syncer.createOriginalUserFromUser(user)
|
newOUser := syncer.createOriginalUserFromUser(user)
|
||||||
|
|
||||||
|
fmt.Printf("New oUser: %v\n", newOUser)
|
||||||
_, err = syncer.addUser(newOUser)
|
_, err = syncer.addUser(newOUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("New oUser: %v\n", newOUser)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) syncUsersNoError() {
|
||||||
|
err := syncer.syncUsers()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("syncUsersNoError() error: %s\n", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -31,8 +32,8 @@ type Credential struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (syncer *Syncer) getOriginalUsers() ([]*OriginalUser, error) {
|
func (syncer *Syncer) getOriginalUsers() ([]*OriginalUser, error) {
|
||||||
sql := fmt.Sprintf("select * from %s", syncer.getTable())
|
var results []map[string]sql.NullString
|
||||||
results, err := syncer.Ormer.Engine.QueryString(sql)
|
err := syncer.Ormer.Engine.Table(syncer.getTable()).Find(&results)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -64,19 +65,10 @@ func (syncer *Syncer) getOriginalUserMap() ([]*OriginalUser, map[string]*Origina
|
|||||||
|
|
||||||
func (syncer *Syncer) addUser(user *OriginalUser) (bool, error) {
|
func (syncer *Syncer) addUser(user *OriginalUser) (bool, error) {
|
||||||
m := syncer.getMapFromOriginalUser(user)
|
m := syncer.getMapFromOriginalUser(user)
|
||||||
keyString, valueString := syncer.getSqlKeyValueStringFromMap(m)
|
affected, err := syncer.Ormer.Engine.Table(syncer.getTable()).Insert(m)
|
||||||
|
|
||||||
sql := fmt.Sprintf("insert into %s (%s) values (%s)", syncer.getTable(), keyString, valueString)
|
|
||||||
res, err := syncer.Ormer.Engine.Exec(sql)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := res.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,23 +85,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()
|
key := syncer.getKey()
|
||||||
|
|
||||||
m := syncer.getMapFromOriginalUser(user)
|
m := syncer.getMapFromOriginalUser(user)
|
||||||
pkValue := m[key]
|
pkValue := m[key]
|
||||||
delete(m, key)
|
delete(m, key)
|
||||||
setString := syncer.getSqlSetStringFromMap(m)
|
|
||||||
|
|
||||||
sql := fmt.Sprintf("update %s set %s where %s = %s", syncer.getTable(), setString, key, pkValue)
|
affected, err := syncer.Ormer.Engine.Table(syncer.getTable()).ID(pkValue).Update(&m)
|
||||||
res, err := syncer.Ormer.Engine.Exec(sql)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := res.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +168,11 @@ func (syncer *Syncer) initAdapter() {
|
|||||||
if syncer.DatabaseType == "mssql" {
|
if syncer.DatabaseType == "mssql" {
|
||||||
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", syncer.User, syncer.Password, syncer.Host, syncer.Port, syncer.Database)
|
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", syncer.User, syncer.Password, syncer.Host, syncer.Port, syncer.Database)
|
||||||
} else if syncer.DatabaseType == "postgres" {
|
} else if syncer.DatabaseType == "postgres" {
|
||||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", syncer.User, syncer.Password, syncer.Host, syncer.Port, syncer.Database)
|
sslMode := "disable"
|
||||||
|
if syncer.SslMode != "" {
|
||||||
|
sslMode = syncer.SslMode
|
||||||
|
}
|
||||||
|
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=%s dbname=%s", syncer.User, syncer.Password, syncer.Host, syncer.Port, sslMode, syncer.Database)
|
||||||
} else {
|
} else {
|
||||||
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port)
|
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -196,7 +197,7 @@ func (syncer *Syncer) getUserValue(user *User, key string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]string) []*OriginalUser {
|
func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]sql.NullString) []*OriginalUser {
|
||||||
users := []*OriginalUser{}
|
users := []*OriginalUser{}
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
originalUser := &OriginalUser{
|
originalUser := &OriginalUser{
|
||||||
@ -216,11 +217,11 @@ func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]string) []*Or
|
|||||||
names := strings.Split(tableColumnName, "+")
|
names := strings.Split(tableColumnName, "+")
|
||||||
var values []string
|
var values []string
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
values = append(values, result[strings.Trim(name, " ")])
|
values = append(values, result[strings.Trim(name, " ")].String)
|
||||||
}
|
}
|
||||||
value = strings.Join(values, " ")
|
value = strings.Join(values, " ")
|
||||||
} else {
|
} else {
|
||||||
value = result[tableColumnName]
|
value = result[tableColumnName].String
|
||||||
}
|
}
|
||||||
syncer.setUserByKeyValue(originalUser, tableColumn.CasdoorName, value)
|
syncer.setUserByKeyValue(originalUser, tableColumn.CasdoorName, value)
|
||||||
}
|
}
|
||||||
@ -249,9 +250,9 @@ func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]string) []*Or
|
|||||||
// enable
|
// enable
|
||||||
value, ok := result["ENABLED"]
|
value, ok := result["ENABLED"]
|
||||||
if ok {
|
if ok {
|
||||||
originalUser.IsForbidden = !util.ParseBool(value)
|
originalUser.IsForbidden = !util.ParseBool(value.String)
|
||||||
} else {
|
} else {
|
||||||
originalUser.IsForbidden = !util.ParseBool(result["enabled"])
|
originalUser.IsForbidden = !util.ParseBool(result["enabled"].String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,19 +323,3 @@ func (syncer *Syncer) getSqlSetStringFromMap(m map[string]string) string {
|
|||||||
}
|
}
|
||||||
return strings.Join(tokens, ", ")
|
return strings.Join(tokens, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (syncer *Syncer) getSqlKeyValueStringFromMap(m map[string]string) (string, string) {
|
|
||||||
typeMap := syncer.getTableColumnsTypeMap()
|
|
||||||
|
|
||||||
keys := []string{}
|
|
||||||
values := []string{}
|
|
||||||
for k, v := range m {
|
|
||||||
if typeMap[k] == "string" {
|
|
||||||
v = fmt.Sprintf("'%s'", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
keys = append(keys, k)
|
|
||||||
values = append(values, v)
|
|
||||||
}
|
|
||||||
return strings.Join(keys, ", "), strings.Join(values, ", ")
|
|
||||||
}
|
|
||||||
|
@ -185,38 +185,57 @@ func StoreCasTokenForProxyTicket(token *CasAuthenticationSuccess, targetService,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GenerateCasToken(userId string, service string) (string, error) {
|
func GenerateCasToken(userId string, service string) (string, error) {
|
||||||
if user, err := GetUser(userId); err != nil {
|
user, err := GetUser(userId)
|
||||||
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
} else if user != nil {
|
|
||||||
authenticationSuccess := CasAuthenticationSuccess{
|
|
||||||
User: user.Name,
|
|
||||||
Attributes: &CasAttributes{
|
|
||||||
AuthenticationDate: time.Now(),
|
|
||||||
UserAttributes: &CasUserAttributes{},
|
|
||||||
},
|
|
||||||
ProxyGrantingTicket: fmt.Sprintf("PGTIOU-%s", util.GenerateId()),
|
|
||||||
}
|
|
||||||
data, _ := json.Marshal(user)
|
|
||||||
tmp := map[string]string{}
|
|
||||||
json.Unmarshal(data, &tmp)
|
|
||||||
for k, v := range tmp {
|
|
||||||
if v != "" {
|
|
||||||
authenticationSuccess.Attributes.UserAttributes.Attributes = append(authenticationSuccess.Attributes.UserAttributes.Attributes, &CasNamedAttribute{
|
|
||||||
Name: k,
|
|
||||||
Value: v,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
st := fmt.Sprintf("ST-%d", rand.Int())
|
|
||||||
stToServiceResponse.Store(st, &CasAuthenticationSuccessWrapper{
|
|
||||||
AuthenticationSuccess: &authenticationSuccess,
|
|
||||||
Service: service,
|
|
||||||
UserId: userId,
|
|
||||||
})
|
|
||||||
return st, nil
|
|
||||||
} else {
|
|
||||||
return "", fmt.Errorf("invalid user Id")
|
|
||||||
}
|
}
|
||||||
|
if user == nil {
|
||||||
|
return "", fmt.Errorf("The user: %s doesn't exist", userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, _ = GetMaskedUser(user, false)
|
||||||
|
|
||||||
|
authenticationSuccess := CasAuthenticationSuccess{
|
||||||
|
User: user.Name,
|
||||||
|
Attributes: &CasAttributes{
|
||||||
|
AuthenticationDate: time.Now(),
|
||||||
|
UserAttributes: &CasUserAttributes{},
|
||||||
|
},
|
||||||
|
ProxyGrantingTicket: fmt.Sprintf("PGTIOU-%s", util.GenerateId()),
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(user)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := map[string]interface{}{}
|
||||||
|
err = json.Unmarshal(data, &tmp)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range tmp {
|
||||||
|
value := fmt.Sprintf("%v", v)
|
||||||
|
if value == "<nil>" || value == "[]" || value == "map[]" {
|
||||||
|
value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if value != "" {
|
||||||
|
authenticationSuccess.Attributes.UserAttributes.Attributes = append(authenticationSuccess.Attributes.UserAttributes.Attributes, &CasNamedAttribute{
|
||||||
|
Name: k,
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
st := fmt.Sprintf("ST-%d", rand.Int())
|
||||||
|
stToServiceResponse.Store(st, &CasAuthenticationSuccessWrapper{
|
||||||
|
AuthenticationSuccess: &authenticationSuccess,
|
||||||
|
Service: service,
|
||||||
|
UserId: userId,
|
||||||
|
})
|
||||||
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValidationBySaml
|
// GetValidationBySaml
|
||||||
|
@ -194,16 +194,17 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Userinfo struct {
|
type Userinfo struct {
|
||||||
Sub string `json:"sub"`
|
Sub string `json:"sub"`
|
||||||
Iss string `json:"iss"`
|
Iss string `json:"iss"`
|
||||||
Aud string `json:"aud"`
|
Aud string `json:"aud"`
|
||||||
Name string `json:"preferred_username,omitempty"`
|
Name string `json:"preferred_username,omitempty"`
|
||||||
DisplayName string `json:"name,omitempty"`
|
DisplayName string `json:"name,omitempty"`
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
Avatar string `json:"picture,omitempty"`
|
EmailVerified bool `json:"email_verified,omitempty"`
|
||||||
Address string `json:"address,omitempty"`
|
Avatar string `json:"picture,omitempty"`
|
||||||
Phone string `json:"phone,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
Groups []string `json:"groups,omitempty"`
|
Phone string `json:"phone,omitempty"`
|
||||||
|
Groups []string `json:"groups,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ManagedAccount struct {
|
type ManagedAccount struct {
|
||||||
@ -627,12 +628,10 @@ func AddUser(user *User) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.PasswordType == "" && organization.PasswordType != "" {
|
if user.PasswordType == "" || user.PasswordType == "plain" {
|
||||||
user.PasswordType = organization.PasswordType
|
user.UpdateUserPassword(organization)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.UpdateUserPassword(organization)
|
|
||||||
|
|
||||||
err = user.UpdateUserHash()
|
err = user.UpdateUserHash()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -759,6 +758,7 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
|
|||||||
}
|
}
|
||||||
if strings.Contains(scope, "email") {
|
if strings.Contains(scope, "email") {
|
||||||
resp.Email = user.Email
|
resp.Email = user.Email
|
||||||
|
resp.EmailVerified = user.EmailVerified
|
||||||
}
|
}
|
||||||
if strings.Contains(scope, "address") {
|
if strings.Contains(scope, "address") {
|
||||||
resp.Address = user.Location
|
resp.Address = user.Location
|
||||||
|
@ -35,7 +35,7 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
|
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
|
||||||
if strings.Contains(err.Error(), "EOF") || strings.Contains(err.Error(), "no such host") {
|
if strings.Contains(err.Error(), "EOF") || strings.Contains(err.Error(), "no such host") || strings.Contains(err.Error(), "did not properly respond after a period of time") {
|
||||||
return nil, "", nil
|
return nil, "", nil
|
||||||
} else {
|
} else {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
|
@ -320,6 +320,11 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
|||||||
itemsChanged = append(itemsChanged, item)
|
itemsChanged = append(itemsChanged, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldUser.Score != newUser.Score {
|
||||||
|
item := GetAccountItemByName("Score", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
for i := range itemsChanged {
|
for i := range itemsChanged {
|
||||||
if pass, err := CheckAccountItemModifyRule(itemsChanged[i], isAdmin, lang); !pass {
|
if pass, err := CheckAccountItemModifyRule(itemsChanged[i], isAdmin, lang); !pass {
|
||||||
return pass, err
|
return pass, err
|
||||||
|
75
pp/alipay.go
75
pp/alipay.go
@ -16,9 +16,9 @@ package pp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
|
||||||
"github.com/go-pay/gopay"
|
"github.com/go-pay/gopay"
|
||||||
"github.com/go-pay/gopay/alipay"
|
"github.com/go-pay/gopay/alipay"
|
||||||
)
|
)
|
||||||
@ -28,6 +28,11 @@ type AlipayPaymentProvider struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) (*AlipayPaymentProvider, error) {
|
func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) (*AlipayPaymentProvider, error) {
|
||||||
|
// clientId => appId
|
||||||
|
// cert.Certificate => appCertificate
|
||||||
|
// cert.PrivateKey => appPrivateKey
|
||||||
|
// rootCert.Certificate => authorityPublicKey
|
||||||
|
// rootCert.PrivateKey => authorityRootPublicKey
|
||||||
pp := &AlipayPaymentProvider{}
|
pp := &AlipayPaymentProvider{}
|
||||||
|
|
||||||
client, err := alipay.NewClient(appId, appPrivateKey, true)
|
client, err := alipay.NewClient(appId, appPrivateKey, true)
|
||||||
@ -46,54 +51,60 @@ func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey
|
|||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
// pp.Client.DebugSwitch = gopay.DebugOn
|
// pp.Client.DebugSwitch = gopay.DebugOn
|
||||||
|
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
|
pp.Client.SetReturnUrl(returnUrl)
|
||||||
bm.Set("providerName", providerName)
|
pp.Client.SetNotifyUrl(notifyUrl)
|
||||||
bm.Set("productName", productName)
|
bm.Set("subject", joinAttachString([]string{productName, productDisplayName, providerName}))
|
||||||
|
|
||||||
bm.Set("return_url", returnUrl)
|
|
||||||
bm.Set("notify_url", notifyUrl)
|
|
||||||
|
|
||||||
bm.Set("subject", productDisplayName)
|
|
||||||
bm.Set("out_trade_no", paymentName)
|
bm.Set("out_trade_no", paymentName)
|
||||||
bm.Set("total_amount", getPriceString(price))
|
bm.Set("total_amount", priceFloat64ToString(price))
|
||||||
|
|
||||||
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return payUrl, "", nil
|
return payUrl, paymentName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
func (pp *AlipayPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
bm, err := alipay.ParseNotifyToBodyMap(request)
|
bm := gopay.BodyMap{}
|
||||||
|
bm.Set("out_trade_no", orderId)
|
||||||
|
aliRsp, err := pp.Client.TradeQuery(context.Background(), bm)
|
||||||
|
notifyResult := &NotifyResult{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
errRsp := &alipay.ErrorResponse{}
|
||||||
|
unmarshalErr := json.Unmarshal([]byte(err.Error()), errRsp)
|
||||||
|
if unmarshalErr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if errRsp.SubCode == "ACQ.TRADE_NOT_EXIST" {
|
||||||
|
notifyResult.PaymentStatus = PaymentStateCanceled
|
||||||
|
return notifyResult, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
switch aliRsp.Response.TradeStatus {
|
||||||
providerName := bm.Get("providerName")
|
case "WAIT_BUYER_PAY":
|
||||||
productName := bm.Get("productName")
|
notifyResult.PaymentStatus = PaymentStateCreated
|
||||||
|
return notifyResult, nil
|
||||||
productDisplayName := bm.Get("subject")
|
case "TRADE_CLOSED":
|
||||||
paymentName := bm.Get("out_trade_no")
|
notifyResult.PaymentStatus = PaymentStateTimeout
|
||||||
price := util.ParseFloat(bm.Get("total_amount"))
|
return notifyResult, nil
|
||||||
|
case "TRADE_SUCCESS":
|
||||||
ok, err := alipay.VerifySignWithCert(authorityPublicKey, bm)
|
// skip
|
||||||
if err != nil {
|
default:
|
||||||
return nil, err
|
notifyResult.PaymentStatus = PaymentStateError
|
||||||
|
notifyResult.NotifyMessage = fmt.Sprintf("unexpected alipay trade state: %v", aliRsp.Response.TradeStatus)
|
||||||
|
return notifyResult, nil
|
||||||
}
|
}
|
||||||
if !ok {
|
productDisplayName, productName, providerName, _ := parseAttachString(aliRsp.Response.Subject)
|
||||||
return nil, err
|
notifyResult = &NotifyResult{
|
||||||
}
|
|
||||||
notifyResult := &NotifyResult{
|
|
||||||
ProductName: productName,
|
ProductName: productName,
|
||||||
ProductDisplayName: productDisplayName,
|
ProductDisplayName: productDisplayName,
|
||||||
ProviderName: providerName,
|
ProviderName: providerName,
|
||||||
OrderId: orderId,
|
OrderId: orderId,
|
||||||
PaymentStatus: PaymentStatePaid,
|
PaymentStatus: PaymentStatePaid,
|
||||||
Price: price,
|
Price: priceStringToFloat64(aliRsp.Response.TotalAmount),
|
||||||
PaymentName: paymentName,
|
PaymentName: orderId,
|
||||||
}
|
}
|
||||||
return notifyResult, nil
|
return notifyResult, nil
|
||||||
}
|
}
|
||||||
|
10
pp/dummy.go
10
pp/dummy.go
@ -14,11 +14,6 @@
|
|||||||
|
|
||||||
package pp
|
package pp
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DummyPaymentProvider struct{}
|
type DummyPaymentProvider struct{}
|
||||||
|
|
||||||
func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
|
func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
|
||||||
@ -27,11 +22,10 @@ func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pp *DummyPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *DummyPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
payUrl := fmt.Sprintf("/payments/%s/result", paymentName)
|
return returnUrl, "", nil
|
||||||
return payUrl, "", nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *DummyPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
func (pp *DummyPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
return &NotifyResult{
|
return &NotifyResult{
|
||||||
PaymentStatus: PaymentStatePaid,
|
PaymentStatus: PaymentStatePaid,
|
||||||
}, nil
|
}, nil
|
||||||
|
2
pp/gc.go
2
pp/gc.go
@ -216,7 +216,7 @@ func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerN
|
|||||||
return payRespInfo.PayUrl, "", nil
|
return payRespInfo.PayUrl, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
func (pp *GcPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
reqBody := GcRequestBody{}
|
reqBody := GcRequestBody{}
|
||||||
m, err := url.ParseQuery(string(body))
|
m, err := url.ParseQuery(string(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
@ -88,7 +87,7 @@ func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
return ppRsp.Response.Links[1].Href, ppRsp.Response.Id, nil
|
return ppRsp.Response.Links[1].Href, ppRsp.Response.Id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
func (pp *PaypalPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
notifyResult := &NotifyResult{}
|
notifyResult := &NotifyResult{}
|
||||||
captureRsp, err := pp.Client.OrderCapture(context.Background(), orderId, nil)
|
captureRsp, err := pp.Client.OrderCapture(context.Background(), orderId, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -14,10 +14,6 @@
|
|||||||
|
|
||||||
package pp
|
package pp
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PaymentState string
|
type PaymentState string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -44,45 +40,7 @@ type NotifyResult struct {
|
|||||||
|
|
||||||
type PaymentProvider interface {
|
type PaymentProvider interface {
|
||||||
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error)
|
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error)
|
||||||
Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error)
|
Notify(body []byte, orderId string) (*NotifyResult, error)
|
||||||
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
||||||
GetResponseError(err error) string
|
GetResponseError(err error) string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPaymentProvider(typ string, clientId string, clientSecret string, host string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string, clientId2 string) (PaymentProvider, error) {
|
|
||||||
if typ == "Dummy" {
|
|
||||||
pp, err := NewDummyPaymentProvider()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pp, nil
|
|
||||||
} else if typ == "Alipay" {
|
|
||||||
pp, err := NewAlipayPaymentProvider(clientId, appCertificate, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pp, nil
|
|
||||||
} else if typ == "GC" {
|
|
||||||
return NewGcPaymentProvider(clientId, clientSecret, host), nil
|
|
||||||
} else if typ == "WeChat Pay" {
|
|
||||||
pp, err := NewWechatPaymentProvider(clientId, clientSecret, clientId2, appCertificate, appPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pp, nil
|
|
||||||
} else if typ == "PayPal" {
|
|
||||||
pp, err := NewPaypalPaymentProvider(clientId, clientSecret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pp, nil
|
|
||||||
} else if typ == "Stripe" {
|
|
||||||
pp, err := NewStripePaymentProvider(clientId, clientSecret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
@ -16,7 +16,6 @@ package pp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
@ -94,7 +93,7 @@ func (pp *StripePaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
return sCheckout.URL, sCheckout.ID, nil
|
return sCheckout.URL, sCheckout.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *StripePaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
func (pp *StripePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
notifyResult := &NotifyResult{}
|
notifyResult := &NotifyResult{}
|
||||||
sCheckout, err := stripeCheckout.Get(orderId, nil)
|
sCheckout, err := stripeCheckout.Get(orderId, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -49,3 +49,11 @@ func priceFloat64ToInt64(price float64) int64 {
|
|||||||
func priceFloat64ToString(price float64) string {
|
func priceFloat64ToString(price float64) string {
|
||||||
return strconv.FormatFloat(price, 'f', 2, 64)
|
return strconv.FormatFloat(price, 'f', 2, 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func priceStringToFloat64(price string) float64 {
|
||||||
|
f, err := strconv.ParseFloat(price, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ package pp
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"fmt"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/go-pay/gopay"
|
"github.com/go-pay/gopay"
|
||||||
@ -31,17 +31,22 @@ type WechatPayNotifyResponse struct {
|
|||||||
|
|
||||||
type WechatPaymentProvider struct {
|
type WechatPaymentProvider struct {
|
||||||
Client *wechat.ClientV3
|
Client *wechat.ClientV3
|
||||||
appId string
|
AppId string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWechatPaymentProvider(mchId string, apiV3Key string, appId string, mchCertSerialNumber string, privateKey string) (*WechatPaymentProvider, error) {
|
func NewWechatPaymentProvider(mchId string, apiV3Key string, appId string, serialNo string, privateKey string) (*WechatPaymentProvider, error) {
|
||||||
if appId == "" && mchId == "" && mchCertSerialNumber == "" && apiV3Key == "" && privateKey == "" {
|
// https://pay.weixin.qq.com/docs/merchant/products/native-payment/preparation.html
|
||||||
|
// clientId => mchId
|
||||||
|
// clientSecret => apiV3Key
|
||||||
|
// clientId2 => appId
|
||||||
|
|
||||||
|
// appCertificate => serialNo
|
||||||
|
// appPrivateKey => privateKey
|
||||||
|
if appId == "" || mchId == "" || serialNo == "" || apiV3Key == "" || privateKey == "" {
|
||||||
return &WechatPaymentProvider{}, nil
|
return &WechatPaymentProvider{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pp := &WechatPaymentProvider{appId: appId}
|
clientV3, err := wechat.NewClientV3(mchId, serialNo, apiV3Key, privateKey)
|
||||||
|
|
||||||
clientV3, err := wechat.NewClientV3(mchId, mchCertSerialNumber, apiV3Key, privateKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -50,73 +55,70 @@ func NewWechatPaymentProvider(mchId string, apiV3Key string, appId string, mchCe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
pp := &WechatPaymentProvider{
|
||||||
pp.Client = clientV3.SetPlatformCert([]byte(platformCert), serialNo)
|
Client: clientV3.SetPlatformCert([]byte(platformCert), serialNo),
|
||||||
|
AppId: appId,
|
||||||
|
}
|
||||||
|
|
||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *WechatPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
// pp.Client.DebugSwitch = gopay.DebugOn
|
|
||||||
|
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
|
|
||||||
bm.Set("attach", joinAttachString([]string{productDisplayName, productName, providerName}))
|
bm.Set("attach", joinAttachString([]string{productDisplayName, productName, providerName}))
|
||||||
bm.Set("appid", pp.appId)
|
bm.Set("appid", pp.AppId)
|
||||||
bm.Set("description", productDisplayName)
|
bm.Set("description", productDisplayName)
|
||||||
bm.Set("notify_url", notifyUrl)
|
bm.Set("notify_url", notifyUrl)
|
||||||
bm.Set("out_trade_no", paymentName)
|
bm.Set("out_trade_no", paymentName)
|
||||||
bm.SetBodyMap("amount", func(bm gopay.BodyMap) {
|
bm.SetBodyMap("amount", func(bm gopay.BodyMap) {
|
||||||
bm.Set("total", int(price*100))
|
bm.Set("total", priceFloat64ToInt64(price))
|
||||||
bm.Set("currency", "CNY")
|
bm.Set("currency", currency)
|
||||||
})
|
})
|
||||||
|
|
||||||
wxRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
nativeRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
if nativeRsp.Code != wechat.Success {
|
||||||
if wxRsp.Code != wechat.Success {
|
return "", "", errors.New(nativeRsp.Error)
|
||||||
return "", "", errors.New(wxRsp.Error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wxRsp.Response.CodeUrl, "", nil
|
return nativeRsp.Response.CodeUrl, paymentName, nil // Wechat can use paymentName as the OutTradeNo to query order status
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
func (pp *WechatPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
notifyReq, err := wechat.V3ParseNotify(request)
|
notifyResult := &NotifyResult{}
|
||||||
|
queryRsp, err := pp.Client.V3TransactionQueryOrder(context.Background(), wechat.OutTradeNo, orderId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if queryRsp.Code != wechat.Success {
|
||||||
cert := pp.Client.WxPublicKey()
|
return nil, errors.New(queryRsp.Error)
|
||||||
err = notifyReq.VerifySignByPK(cert)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiKey := string(pp.Client.ApiV3Key)
|
switch queryRsp.Response.TradeState {
|
||||||
result, err := notifyReq.DecryptCipherText(apiKey)
|
case "SUCCESS":
|
||||||
if err != nil {
|
// skip
|
||||||
return nil, err
|
case "CLOSED":
|
||||||
|
notifyResult.PaymentStatus = PaymentStateCanceled
|
||||||
|
return notifyResult, nil
|
||||||
|
case "NOTPAY", "USERPAYING": // not-pad: waiting for user to pay; user-paying: user is paying
|
||||||
|
notifyResult.PaymentStatus = PaymentStateCreated
|
||||||
|
return notifyResult, nil
|
||||||
|
default:
|
||||||
|
notifyResult.PaymentStatus = PaymentStateError
|
||||||
|
notifyResult.NotifyMessage = fmt.Sprintf("unexpected wechat trade state: %v", queryRsp.Response.TradeState)
|
||||||
|
return notifyResult, nil
|
||||||
}
|
}
|
||||||
|
productDisplayName, productName, providerName, _ := parseAttachString(queryRsp.Response.Attach)
|
||||||
paymentName := result.OutTradeNo
|
notifyResult = &NotifyResult{
|
||||||
price := float64(result.Amount.PayerTotal) / 100
|
|
||||||
|
|
||||||
productDisplayName, productName, providerName, err := parseAttachString(result.Attach)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyResult := &NotifyResult{
|
|
||||||
ProductName: productName,
|
ProductName: productName,
|
||||||
ProductDisplayName: productDisplayName,
|
ProductDisplayName: productDisplayName,
|
||||||
ProviderName: providerName,
|
ProviderName: providerName,
|
||||||
OrderId: orderId,
|
OrderId: orderId,
|
||||||
Price: price,
|
Price: priceInt64ToFloat64(int64(queryRsp.Response.Amount.Total)),
|
||||||
PaymentStatus: PaymentStatePaid,
|
PaymentStatus: PaymentStatePaid,
|
||||||
PaymentName: paymentName,
|
PaymentName: queryRsp.Response.OutTradeNo,
|
||||||
}
|
}
|
||||||
return notifyResult, nil
|
return notifyResult, nil
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,13 @@ func AutoSigninFilter(ctx *context.Context) {
|
|||||||
|
|
||||||
// GET parameter like "/page?access_token=123" or
|
// GET parameter like "/page?access_token=123" or
|
||||||
// HTTP Bearer token like "Authorization: Bearer 123"
|
// HTTP Bearer token like "Authorization: Bearer 123"
|
||||||
accessToken := util.GetMaxLenStr(ctx.Input.Query("accessToken"), ctx.Input.Query("access_token"), parseBearerToken(ctx))
|
accessToken := ctx.Input.Query("accessToken")
|
||||||
|
if accessToken == "" {
|
||||||
|
accessToken = ctx.Input.Query("access_token")
|
||||||
|
}
|
||||||
|
if accessToken == "" {
|
||||||
|
accessToken = parseBearerToken(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
if accessToken != "" {
|
if accessToken != "" {
|
||||||
token, err := object.GetTokenByAccessToken(accessToken)
|
token, err := object.GetTokenByAccessToken(accessToken)
|
||||||
|
@ -40,6 +40,13 @@ func CorsFilter(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.Request.RequestURI == "/api/userinfo" {
|
||||||
|
ctx.Output.Header(headerAllowOrigin, origin)
|
||||||
|
ctx.Output.Header(headerAllowMethods, "POST, GET, OPTIONS, DELETE")
|
||||||
|
ctx.Output.Header(headerAllowHeaders, "Content-Type, Authorization")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if origin != "" && originConf != "" && origin != originConf {
|
if origin != "" && originConf != "" && origin != originConf {
|
||||||
ok, err := object.IsOriginAllowed(origin)
|
ok, err := object.IsOriginAllowed(origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,6 +63,7 @@ func initAPI() {
|
|||||||
beego.Router("/api/webhook", &controllers.ApiController{}, "POST:HandleOfficialAccountEvent")
|
beego.Router("/api/webhook", &controllers.ApiController{}, "POST:HandleOfficialAccountEvent")
|
||||||
beego.Router("/api/get-webhook-event", &controllers.ApiController{}, "GET:GetWebhookEventType")
|
beego.Router("/api/get-webhook-event", &controllers.ApiController{}, "GET:GetWebhookEventType")
|
||||||
beego.Router("/api/get-captcha-status", &controllers.ApiController{}, "GET:GetCaptchaStatus")
|
beego.Router("/api/get-captcha-status", &controllers.ApiController{}, "GET:GetCaptchaStatus")
|
||||||
|
beego.Router("/api/callback", &controllers.ApiController{}, "POST:Callback")
|
||||||
|
|
||||||
beego.Router("/api/get-organizations", &controllers.ApiController{}, "GET:GetOrganizations")
|
beego.Router("/api/get-organizations", &controllers.ApiController{}, "GET:GetOrganizations")
|
||||||
beego.Router("/api/get-organization", &controllers.ApiController{}, "GET:GetOrganization")
|
beego.Router("/api/get-organization", &controllers.ApiController{}, "GET:GetOrganization")
|
||||||
@ -247,10 +248,10 @@ func initAPI() {
|
|||||||
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/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")
|
||||||
beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "Get:WebAuthnSigninBegin")
|
beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "GET:WebAuthnSigninBegin")
|
||||||
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "Post:WebAuthnSigninFinish")
|
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "POST:WebAuthnSigninFinish")
|
||||||
|
|
||||||
beego.Router("/api/mfa/setup/initiate", &controllers.ApiController{}, "POST:MfaSetupInitiate")
|
beego.Router("/api/mfa/setup/initiate", &controllers.ApiController{}, "POST:MfaSetupInitiate")
|
||||||
beego.Router("/api/mfa/setup/verify", &controllers.ApiController{}, "POST:MfaSetupVerify")
|
beego.Router("/api/mfa/setup/verify", &controllers.ApiController{}, "POST:MfaSetupVerify")
|
||||||
@ -273,7 +274,7 @@ func initAPI() {
|
|||||||
beego.Router("/cas/:organization/:application/proxy", &controllers.RootController{}, "GET:CasProxy")
|
beego.Router("/cas/:organization/:application/proxy", &controllers.RootController{}, "GET:CasProxy")
|
||||||
beego.Router("/cas/:organization/:application/validate", &controllers.RootController{}, "GET:CasValidate")
|
beego.Router("/cas/:organization/:application/validate", &controllers.RootController{}, "GET:CasValidate")
|
||||||
|
|
||||||
beego.Router("/cas/:organization/:application/p3/serviceValidate", &controllers.RootController{}, "GET:CasP3ServiceAndProxyValidate")
|
beego.Router("/cas/:organization/:application/p3/serviceValidate", &controllers.RootController{}, "GET:CasP3ServiceValidate")
|
||||||
beego.Router("/cas/:organization/:application/p3/proxyValidate", &controllers.RootController{}, "GET:CasP3ServiceAndProxyValidate")
|
beego.Router("/cas/:organization/:application/p3/proxyValidate", &controllers.RootController{}, "GET:CasP3ProxyValidate")
|
||||||
beego.Router("/cas/:organization/:application/samlValidate", &controllers.RootController{}, "POST:SamlValidate")
|
beego.Router("/cas/:organization/:application/samlValidate", &controllers.RootController{}, "POST:SamlValidate")
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ package routers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -59,6 +60,13 @@ func StaticFilter(ctx *context.Context) {
|
|||||||
path = "web/build/index.html"
|
path = "web/build/index.html"
|
||||||
}
|
}
|
||||||
if !util.FileExist(path) {
|
if !util.FileExist(path) {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dir = strings.ReplaceAll(dir, "\\", "/")
|
||||||
|
errorText := fmt.Sprintf("The Casdoor frontend HTML file: \"index.html\" was not found, it should be placed at: \"%s/web/build/index.html\". For more information, see: https://casdoor.org/docs/basic/server-installation/#frontend-1", dir)
|
||||||
|
http.ServeContent(ctx.ResponseWriter, ctx.Request, "Casdoor frontend has encountered error...", time.Now(), strings.NewReader(errorText))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -23,76 +24,69 @@ import (
|
|||||||
"github.com/casdoor/oss"
|
"github.com/casdoor/oss"
|
||||||
)
|
)
|
||||||
|
|
||||||
var baseFolder = "files"
|
// LocalFileSystemProvider file system storage
|
||||||
|
type LocalFileSystemProvider struct {
|
||||||
// FileSystem file system storage
|
BaseDir string
|
||||||
type FileSystem struct {
|
|
||||||
Base string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileSystem initialize the local file system storage
|
// NewLocalFileSystemStorageProvider initialize the local file system storage
|
||||||
func NewFileSystem(base string) *FileSystem {
|
func NewLocalFileSystemStorageProvider() *LocalFileSystemProvider {
|
||||||
absBase, err := filepath.Abs(base)
|
baseFolder := "files"
|
||||||
|
absBase, err := filepath.Abs(baseFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("local file system storage's base folder is not initialized")
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &FileSystem{Base: absBase}
|
return &LocalFileSystemProvider{BaseDir: absBase}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFullPath get full path from absolute/relative path
|
// GetFullPath get full path from absolute/relative path
|
||||||
func (fileSystem FileSystem) GetFullPath(path string) string {
|
func (sp LocalFileSystemProvider) GetFullPath(path string) string {
|
||||||
fullPath := path
|
fullPath := path
|
||||||
if !strings.HasPrefix(path, fileSystem.Base) {
|
if !strings.HasPrefix(path, sp.BaseDir) {
|
||||||
fullPath, _ = filepath.Abs(filepath.Join(fileSystem.Base, path))
|
fullPath, _ = filepath.Abs(filepath.Join(sp.BaseDir, path))
|
||||||
}
|
}
|
||||||
return fullPath
|
return fullPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get receive file with given path
|
// Get receive file with given path
|
||||||
func (fileSystem FileSystem) Get(path string) (*os.File, error) {
|
func (sp LocalFileSystemProvider) Get(path string) (*os.File, error) {
|
||||||
return os.Open(fileSystem.GetFullPath(path))
|
return os.Open(sp.GetFullPath(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStream get file as stream
|
// GetStream get file as stream
|
||||||
func (fileSystem FileSystem) GetStream(path string) (io.ReadCloser, error) {
|
func (sp LocalFileSystemProvider) GetStream(path string) (io.ReadCloser, error) {
|
||||||
return os.Open(fileSystem.GetFullPath(path))
|
return os.Open(sp.GetFullPath(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put store a reader into given path
|
// Put store a reader into given path
|
||||||
func (fileSystem FileSystem) Put(path string, reader io.Reader) (*oss.Object, error) {
|
func (sp LocalFileSystemProvider) Put(path string, reader io.Reader) (*oss.Object, error) {
|
||||||
var (
|
fullPath := sp.GetFullPath(path)
|
||||||
fullPath = fileSystem.GetFullPath(path)
|
|
||||||
err = os.MkdirAll(filepath.Dir(fullPath), os.ModePerm)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
err := os.MkdirAll(filepath.Dir(fullPath), os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("Casdoor fails to create folder: \"%s\" for local file system storage provider: %s. Make sure Casdoor process has correct permission to create/access it, or you can create it manually in advance", filepath.Dir(fullPath), err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
dst, err := os.Create(filepath.Clean(fullPath))
|
dst, err := os.Create(filepath.Clean(fullPath))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if seeker, ok := reader.(io.ReadSeeker); ok {
|
if seeker, ok := reader.(io.ReadSeeker); ok {
|
||||||
seeker.Seek(0, 0)
|
seeker.Seek(0, 0)
|
||||||
}
|
}
|
||||||
_, err = io.Copy(dst, reader)
|
_, err = io.Copy(dst, reader)
|
||||||
}
|
}
|
||||||
|
return &oss.Object{Path: path, Name: filepath.Base(path), StorageInterface: sp}, err
|
||||||
return &oss.Object{Path: path, Name: filepath.Base(path), StorageInterface: fileSystem}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete delete file
|
// Delete delete file
|
||||||
func (fileSystem FileSystem) Delete(path string) error {
|
func (sp LocalFileSystemProvider) Delete(path string) error {
|
||||||
return os.Remove(fileSystem.GetFullPath(path))
|
return os.Remove(sp.GetFullPath(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// List list all objects under current path
|
// List list all objects under current path
|
||||||
func (fileSystem FileSystem) List(path string) ([]*oss.Object, error) {
|
func (sp LocalFileSystemProvider) List(path string) ([]*oss.Object, error) {
|
||||||
var (
|
objects := []*oss.Object{}
|
||||||
objects []*oss.Object
|
fullPath := sp.GetFullPath(path)
|
||||||
fullPath = fileSystem.GetFullPath(path)
|
|
||||||
)
|
|
||||||
|
|
||||||
filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error {
|
filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error {
|
||||||
if path == fullPath {
|
if path == fullPath {
|
||||||
@ -102,10 +96,10 @@ func (fileSystem FileSystem) List(path string) ([]*oss.Object, error) {
|
|||||||
if err == nil && !info.IsDir() {
|
if err == nil && !info.IsDir() {
|
||||||
modTime := info.ModTime()
|
modTime := info.ModTime()
|
||||||
objects = append(objects, &oss.Object{
|
objects = append(objects, &oss.Object{
|
||||||
Path: strings.TrimPrefix(path, fileSystem.Base),
|
Path: strings.TrimPrefix(path, sp.BaseDir),
|
||||||
Name: info.Name(),
|
Name: info.Name(),
|
||||||
LastModified: &modTime,
|
LastModified: &modTime,
|
||||||
StorageInterface: fileSystem,
|
StorageInterface: sp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -114,16 +108,12 @@ func (fileSystem FileSystem) List(path string) ([]*oss.Object, error) {
|
|||||||
return objects, nil
|
return objects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEndpoint get endpoint, FileSystem's endpoint is /
|
// GetEndpoint get endpoint, LocalFileSystemProvider's endpoint is /
|
||||||
func (fileSystem FileSystem) GetEndpoint() string {
|
func (sp LocalFileSystemProvider) GetEndpoint() string {
|
||||||
return "/"
|
return "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetURL get public accessible URL
|
// GetURL get public accessible URL
|
||||||
func (fileSystem FileSystem) GetURL(path string) (url string, err error) {
|
func (sp LocalFileSystemProvider) GetURL(path string) (url string, err error) {
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocalFileSystemStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
|
||||||
return NewFileSystem(baseFolder)
|
|
||||||
}
|
|
||||||
|
@ -19,7 +19,7 @@ import "github.com/casdoor/oss"
|
|||||||
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
||||||
switch providerType {
|
switch providerType {
|
||||||
case "Local File System":
|
case "Local File System":
|
||||||
return NewLocalFileSystemStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
return NewLocalFileSystemStorageProvider()
|
||||||
case "AWS S3":
|
case "AWS S3":
|
||||||
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||||
case "MinIO":
|
case "MinIO":
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -186,32 +187,6 @@ func IsStringsEmpty(strs ...string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaxLenStr(strs ...string) string {
|
|
||||||
m := 0
|
|
||||||
i := 0
|
|
||||||
for j, str := range strs {
|
|
||||||
l := len(str)
|
|
||||||
if l > m {
|
|
||||||
m = l
|
|
||||||
i = j
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strs[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMinLenStr(strs ...string) string {
|
|
||||||
m := int(^uint(0) >> 1)
|
|
||||||
i := 0
|
|
||||||
for j, str := range strs {
|
|
||||||
l := len(str)
|
|
||||||
if l < m {
|
|
||||||
m = l
|
|
||||||
i = j
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strs[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadStringFromPath(path string) string {
|
func ReadStringFromPath(path string) string {
|
||||||
data, err := os.ReadFile(filepath.Clean(path))
|
data, err := os.ReadFile(filepath.Clean(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -315,3 +290,22 @@ func ParseIdToString(input interface{}) (string, error) {
|
|||||||
return "", fmt.Errorf("unsupported id type: %T", input)
|
return "", fmt.Errorf("unsupported id type: %T", input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetValueFromDataSourceName(key string, dataSourceName string) string {
|
||||||
|
reg := regexp.MustCompile(key + "=([^ ]+)")
|
||||||
|
matches := reg.FindStringSubmatch(dataSourceName)
|
||||||
|
if len(matches) >= 2 {
|
||||||
|
return matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUsernameFromEmail(email string) string {
|
||||||
|
tokens := strings.Split(email, "@")
|
||||||
|
if len(tokens) == 0 {
|
||||||
|
return uuid.NewString()
|
||||||
|
} else {
|
||||||
|
return tokens[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -189,45 +189,6 @@ func TestIsStrsEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetMaxLenStr(t *testing.T) {
|
|
||||||
scenarios := []struct {
|
|
||||||
description string
|
|
||||||
input []string
|
|
||||||
expected interface{}
|
|
||||||
}{
|
|
||||||
{"Should be return casdoor", []string{"", "casdoor", "casbin"}, "casdoor"},
|
|
||||||
{"Should be return casdoor_jdk", []string{"", "casdoor", "casbin", "casdoor_jdk"}, "casdoor_jdk"},
|
|
||||||
{"Should be return empty string", []string{""}, ""},
|
|
||||||
}
|
|
||||||
for _, scenery := range scenarios {
|
|
||||||
t.Run(scenery.description, func(t *testing.T) {
|
|
||||||
actual := GetMaxLenStr(scenery.input...)
|
|
||||||
assert.Equal(t, scenery.expected, actual, "The returned value not is expected")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetMinLenStr(t *testing.T) {
|
|
||||||
scenarios := []struct {
|
|
||||||
description string
|
|
||||||
input []string
|
|
||||||
expected interface{}
|
|
||||||
}{
|
|
||||||
{"Should be return casbin", []string{"casdoor", "casbin"}, "casbin"},
|
|
||||||
{"Should be return casbin", []string{"casdoor", "casbin", "casdoor_jdk"}, "casbin"},
|
|
||||||
{"Should be return empty string", []string{"a", "", "casbin"}, ""},
|
|
||||||
{"Should be return a", []string{"a", "casdoor", "casbin"}, "a"},
|
|
||||||
{"Should be return a", []string{"casdoor", "a", "casbin"}, "a"},
|
|
||||||
{"Should be return a", []string{"casbin", "casdoor", "a"}, "a"},
|
|
||||||
}
|
|
||||||
for _, scenery := range scenarios {
|
|
||||||
t.Run(scenery.description, func(t *testing.T) {
|
|
||||||
actual := GetMinLenStr(scenery.input...)
|
|
||||||
assert.Equal(t, scenery.expected, actual, "The returned value not is expected")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSnakeString(t *testing.T) {
|
func TestSnakeString(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
description string
|
description string
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
"i18next": "^19.8.9",
|
"i18next": "^19.8.9",
|
||||||
"libphonenumber-js": "^1.10.19",
|
"libphonenumber-js": "^1.10.19",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
|
"qrcode.react": "^3.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-app-polyfill": "^3.0.0",
|
"react-app-polyfill": "^3.0.0",
|
||||||
"react-codemirror2": "^7.2.1",
|
"react-codemirror2": "^7.2.1",
|
||||||
@ -52,7 +53,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cross-env PORT=7001 craco start",
|
"start": "cross-env PORT=7001 craco start",
|
||||||
"build": "craco --max_old_space_size=4096 build",
|
"build": "craco build",
|
||||||
"test": "craco test",
|
"test": "craco test",
|
||||||
"eject": "craco eject",
|
"eject": "craco eject",
|
||||||
"crowdin:sync": "crowdin upload && crowdin download",
|
"crowdin:sync": "crowdin upload && crowdin download",
|
||||||
@ -82,6 +83,9 @@
|
|||||||
"@babel/eslint-parser": "^7.18.9",
|
"@babel/eslint-parser": "^7.18.9",
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@babel/preset-react": "^7.18.6",
|
"@babel/preset-react": "^7.18.6",
|
||||||
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
|
"@testing-library/react": "^9.3.2",
|
||||||
|
"@testing-library/user-event": "^7.1.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cypress": "^12.5.1",
|
"cypress": "^12.5.1",
|
||||||
"eslint": "8.22.0",
|
"eslint": "8.22.0",
|
||||||
@ -91,10 +95,7 @@
|
|||||||
"lint-staged": "^13.0.3",
|
"lint-staged": "^13.0.3",
|
||||||
"stylelint": "^14.11.0",
|
"stylelint": "^14.11.0",
|
||||||
"stylelint-config-recommended-less": "^1.0.4",
|
"stylelint-config-recommended-less": "^1.0.4",
|
||||||
"stylelint-config-standard": "^28.0.0",
|
"stylelint-config-standard": "^28.0.0"
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
|
||||||
"@testing-library/react": "^9.3.2",
|
|
||||||
"@testing-library/user-event": "^7.1.2"
|
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"src/**/*.{css,less}": [
|
"src/**/*.{css,less}": [
|
||||||
|
@ -89,6 +89,7 @@ import ThemeSelect from "./common/select/ThemeSelect";
|
|||||||
import OrganizationSelect from "./common/select/OrganizationSelect";
|
import OrganizationSelect from "./common/select/OrganizationSelect";
|
||||||
import {clearWeb3AuthToken} from "./auth/Web3Auth";
|
import {clearWeb3AuthToken} from "./auth/Web3Auth";
|
||||||
import AccountAvatar from "./account/AccountAvatar";
|
import AccountAvatar from "./account/AccountAvatar";
|
||||||
|
import OpenTour from "./common/OpenTour";
|
||||||
|
|
||||||
const {Header, Footer, Content} = Layout;
|
const {Header, Footer, Content} = Layout;
|
||||||
|
|
||||||
@ -379,6 +380,7 @@ class App extends Component {
|
|||||||
});
|
});
|
||||||
}} />
|
}} />
|
||||||
<LanguageSelect languages={this.state.account.organization.languages} />
|
<LanguageSelect languages={this.state.account.organization.languages} />
|
||||||
|
<OpenTour />
|
||||||
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() &&
|
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() &&
|
||||||
<OrganizationSelect
|
<OrganizationSelect
|
||||||
initValue={Setting.getOrganization()}
|
initValue={Setting.getOrganization()}
|
||||||
@ -448,7 +450,7 @@ class App extends Component {
|
|||||||
|
|
||||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sessions">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
|
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sessions">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
|
||||||
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
|
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
|
||||||
Setting.getItem(<a target="_blank" rel="noreferrer" href={"http://localhost:18001/records"}>{i18next.t("general:Records")}</a>, "/records"),
|
Setting.getItem(<a target="_blank" rel="noreferrer" href={Conf.CasvisorUrl}>{i18next.t("general:Records")}</a>, "/records"),
|
||||||
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
|
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
@ -460,11 +462,17 @@ class App extends Component {
|
|||||||
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
|
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
|
if (Setting.isAdminUser(this.state.account)) {
|
||||||
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
|
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
|
||||||
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
|
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
|
||||||
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
|
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
|
||||||
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
|
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
|
||||||
|
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
|
||||||
|
} else {
|
||||||
|
res.push(Setting.getItem(<Link style={{color: "black"}} to="/syncers">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
|
||||||
|
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
|
||||||
|
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks")]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -563,9 +571,7 @@ class App extends Component {
|
|||||||
|
|
||||||
renderContent() {
|
renderContent() {
|
||||||
const onClick = ({key}) => {
|
const onClick = ({key}) => {
|
||||||
if (key === "/swagger") {
|
if (key !== "/swagger" && key !== "/records") {
|
||||||
window.open(Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger", "_blank");
|
|
||||||
} else {
|
|
||||||
if (this.state.requiredEnableMfa) {
|
if (this.state.requiredEnableMfa) {
|
||||||
Setting.showMessage("info", "Please enable MFA first!");
|
Setting.showMessage("info", "Please enable MFA first!");
|
||||||
} else {
|
} else {
|
||||||
@ -658,7 +664,8 @@ class App extends Component {
|
|||||||
window.location.pathname.startsWith("/cas") ||
|
window.location.pathname.startsWith("/cas") ||
|
||||||
window.location.pathname.startsWith("/auto-signup") ||
|
window.location.pathname.startsWith("/auto-signup") ||
|
||||||
window.location.pathname.startsWith("/select-plan") ||
|
window.location.pathname.startsWith("/select-plan") ||
|
||||||
window.location.pathname.startsWith("/buy-plan");
|
window.location.pathname.startsWith("/buy-plan") ||
|
||||||
|
window.location.pathname.startsWith("/qrcode") ;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPage() {
|
renderPage() {
|
||||||
|
@ -542,7 +542,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("signup:Terms of Use"), i18next.t("signup:Terms of Use - Tooltip"))} :
|
{Setting.getLabel(i18next.t("signup:Terms of Use"), i18next.t("signup:Terms of Use - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.application.termsOfUse} style={{marginBottom: "10px"}} onChange={e => {
|
<Input prefix={<LinkOutlined />} value={this.state.application.termsOfUse} style={{marginBottom: "10px"}} onChange={e => {
|
||||||
this.updateApplicationField("termsOfUse", e.target.value);
|
this.updateApplicationField("termsOfUse", e.target.value);
|
||||||
}} />
|
}} />
|
||||||
<Upload maxCount={1} accept=".html" showUploadList={false}
|
<Upload maxCount={1} accept=".html" showUploadList={false}
|
||||||
|
@ -13,11 +13,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Input, Result, Space} from "antd";
|
import {Button, Input, Result, Space, Tour} from "antd";
|
||||||
import {SearchOutlined} from "@ant-design/icons";
|
import {SearchOutlined} from "@ant-design/icons";
|
||||||
import Highlighter from "react-highlight-words";
|
import Highlighter from "react-highlight-words";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
|
import * as TourConfig from "./TourConfig";
|
||||||
|
|
||||||
class BaseListPage extends React.Component {
|
class BaseListPage extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -33,6 +34,7 @@ class BaseListPage extends React.Component {
|
|||||||
searchText: "",
|
searchText: "",
|
||||||
searchedColumn: "",
|
searchedColumn: "",
|
||||||
isAuthorized: true,
|
isAuthorized: true,
|
||||||
|
isTourVisible: TourConfig.getTourVisible(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,14 +43,23 @@ class BaseListPage extends React.Component {
|
|||||||
this.fetch({pagination});
|
this.fetch({pagination});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleTourChange = () => {
|
||||||
|
this.setState({isTourVisible: TourConfig.getTourVisible()});
|
||||||
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
window.addEventListener("storageOrganizationChanged", this.handleOrganizationChange);
|
window.addEventListener("storageOrganizationChanged", this.handleOrganizationChange);
|
||||||
|
window.addEventListener("storageTourChanged", this.handleTourChange);
|
||||||
if (!Setting.isAdminUser(this.props.account)) {
|
if (!Setting.isAdminUser(this.props.account)) {
|
||||||
Setting.setOrganization("All");
|
Setting.setOrganization("All");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
if (this.state.intervalId !== null) {
|
||||||
|
clearInterval(this.state.intervalId);
|
||||||
|
}
|
||||||
|
window.removeEventListener("storageTourChanged", this.handleTourChange);
|
||||||
window.removeEventListener("storageOrganizationChanged", this.handleOrganizationChange);
|
window.removeEventListener("storageOrganizationChanged", this.handleOrganizationChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,6 +155,37 @@ class BaseListPage extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setIsTourVisible = () => {
|
||||||
|
TourConfig.setIsTourVisible(false);
|
||||||
|
this.setState({isTourVisible: false});
|
||||||
|
};
|
||||||
|
|
||||||
|
getSteps = () => {
|
||||||
|
const nextPathName = TourConfig.getNextUrl();
|
||||||
|
const steps = TourConfig.getSteps();
|
||||||
|
steps.map((item, index) => {
|
||||||
|
if (!index) {
|
||||||
|
item.target = () => document.querySelector("table");
|
||||||
|
} else {
|
||||||
|
item.target = () => document.getElementById(item.id) || null;
|
||||||
|
}
|
||||||
|
if (index === steps.length - 1) {
|
||||||
|
item.nextButtonProps = {
|
||||||
|
children: TourConfig.getNextButtonChild(nextPathName),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return steps;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleTourComplete = () => {
|
||||||
|
const nextPathName = TourConfig.getNextUrl();
|
||||||
|
if (nextPathName !== "") {
|
||||||
|
this.props.history.push("/" + nextPathName);
|
||||||
|
TourConfig.setIsTourVisible(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.state.isAuthorized) {
|
if (!this.state.isAuthorized) {
|
||||||
return (
|
return (
|
||||||
@ -161,6 +203,17 @@ class BaseListPage extends React.Component {
|
|||||||
{
|
{
|
||||||
this.renderTable(this.state.data)
|
this.renderTable(this.state.data)
|
||||||
}
|
}
|
||||||
|
<Tour
|
||||||
|
open={Setting.isMobile() ? false : this.state.isTourVisible}
|
||||||
|
onClose={this.setIsTourVisible}
|
||||||
|
steps={this.getSteps()}
|
||||||
|
indicatorsRender={(current, total) => (
|
||||||
|
<span>
|
||||||
|
{current + 1} / {total}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
onFinish={this.handleTourComplete}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
export const DefaultApplication = "app-built-in";
|
export const DefaultApplication = "app-built-in";
|
||||||
|
|
||||||
|
export const CasvisorUrl = "https://github.com/casbin/casvisor";
|
||||||
|
|
||||||
export const ShowGithubCorner = false;
|
export const ShowGithubCorner = false;
|
||||||
export const IsDemoMode = false;
|
export const IsDemoMode = false;
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import CasLogout from "./auth/CasLogout";
|
|||||||
import {authConfig} from "./auth/Auth";
|
import {authConfig} from "./auth/Auth";
|
||||||
import ProductBuyPage from "./ProductBuyPage";
|
import ProductBuyPage from "./ProductBuyPage";
|
||||||
import PaymentResultPage from "./PaymentResultPage";
|
import PaymentResultPage from "./PaymentResultPage";
|
||||||
|
import QrCodePage from "./QrCodePage";
|
||||||
|
|
||||||
class EntryPage extends React.Component {
|
class EntryPage extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -113,6 +114,7 @@ class EntryPage extends React.Component {
|
|||||||
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
||||||
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
||||||
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
||||||
|
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -101,6 +101,21 @@ class PaymentListPage extends BaseListPage {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Organization"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
width: "120px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("owner"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<Link to={`/organizations/${text}`}>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Provider"),
|
title: i18next.t("general:Provider"),
|
||||||
dataIndex: "provider",
|
dataIndex: "provider",
|
||||||
@ -117,21 +132,6 @@ class PaymentListPage extends BaseListPage {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: i18next.t("general:Organization"),
|
|
||||||
dataIndex: "owner",
|
|
||||||
key: "owner",
|
|
||||||
width: "120px",
|
|
||||||
sorter: true,
|
|
||||||
...this.getColumnSearchProps("owner"),
|
|
||||||
render: (text, record, index) => {
|
|
||||||
return (
|
|
||||||
<Link to={`/organizations/${text}`}>
|
|
||||||
{text}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: i18next.t("general:User"),
|
title: i18next.t("general:User"),
|
||||||
dataIndex: "user",
|
dataIndex: "user",
|
||||||
@ -264,7 +264,7 @@ class PaymentListPage extends BaseListPage {
|
|||||||
value = params.type;
|
value = params.type;
|
||||||
}
|
}
|
||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
PaymentBackend.getPayments(Setting.getRequestOrganization(this.props.account), Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
PaymentBackend.getPayments(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -101,7 +101,7 @@ class PaymentResultPage extends React.Component {
|
|||||||
payment: payment,
|
payment: payment,
|
||||||
});
|
});
|
||||||
if (payment.state === "Created") {
|
if (payment.state === "Created") {
|
||||||
if (["PayPal", "Stripe"].includes(payment.type)) {
|
if (["PayPal", "Stripe", "Alipay"].includes(payment.type)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
timeout: setTimeout(async() => {
|
timeout: setTimeout(async() => {
|
||||||
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user