mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-23 22:53:31 +08:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
87e2b97813 | |||
d9e44c1f2d | |||
dfa4503f24 | |||
f7fb32893b | |||
66d0758b13 | |||
46ad0fe0be | |||
6b637e3b2e | |||
3354945119 | |||
19c4416f10 | |||
2077db9091 | |||
800f0ed249 | |||
6161040c67 | |||
1d785e61c6 | |||
0329d24867 | |||
fb6f3623ee | |||
eb448bd043 | |||
ea88839db9 | |||
cb95f6977a | |||
9067df92a7 | |||
bfa2ab63ad | |||
505054b0eb | |||
f95ce13b82 | |||
5315f16a48 | |||
d054f3e001 | |||
b158b840bd | |||
b16f1807b3 | |||
d0cce1bf7a | |||
9892cd20ab | |||
d1f31dd327 | |||
94743246a1 | |||
39ad1bc593 | |||
d97f833d2a | |||
948fa911e2 | |||
6073a0f63d | |||
91268bca70 | |||
23dbb0b926 |
@ -16,6 +16,7 @@ ARG USER=casdoor
|
|||||||
|
|
||||||
RUN sed -i 's/https/http/' /etc/apk/repositories
|
RUN sed -i 's/https/http/' /etc/apk/repositories
|
||||||
RUN apk add --update sudo
|
RUN apk add --update sudo
|
||||||
|
RUN apk add tzdata
|
||||||
RUN apk add curl
|
RUN apk add curl
|
||||||
RUN apk add ca-certificates && update-ca-certificates
|
RUN apk add ca-certificates && update-ca-certificates
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ socks5Proxy = "127.0.0.1:10808"
|
|||||||
verificationCodeTimeout = 10
|
verificationCodeTimeout = 10
|
||||||
initScore = 0
|
initScore = 0
|
||||||
logPostOnly = true
|
logPostOnly = true
|
||||||
|
isUsernameLowered = false
|
||||||
origin =
|
origin =
|
||||||
originFrontend =
|
originFrontend =
|
||||||
staticBaseUrl = "https://cdn.casbin.org"
|
staticBaseUrl = "https://cdn.casbin.org"
|
||||||
|
@ -44,6 +44,8 @@ type Response struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Captcha struct {
|
type Captcha struct {
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
AppKey string `json:"appKey"`
|
AppKey string `json:"appKey"`
|
||||||
Scene string `json:"scene"`
|
Scene string `json:"scene"`
|
||||||
@ -259,22 +261,24 @@ func (c *ApiController) Signup() {
|
|||||||
c.SetSessionUsername(user.GetId())
|
c.SetSessionUsername(user.GetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = object.DisableVerificationCode(authForm.Email)
|
if authForm.Email != "" {
|
||||||
if err != nil {
|
err = object.DisableVerificationCode(authForm.Email)
|
||||||
c.ResponseError(err.Error())
|
if err != nil {
|
||||||
return
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = object.DisableVerificationCode(checkPhone)
|
if checkPhone != "" {
|
||||||
if err != nil {
|
err = object.DisableVerificationCode(checkPhone)
|
||||||
c.ResponseError(err.Error())
|
if err != nil {
|
||||||
return
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
c.Ctx.Input.SetParam("recordSignup", "true")
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
|
|
||||||
userId := user.GetId()
|
userId := user.GetId()
|
||||||
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
||||||
@ -532,10 +536,12 @@ func (c *ApiController) GetCaptcha() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(Captcha{Type: captchaProvider.Type, CaptchaId: id, CaptchaImage: img})
|
c.ResponseOk(Captcha{Owner: captchaProvider.Owner, Name: captchaProvider.Name, Type: captchaProvider.Type, CaptchaId: id, CaptchaImage: img})
|
||||||
return
|
return
|
||||||
} else if captchaProvider.Type != "" {
|
} else if captchaProvider.Type != "" {
|
||||||
c.ResponseOk(Captcha{
|
c.ResponseOk(Captcha{
|
||||||
|
Owner: captchaProvider.Owner,
|
||||||
|
Name: captchaProvider.Name,
|
||||||
Type: captchaProvider.Type,
|
Type: captchaProvider.Type,
|
||||||
SubType: captchaProvider.SubType,
|
SubType: captchaProvider.SubType,
|
||||||
ClientId: captchaProvider.ClientId,
|
ClientId: captchaProvider.ClientId,
|
||||||
|
@ -508,10 +508,7 @@ func (c *ApiController) Login() {
|
|||||||
|
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
}
|
}
|
||||||
} else if authForm.Provider != "" {
|
} else if authForm.Provider != "" {
|
||||||
var application *object.Application
|
var application *object.Application
|
||||||
@ -632,10 +629,7 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
||||||
// Sign up via OAuth
|
// Sign up via OAuth
|
||||||
if application.EnableLinkWithEmail {
|
if application.EnableLinkWithEmail {
|
||||||
@ -768,16 +762,8 @@ func (c *ApiController) Login() {
|
|||||||
|
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
c.Ctx.Input.SetParam("recordSignup", "true")
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
|
|
||||||
record2 := object.NewRecord(c.Ctx)
|
|
||||||
record2.Action = "signup"
|
|
||||||
record2.Organization = application.Organization
|
|
||||||
record2.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record2) })
|
|
||||||
} else if provider.Category == "SAML" {
|
} else if provider.Category == "SAML" {
|
||||||
// TODO: since we get the user info from SAML response, we can try to create the user
|
// TODO: since we get the user info from SAML response, we can try to create the user
|
||||||
resp = &Response{Status: "error", Msg: fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id))}
|
resp = &Response{Status: "error", Msg: fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id))}
|
||||||
@ -879,10 +865,7 @@ func (c *ApiController) Login() {
|
|||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
c.setMfaUserSession("")
|
c.setMfaUserSession("")
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
} else {
|
} else {
|
||||||
if c.GetSessionUsername() != "" {
|
if c.GetSessionUsername() != "" {
|
||||||
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
||||||
@ -901,10 +884,7 @@ func (c *ApiController) Login() {
|
|||||||
user := c.getCurrentUser()
|
user := c.getCurrentUser()
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
} else {
|
} else {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(authForm)))
|
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(authForm)))
|
||||||
return
|
return
|
||||||
|
@ -68,7 +68,7 @@ func (c *ApiController) GetCerts() {
|
|||||||
// GetGlobalCerts
|
// GetGlobalCerts
|
||||||
// @Title GetGlobalCerts
|
// @Title GetGlobalCerts
|
||||||
// @Tag Cert API
|
// @Tag Cert API
|
||||||
// @Description get globle certs
|
// @Description get global certs
|
||||||
// @Success 200 {array} object.Cert The Response object
|
// @Success 200 {array} object.Cert The Response object
|
||||||
// @router /get-global-certs [get]
|
// @router /get-global-certs [get]
|
||||||
func (c *ApiController) GetGlobalCerts() {
|
func (c *ApiController) GetGlobalCerts() {
|
||||||
|
@ -43,13 +43,20 @@ func (c *ApiController) GetGroups() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
if withTree == "true" {
|
|
||||||
c.ResponseOk(object.ConvertToTreeData(groups, owner))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.ResponseOk(groups)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = object.ExtendGroupsWithUsers(groups)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if withTree == "true" {
|
||||||
|
c.ResponseOk(object.ConvertToTreeData(groups, owner))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(groups)
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
count, err := object.GetGroupCount(owner, field, value)
|
count, err := object.GetGroupCount(owner, field, value)
|
||||||
@ -64,6 +71,12 @@ func (c *ApiController) GetGroups() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
err = object.ExtendGroupsWithUsers(groups)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.ResponseOk(groups, paginator.Nums())
|
c.ResponseOk(groups, paginator.Nums())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +97,13 @@ func (c *ApiController) GetGroup() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = object.ExtendGroupWithUsers(group)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.ResponseOk(group)
|
c.ResponseOk(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +141,20 @@ func (c *ApiController) GetProvider() {
|
|||||||
c.ResponseOk(object.GetMaskedProvider(provider, isMaskEnabled))
|
c.ResponseOk(object.GetMaskedProvider(provider, isMaskEnabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) requireProviderPermission(provider *object.Provider) bool {
|
||||||
|
isGlobalAdmin, user := c.isGlobalAdmin()
|
||||||
|
if isGlobalAdmin {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if provider.Owner == "admin" || user.Owner != provider.Owner {
|
||||||
|
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateProvider
|
// UpdateProvider
|
||||||
// @Title UpdateProvider
|
// @Title UpdateProvider
|
||||||
// @Tag Provider API
|
// @Tag Provider API
|
||||||
@ -159,6 +173,11 @@ func (c *ApiController) UpdateProvider() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ok := c.requireProviderPermission(&provider)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.UpdateProvider(id, &provider))
|
c.Data["json"] = wrapActionResponse(object.UpdateProvider(id, &provider))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
@ -184,11 +203,17 @@ func (c *ApiController) AddProvider() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkQuotaForProvider(int(count)); err != nil {
|
err = checkQuotaForProvider(int(count))
|
||||||
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ok := c.requireProviderPermission(&provider)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.AddProvider(&provider))
|
c.Data["json"] = wrapActionResponse(object.AddProvider(&provider))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
@ -208,6 +233,11 @@ func (c *ApiController) DeleteProvider() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ok := c.requireProviderPermission(&provider)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.DeleteProvider(&provider))
|
c.Data["json"] = wrapActionResponse(object.DeleteProvider(&provider))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -113,23 +113,25 @@ func (c *ApiController) SendEmail() {
|
|||||||
|
|
||||||
content := emailForm.Content
|
content := emailForm.Content
|
||||||
if content == "" {
|
if content == "" {
|
||||||
code := "123456"
|
content = provider.Content
|
||||||
|
}
|
||||||
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
code := "123456"
|
||||||
content = strings.Replace(provider.Content, "%s", code, 1)
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
if !strings.HasPrefix(userId, "app/") {
|
content = strings.Replace(content, "%s", code, 1)
|
||||||
var user *object.User
|
userString := "Hi"
|
||||||
user, err = object.GetUser(userId)
|
if !strings.HasPrefix(userId, "app/") {
|
||||||
if err != nil {
|
var user *object.User
|
||||||
c.ResponseError(err.Error())
|
user, err = object.GetUser(userId)
|
||||||
return
|
if err != nil {
|
||||||
}
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
if user != nil {
|
}
|
||||||
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
if user != nil {
|
||||||
}
|
userString = user.GetFriendlyName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
content = strings.Replace(content, "%{user.friendlyName}", userString, 1)
|
||||||
|
|
||||||
for _, receiver := range emailForm.Receivers {
|
for _, receiver := range emailForm.Receivers {
|
||||||
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
||||||
|
@ -164,6 +164,7 @@ func (c *ApiController) GetOAuthToken() {
|
|||||||
code := c.Input().Get("code")
|
code := c.Input().Get("code")
|
||||||
verifier := c.Input().Get("code_verifier")
|
verifier := c.Input().Get("code_verifier")
|
||||||
scope := c.Input().Get("scope")
|
scope := c.Input().Get("scope")
|
||||||
|
nonce := c.Input().Get("nonce")
|
||||||
username := c.Input().Get("username")
|
username := c.Input().Get("username")
|
||||||
password := c.Input().Get("password")
|
password := c.Input().Get("password")
|
||||||
tag := c.Input().Get("tag")
|
tag := c.Input().Get("tag")
|
||||||
@ -197,6 +198,9 @@ func (c *ApiController) GetOAuthToken() {
|
|||||||
if scope == "" {
|
if scope == "" {
|
||||||
scope = tokenRequest.Scope
|
scope = tokenRequest.Scope
|
||||||
}
|
}
|
||||||
|
if nonce == "" {
|
||||||
|
nonce = tokenRequest.Nonce
|
||||||
|
}
|
||||||
if username == "" {
|
if username == "" {
|
||||||
username = tokenRequest.Username
|
username = tokenRequest.Username
|
||||||
}
|
}
|
||||||
@ -216,7 +220,7 @@ func (c *ApiController) GetOAuthToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
host := c.Ctx.Request.Host
|
host := c.Ctx.Request.Host
|
||||||
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, nonce, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@ -21,6 +21,7 @@ type TokenRequest struct {
|
|||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Verifier string `json:"code_verifier"`
|
Verifier string `json:"code_verifier"`
|
||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
|
@ -111,46 +111,44 @@ func newEmail(fromAddress string, toAddress string, subject string, content stri
|
|||||||
Subject: subject,
|
Subject: subject,
|
||||||
HTML: content,
|
HTML: content,
|
||||||
},
|
},
|
||||||
Importance: importanceNormal,
|
Importance: importanceNormal,
|
||||||
|
Attachments: []Attachment{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AzureACSEmailProvider) sendEmail(e *Email) error {
|
func (a *AzureACSEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||||
postBody, err := json.Marshal(e)
|
email := newEmail(fromAddress, toAddress, subject, content)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("email JSON marshall failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyBuffer := bytes.NewBuffer(postBody)
|
postBody, err := json.Marshal(email)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
endpoint := strings.TrimSuffix(a.Endpoint, "/")
|
endpoint := strings.TrimSuffix(a.Endpoint, "/")
|
||||||
url := fmt.Sprintf("%s/emails:send?api-version=2023-03-31", endpoint)
|
url := fmt.Sprintf("%s/emails:send?api-version=2023-03-31", endpoint)
|
||||||
|
|
||||||
|
bodyBuffer := bytes.NewBuffer(postBody)
|
||||||
req, err := http.NewRequest("POST", url, bodyBuffer)
|
req, err := http.NewRequest("POST", url, bodyBuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating AzureACS API request: %s", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign the request using the AzureACS access key and HMAC-SHA256
|
|
||||||
err = signRequestHMAC(a.AccessKey, req)
|
err = signRequestHMAC(a.AccessKey, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error signing AzureACS API request: %s", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
// Some important header
|
|
||||||
req.Header.Set("repeatability-request-id", uuid.New().String())
|
req.Header.Set("repeatability-request-id", uuid.New().String())
|
||||||
req.Header.Set("repeatability-first-sent", time.Now().UTC().Format(http.TimeFormat))
|
req.Header.Set("repeatability-first-sent", time.Now().UTC().Format(http.TimeFormat))
|
||||||
|
|
||||||
// Send request
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error sending AzureACS API request: %s", err)
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Response error Handling
|
|
||||||
if resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusUnauthorized {
|
if resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusUnauthorized {
|
||||||
commError := ErrorResponse{}
|
commError := ErrorResponse{}
|
||||||
|
|
||||||
@ -159,11 +157,11 @@ func (a *AzureACSEmailProvider) sendEmail(e *Email) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("error sending email: %s", commError.Error.Message)
|
return fmt.Errorf("status code: %d, error message: %s", resp.StatusCode, commError.Error.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusAccepted {
|
if resp.StatusCode != http.StatusAccepted {
|
||||||
return fmt.Errorf("error sending email: status: %d", resp.StatusCode)
|
return fmt.Errorf("status code: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -221,9 +219,3 @@ func GetHmac(content string, key []byte) string {
|
|||||||
|
|
||||||
return base64.StdEncoding.EncodeToString(hmac.Sum(nil))
|
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)
|
|
||||||
}
|
|
||||||
|
@ -23,6 +23,8 @@ func GetEmailProvider(typ string, clientId string, clientSecret string, host str
|
|||||||
return NewAzureACSEmailProvider(clientSecret, host)
|
return NewAzureACSEmailProvider(clientSecret, host)
|
||||||
} else if typ == "Custom HTTP Email" {
|
} else if typ == "Custom HTTP Email" {
|
||||||
return NewHttpEmailProvider(endpoint, method)
|
return NewHttpEmailProvider(endpoint, method)
|
||||||
|
} else if typ == "SendGrid" {
|
||||||
|
return NewSendgridEmailProvider(clientSecret)
|
||||||
} else {
|
} else {
|
||||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
||||||
}
|
}
|
||||||
|
68
email/sendgrid.go
Normal file
68
email/sendgrid.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2024 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 (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sendgrid/sendgrid-go"
|
||||||
|
"github.com/sendgrid/sendgrid-go/helpers/mail"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SendgridEmailProvider struct {
|
||||||
|
ApiKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendgridResponseBody struct {
|
||||||
|
Errors []struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Field interface{} `json:"field"`
|
||||||
|
Help interface{} `json:"help"`
|
||||||
|
} `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSendgridEmailProvider(apiKey string) *SendgridEmailProvider {
|
||||||
|
return &SendgridEmailProvider{ApiKey: apiKey}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SendgridEmailProvider) Send(fromAddress string, fromName, toAddress string, subject string, content string) error {
|
||||||
|
from := mail.NewEmail(fromName, fromAddress)
|
||||||
|
to := mail.NewEmail("", toAddress)
|
||||||
|
message := mail.NewSingleEmail(from, subject, to, "", content)
|
||||||
|
client := sendgrid.NewSendClient(s.ApiKey)
|
||||||
|
response, err := client.Send(message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode >= 300 {
|
||||||
|
var responseBody SendgridResponseBody
|
||||||
|
err = json.Unmarshal([]byte(response.Body), &responseBody)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
messages := []string{}
|
||||||
|
for _, sendgridError := range responseBody.Errors {
|
||||||
|
messages = append(messages, sendgridError.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("SendGrid status code: %d, error message: %s", response.StatusCode, strings.Join(messages, " | "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
1
go.mod
1
go.mod
@ -45,6 +45,7 @@ require (
|
|||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/russellhaering/gosaml2 v0.9.0
|
github.com/russellhaering/gosaml2 v0.9.0
|
||||||
github.com/russellhaering/goxmldsig v1.2.0
|
github.com/russellhaering/goxmldsig v1.2.0
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible // indirect
|
||||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
||||||
|
3
go.sum
3
go.sum
@ -1907,8 +1907,11 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
|
|||||||
github.com/scim2/filter-parser/v2 v2.2.0 h1:QGadEcsmypxg8gYChRSM2j1edLyE/2j72j+hdmI4BJM=
|
github.com/scim2/filter-parser/v2 v2.2.0 h1:QGadEcsmypxg8gYChRSM2j1edLyE/2j72j+hdmI4BJM=
|
||||||
github.com/scim2/filter-parser/v2 v2.2.0/go.mod h1:jWnkDToqX/Y0ugz0P5VvpVEUKcWcyHHj+X+je9ce5JA=
|
github.com/scim2/filter-parser/v2 v2.2.0/go.mod h1:jWnkDToqX/Y0ugz0P5VvpVEUKcWcyHHj+X+je9ce5JA=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||||
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||||
github.com/sendgrid/sendgrid-go v3.13.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
github.com/sendgrid/sendgrid-go v3.13.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
||||||
|
1
main.go
1
main.go
@ -59,6 +59,7 @@ func main() {
|
|||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
|
||||||
|
beego.InsertFilter("*", beego.AfterExec, routers.AfterRecordMessage, false)
|
||||||
|
|
||||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||||
beego.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"
|
beego.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"
|
||||||
|
@ -17,6 +17,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -30,13 +31,13 @@ type Group struct {
|
|||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||||
|
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
Manager string `xorm:"varchar(100)" json:"manager"`
|
Manager string `xorm:"varchar(100)" json:"manager"`
|
||||||
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
|
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
|
||||||
Type string `xorm:"varchar(100)" json:"type"`
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
ParentId string `xorm:"varchar(100)" json:"parentId"`
|
ParentId string `xorm:"varchar(100)" json:"parentId"`
|
||||||
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
|
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
|
||||||
Users []*User `xorm:"-" json:"users"`
|
Users []string `xorm:"-" json:"users"`
|
||||||
|
|
||||||
Title string `json:"title,omitempty"`
|
Title string `json:"title,omitempty"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
@ -288,6 +289,55 @@ func GetGroupUsers(groupId string) ([]*User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExtendGroupWithUsers(group *Group) error {
|
||||||
|
if group == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := GetUsers(group.Owner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
groupId := group.GetId()
|
||||||
|
userIds := []string{}
|
||||||
|
for _, user := range users {
|
||||||
|
if util.InSlice(user.Groups, groupId) {
|
||||||
|
userIds = append(userIds, user.GetId())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group.Users = userIds
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtendGroupsWithUsers(groups []*Group) error {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
errChan := make(chan error, len(groups))
|
||||||
|
|
||||||
|
for _, group := range groups {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(group *Group) {
|
||||||
|
defer wg.Done()
|
||||||
|
err := ExtendGroupWithUsers(group)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
}(group)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
close(errChan)
|
||||||
|
|
||||||
|
for err := range errChan {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func GroupChangeTrigger(oldName, newName string) error {
|
func GroupChangeTrigger(oldName, newName string) error {
|
||||||
session := ormer.Engine.NewSession()
|
session := ormer.Engine.NewSession()
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -34,7 +35,12 @@ type Record struct {
|
|||||||
casvisorsdk.Record
|
casvisorsdk.Record
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
type Response struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
|
||||||
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
|
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
|
||||||
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
|
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
|
||||||
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
|
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
|
||||||
@ -47,6 +53,17 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
|||||||
object = string(ctx.Input.RequestBody)
|
object = string(ctx.Input.RequestBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
respBytes, err := json.Marshal(ctx.Input.Data()["json"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp Response
|
||||||
|
err = json.Unmarshal(respBytes, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
language := ctx.Request.Header.Get("Accept-Language")
|
language := ctx.Request.Header.Get("Accept-Language")
|
||||||
if len(language) > 2 {
|
if len(language) > 2 {
|
||||||
language = language[0:2]
|
language = language[0:2]
|
||||||
@ -63,10 +80,10 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
|||||||
Action: action,
|
Action: action,
|
||||||
Language: languageCode,
|
Language: languageCode,
|
||||||
Object: object,
|
Object: object,
|
||||||
Response: "",
|
Response: fmt.Sprintf("{status:\"%s\", msg:\"%s\"}", resp.Status, resp.Msg),
|
||||||
IsTriggered: false,
|
IsTriggered: false,
|
||||||
}
|
}
|
||||||
return &record
|
return &record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddRecord(record *casvisorsdk.Record) bool {
|
func AddRecord(record *casvisorsdk.Record) bool {
|
||||||
@ -123,6 +140,12 @@ func GetRecords() ([]*casvisorsdk.Record, error) {
|
|||||||
|
|
||||||
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
|
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
|
||||||
records := []*casvisorsdk.Record{}
|
records := []*casvisorsdk.Record{}
|
||||||
|
|
||||||
|
if sortField == "" || sortOrder == "" {
|
||||||
|
sortField = "id"
|
||||||
|
sortOrder = "descend"
|
||||||
|
}
|
||||||
|
|
||||||
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&records, filterRecord)
|
err := session.Find(&records, filterRecord)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -142,6 +165,25 @@ func GetRecordsByField(record *casvisorsdk.Record) ([]*casvisorsdk.Record, error
|
|||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CopyRecord(record *casvisorsdk.Record) *casvisorsdk.Record {
|
||||||
|
res := &casvisorsdk.Record{
|
||||||
|
Owner: record.Owner,
|
||||||
|
Name: record.Name,
|
||||||
|
CreatedTime: record.CreatedTime,
|
||||||
|
Organization: record.Organization,
|
||||||
|
ClientIp: record.ClientIp,
|
||||||
|
User: record.User,
|
||||||
|
Method: record.Method,
|
||||||
|
RequestUri: record.RequestUri,
|
||||||
|
Action: record.Action,
|
||||||
|
Language: record.Language,
|
||||||
|
Object: record.Object,
|
||||||
|
Response: record.Response,
|
||||||
|
IsTriggered: record.IsTriggered,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func getFilteredWebhooks(webhooks []*Webhook, organization string, action string) []*Webhook {
|
func getFilteredWebhooks(webhooks []*Webhook, organization string, action string) []*Webhook {
|
||||||
res := []*Webhook{}
|
res := []*Webhook{}
|
||||||
for _, webhook := range webhooks {
|
for _, webhook := range webhooks {
|
||||||
|
@ -189,7 +189,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, refreshToken string, tag string, avatar string, lang string) (interface{}, error) {
|
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, nonce string, username string, password string, host string, refreshToken string, tag string, avatar string, lang string) (interface{}, error) {
|
||||||
application, err := GetApplicationByClientId(clientId)
|
application, err := GetApplicationByClientId(clientId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -220,6 +220,8 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
|
|||||||
token, tokenError, err = GetPasswordToken(application, username, password, scope, host)
|
token, tokenError, err = GetPasswordToken(application, username, password, scope, host)
|
||||||
case "client_credentials": // Client Credentials Grant
|
case "client_credentials": // Client Credentials Grant
|
||||||
token, tokenError, err = GetClientCredentialsToken(application, clientSecret, scope, host)
|
token, tokenError, err = GetClientCredentialsToken(application, clientSecret, scope, host)
|
||||||
|
case "token", "id_token": // Implicit Grant
|
||||||
|
token, tokenError, err = GetImplicitToken(application, username, scope, nonce, host)
|
||||||
case "refresh_token":
|
case "refresh_token":
|
||||||
refreshToken2, err := RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
refreshToken2, err := RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -582,6 +584,33 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
|||||||
return token, nil, nil
|
return token, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetImplicitToken
|
||||||
|
// Implicit flow
|
||||||
|
func GetImplicitToken(application *Application, username string, scope string, nonce string, host string) (*Token, *TokenError, error) {
|
||||||
|
user, err := GetUserByFields(application.Organization, username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
return nil, &TokenError{
|
||||||
|
Error: InvalidGrant,
|
||||||
|
ErrorDescription: "the user does not exist",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if user.IsForbidden {
|
||||||
|
return nil, &TokenError{
|
||||||
|
Error: InvalidGrant,
|
||||||
|
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := GetTokenByUser(application, user, scope, nonce, host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return token, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetTokenByUser
|
// GetTokenByUser
|
||||||
// Implicit flow
|
// Implicit flow
|
||||||
func GetTokenByUser(application *Application, user *User, scope string, nonce string, host string) (*Token, error) {
|
func GetTokenByUser(application *Application, user *User, scope string, nonce string, host string) (*Token, error) {
|
||||||
|
@ -675,7 +675,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
"owner", "display_name", "avatar", "first_name", "last_name",
|
"owner", "display_name", "avatar", "first_name", "last_name",
|
||||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
"location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||||
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids",
|
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids",
|
||||||
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
|
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret", "mfa_phone_enabled", "mfa_email_enabled",
|
||||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||||
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
||||||
@ -833,6 +833,11 @@ func AddUser(user *User) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isUsernameLowered := conf.GetConfigBool("isUsernameLowered")
|
||||||
|
if isUsernameLowered {
|
||||||
|
user.Name = strings.ToLower(user.Name)
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := ormer.Engine.Insert(user)
|
affected, err := ormer.Engine.Insert(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -846,6 +851,8 @@ func AddUsers(users []*User) (bool, error) {
|
|||||||
return false, fmt.Errorf("no users are provided")
|
return false, fmt.Errorf("no users are provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isUsernameLowered := conf.GetConfigBool("isUsernameLowered")
|
||||||
|
|
||||||
// organization := GetOrganizationByUser(users[0])
|
// organization := GetOrganizationByUser(users[0])
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
// this function is only used for syncer or batch upload, so no need to encrypt the password
|
// this function is only used for syncer or batch upload, so no need to encrypt the password
|
||||||
@ -869,6 +876,10 @@ func AddUsers(users []*User) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isUsernameLowered {
|
||||||
|
user.Name = strings.ToLower(user.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := ormer.Engine.Insert(users)
|
affected, err := ormer.Engine.Insert(users)
|
||||||
|
@ -92,9 +92,12 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
|
|||||||
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
content := strings.Replace(provider.Content, "%s", code, 1)
|
content := strings.Replace(provider.Content, "%s", code, 1)
|
||||||
|
|
||||||
|
userString := "Hi"
|
||||||
if user != nil {
|
if user != nil {
|
||||||
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
userString = user.GetFriendlyName()
|
||||||
}
|
}
|
||||||
|
content = strings.Replace(content, "%{user.friendlyName}", userString, 1)
|
||||||
|
|
||||||
err := IsAllowSend(user, remoteAddr, provider.Category)
|
err := IsAllowSend(user, remoteAddr, provider.Category)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -187,14 +190,17 @@ func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult
|
|||||||
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet, or has already been used!")}, nil
|
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet, or has already been used!")}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
|
timeoutInMinutes, err := conf.GetConfigInt64("verificationCodeTimeout")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
if now-record.Time > timeout*60 {
|
if now-record.Time > timeoutInMinutes*60*10 {
|
||||||
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)}, nil
|
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet!")}, nil
|
||||||
|
}
|
||||||
|
if now-record.Time > timeoutInMinutes*60 {
|
||||||
|
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeoutInMinutes)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if record.Code != code {
|
if record.Code != code {
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/object"
|
||||||
|
|
||||||
"github.com/beego/beego/context"
|
"github.com/beego/beego/context"
|
||||||
"github.com/casdoor/casdoor/authz"
|
"github.com/casdoor/casdoor/authz"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -211,5 +213,17 @@ func ApiFilter(ctx *context.Context) {
|
|||||||
|
|
||||||
if !isAllowed {
|
if !isAllowed {
|
||||||
denyRequest(ctx)
|
denyRequest(ctx)
|
||||||
|
record, err := object.NewRecord(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Organization = subOwner
|
||||||
|
record.User = subName // auth:Unauthorized operation
|
||||||
|
record.Response = fmt.Sprintf("{status:\"error\", msg:\"%s\"}", T(ctx, "auth:Unauthorized operation"))
|
||||||
|
|
||||||
|
util.SafeGoroutine(func() {
|
||||||
|
object.AddRecord(record)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,12 @@
|
|||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/beego/beego/context"
|
"github.com/beego/beego/context"
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getUser(ctx *context.Context) (username string) {
|
func getUser(ctx *context.Context) (username string) {
|
||||||
@ -60,12 +63,49 @@ func RecordMessage(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
record := object.NewRecord(ctx)
|
|
||||||
|
|
||||||
userId := getUser(ctx)
|
userId := getUser(ctx)
|
||||||
|
|
||||||
|
ctx.Input.SetParam("recordUserId", userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AfterRecordMessage(ctx *context.Context) {
|
||||||
|
record, err := object.NewRecord(ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("AfterRecordMessage() error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userId := ctx.Input.Params()["recordUserId"]
|
||||||
if userId != "" {
|
if userId != "" {
|
||||||
record.Organization, record.User = util.GetOwnerAndNameFromId(userId)
|
record.Organization, record.User = util.GetOwnerAndNameFromId(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
var record2 *casvisorsdk.Record
|
||||||
|
recordSignup := ctx.Input.Params()["recordSignup"]
|
||||||
|
if recordSignup == "true" {
|
||||||
|
record2 = object.CopyRecord(record)
|
||||||
|
record2.Action = "new-user"
|
||||||
|
|
||||||
|
var user *object.User
|
||||||
|
user, err = object.GetUser(userId)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("AfterRecordMessage() error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
err = fmt.Errorf("the user: %s is not found", userId)
|
||||||
|
fmt.Printf("AfterRecordMessage() error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
record2.Object = util.StructToJson(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
util.SafeGoroutine(func() {
|
||||||
|
object.AddRecord(record)
|
||||||
|
|
||||||
|
if record2 != nil {
|
||||||
|
object.AddRecord(record2)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ func (sp LocalFileSystemProvider) Put(path string, reader io.Reader) (*oss.Objec
|
|||||||
|
|
||||||
dst, err := os.Create(filepath.Clean(fullPath))
|
dst, err := os.Create(filepath.Clean(fullPath))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
defer dst.Close()
|
||||||
if seeker, ok := reader.(io.ReadSeeker); ok {
|
if seeker, ok := reader.(io.ReadSeeker); ok {
|
||||||
seeker.Seek(0, 0)
|
seeker.Seek(0, 0)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/cssinjs": "1.16.1",
|
"@ant-design/cssinjs": "^1.10.1",
|
||||||
"@ant-design/icons": "^4.7.0",
|
"@ant-design/icons": "^4.7.0",
|
||||||
"@craco/craco": "^6.4.5",
|
"@craco/craco": "^6.4.5",
|
||||||
"@crowdin/cli": "^3.7.10",
|
"@crowdin/cli": "^3.7.10",
|
||||||
|
@ -41,6 +41,7 @@ setTwoToneColor("rgb(87,52,211)");
|
|||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.setThemeAlgorithm();
|
||||||
let storageThemeAlgorithm = [];
|
let storageThemeAlgorithm = [];
|
||||||
try {
|
try {
|
||||||
storageThemeAlgorithm = localStorage.getItem("themeAlgorithm") ? JSON.parse(localStorage.getItem("themeAlgorithm")) : ["default"];
|
storageThemeAlgorithm = localStorage.getItem("themeAlgorithm") ? JSON.parse(localStorage.getItem("themeAlgorithm")) : ["default"];
|
||||||
@ -157,6 +158,15 @@ class App extends Component {
|
|||||||
return Setting.getLogo(themes);
|
return Setting.getLogo(themes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setThemeAlgorithm() {
|
||||||
|
const currentUrl = window.location.href;
|
||||||
|
const url = new URL(currentUrl);
|
||||||
|
const themeType = url.searchParams.get("theme");
|
||||||
|
if (themeType === "dark" || themeType === "default") {
|
||||||
|
localStorage.setItem("themeAlgorithm", JSON.stringify([themeType]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setLanguage(account) {
|
setLanguage(account) {
|
||||||
const language = account?.language;
|
const language = account?.language;
|
||||||
if (language !== null && language !== "" && language !== i18next.language) {
|
if (language !== null && language !== "" && language !== i18next.language) {
|
||||||
@ -364,6 +374,7 @@ class App extends Component {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onLoginSuccess={(redirectUrl) => {
|
onLoginSuccess={(redirectUrl) => {
|
||||||
|
window.google?.accounts?.id?.cancel();
|
||||||
if (redirectUrl) {
|
if (redirectUrl) {
|
||||||
localStorage.setItem("mfaRedirectUrl", redirectUrl);
|
localStorage.setItem("mfaRedirectUrl", redirectUrl);
|
||||||
}
|
}
|
||||||
|
@ -456,6 +456,10 @@ class ApplicationEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col span={1} >
|
<Col span={1} >
|
||||||
<Switch checked={this.state.application.enableSigninSession} onChange={checked => {
|
<Switch checked={this.state.application.enableSigninSession} onChange={checked => {
|
||||||
|
if (!checked) {
|
||||||
|
this.updateApplicationField("enableAutoSignin", false);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateApplicationField("enableSigninSession", checked);
|
this.updateApplicationField("enableSigninSession", checked);
|
||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
@ -466,6 +470,11 @@ class ApplicationEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col span={1} >
|
<Col span={1} >
|
||||||
<Switch checked={this.state.application.enableAutoSignin} onChange={checked => {
|
<Switch checked={this.state.application.enableAutoSignin} onChange={checked => {
|
||||||
|
if (!this.state.application.enableSigninSession && checked) {
|
||||||
|
Setting.showMessage("error", i18next.t("application:Please enable \"Signin session\" first before enabling \"Auto signin\""));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.updateApplicationField("enableAutoSignin", checked);
|
this.updateApplicationField("enableAutoSignin", checked);
|
||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -177,6 +177,16 @@ class GroupEditPage extends React.Component {
|
|||||||
)} />
|
)} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Users"), i18next.t("general:Users - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col style={{marginTop: "5px"}} span={22} >
|
||||||
|
{
|
||||||
|
Setting.getTags(this.state.group.users, "users")
|
||||||
|
}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||||
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :
|
@ -195,6 +195,17 @@ class GroupListPage extends BaseListPage {
|
|||||||
</Link>;
|
</Link>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Users"),
|
||||||
|
dataIndex: "users",
|
||||||
|
key: "users",
|
||||||
|
// width: "200px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("users"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return Setting.getTags(text, "users");
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Action"),
|
title: i18next.t("general:Action"),
|
||||||
dataIndex: "",
|
dataIndex: "",
|
@ -34,8 +34,8 @@ import OrganizationListPage from "./OrganizationListPage";
|
|||||||
import OrganizationEditPage from "./OrganizationEditPage";
|
import OrganizationEditPage from "./OrganizationEditPage";
|
||||||
import UserListPage from "./UserListPage";
|
import UserListPage from "./UserListPage";
|
||||||
import GroupTreePage from "./GroupTreePage";
|
import GroupTreePage from "./GroupTreePage";
|
||||||
import GroupListPage from "./GroupList";
|
import GroupListPage from "./GroupListPage";
|
||||||
import GroupEditPage from "./GroupEdit";
|
import GroupEditPage from "./GroupEditPage";
|
||||||
import UserEditPage from "./UserEditPage";
|
import UserEditPage from "./UserEditPage";
|
||||||
import InvitationListPage from "./InvitationListPage";
|
import InvitationListPage from "./InvitationListPage";
|
||||||
import InvitationEditPage from "./InvitationEditPage";
|
import InvitationEditPage from "./InvitationEditPage";
|
||||||
|
@ -244,7 +244,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||||
}
|
}
|
||||||
case "Email":
|
case "Email":
|
||||||
if (provider.type === "Azure ACS") {
|
if (provider.type === "Azure ACS" || provider.type === "SendGrid") {
|
||||||
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
|
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
|
||||||
} else {
|
} else {
|
||||||
return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"));
|
return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"));
|
||||||
@ -729,7 +729,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{
|
{
|
||||||
(this.state.provider.category === "Storage" && this.state.provider.type === "Google Cloud Storage") ||
|
(this.state.provider.category === "Storage" && this.state.provider.type === "Google Cloud Storage") ||
|
||||||
(this.state.provider.category === "Email" && this.state.provider.type === "Azure ACS") ||
|
(this.state.provider.category === "Email" && (this.state.provider.type === "Azure ACS" || this.state.provider.type === "SendGrid")) ||
|
||||||
(this.state.provider.category === "Notification" && (this.state.provider.type === "Line" || this.state.provider.type === "Telegram" || this.state.provider.type === "Bark" || this.state.provider.type === "Discord" || this.state.provider.type === "Slack" || this.state.provider.type === "Pushbullet" || this.state.provider.type === "Pushover" || this.state.provider.type === "Lark" || this.state.provider.type === "Microsoft Teams")) ? null : (
|
(this.state.provider.category === "Notification" && (this.state.provider.type === "Line" || this.state.provider.type === "Telegram" || this.state.provider.type === "Bark" || this.state.provider.type === "Discord" || this.state.provider.type === "Slack" || this.state.provider.type === "Pushbullet" || this.state.provider.type === "Pushover" || this.state.provider.type === "Lark" || this.state.provider.type === "Microsoft Teams")) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
@ -770,7 +770,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{
|
{
|
||||||
(this.state.provider.type === "WeChat Pay") || (this.state.provider.category === "Email" && this.state.provider.type === "Azure ACS") ? null : (
|
(this.state.provider.type === "WeChat Pay") || (this.state.provider.category === "Email" && (this.state.provider.type === "Azure ACS" || this.state.provider.type === "SendGrid")) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{this.getClientSecret2Label(this.state.provider)} :
|
{this.getClientSecret2Label(this.state.provider)} :
|
||||||
@ -985,17 +985,19 @@ class ProviderEditPage extends React.Component {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : this.state.provider.category === "Email" ? (
|
) : this.state.provider.category === "Email" ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Row style={{marginTop: "20px"}} >
|
{["SendGrid"].includes(this.state.provider.type) ? null : (
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Row style={{marginTop: "20px"}} >
|
||||||
{Setting.getLabel(i18next.t("provider:Host"), i18next.t("provider:Host - Tooltip"))} :
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
</Col>
|
{Setting.getLabel(i18next.t("provider:Host"), i18next.t("provider:Host - Tooltip"))} :
|
||||||
<Col span={22} >
|
</Col>
|
||||||
<Input prefix={<LinkOutlined />} value={this.state.provider.host} onChange={e => {
|
<Col span={22} >
|
||||||
this.updateProviderField("host", e.target.value);
|
<Input prefix={<LinkOutlined />} value={this.state.provider.host} onChange={e => {
|
||||||
}} />
|
this.updateProviderField("host", e.target.value);
|
||||||
</Col>
|
}} />
|
||||||
</Row>
|
</Col>
|
||||||
{["Azure ACS"].includes(this.state.provider.type) ? null : (
|
</Row>
|
||||||
|
)}
|
||||||
|
{["Azure ACS", "SendGrid"].includes(this.state.provider.type) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Port"), i18next.t("provider:Port - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Port"), i18next.t("provider:Port - Tooltip"))} :
|
||||||
@ -1007,7 +1009,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{["Azure ACS"].includes(this.state.provider.type) ? null : (
|
{["Azure ACS", "SendGrid"].includes(this.state.provider.type) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Disable SSL"), i18next.t("provider:Disable SSL - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Disable SSL"), i18next.t("provider:Disable SSL - Tooltip"))} :
|
||||||
@ -1073,7 +1075,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
this.updateProviderField("receiver", e.target.value);
|
this.updateProviderField("receiver", e.target.value);
|
||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
{["Azure ACS"].includes(this.state.provider.type) ? null : (
|
{["Azure ACS", "SendGrid"].includes(this.state.provider.type) ? null : (
|
||||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} >
|
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} >
|
||||||
{i18next.t("provider:Test SMTP Connection")}
|
{i18next.t("provider:Test SMTP Connection")}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -151,6 +151,14 @@ class RecordListPage extends BaseListPage {
|
|||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("language"),
|
...this.getColumnSearchProps("language"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("record:Response"),
|
||||||
|
dataIndex: "response",
|
||||||
|
key: "response",
|
||||||
|
width: "90px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("response"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("record:Object"),
|
title: i18next.t("record:Object"),
|
||||||
dataIndex: "object",
|
dataIndex: "object",
|
||||||
@ -179,7 +187,7 @@ class RecordListPage extends BaseListPage {
|
|||||||
sorter: true,
|
sorter: true,
|
||||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
if (!["signup", "login", "logout", "update-user"].includes(record.action)) {
|
if (!["signup", "login", "logout", "update-user", "new-user"].includes(record.action)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,10 @@ export const OtherProviderInfo = {
|
|||||||
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
||||||
url: "https://learn.microsoft.com/zh-cn/azure/communication-services",
|
url: "https://learn.microsoft.com/zh-cn/azure/communication-services",
|
||||||
},
|
},
|
||||||
|
"SendGrid": {
|
||||||
|
logo: `${StaticBaseUrl}/img/email_sendgrid.png`,
|
||||||
|
url: "https://sendgrid.com/",
|
||||||
|
},
|
||||||
"Custom HTTP Email": {
|
"Custom HTTP Email": {
|
||||||
logo: `${StaticBaseUrl}/img/social_default.png`,
|
logo: `${StaticBaseUrl}/img/social_default.png`,
|
||||||
url: "https://casdoor.org/docs/provider/email/overview",
|
url: "https://casdoor.org/docs/provider/email/overview",
|
||||||
@ -1015,6 +1019,7 @@ export function getProviderTypeOptions(category) {
|
|||||||
{id: "SUBMAIL", name: "SUBMAIL"},
|
{id: "SUBMAIL", name: "SUBMAIL"},
|
||||||
{id: "Mailtrap", name: "Mailtrap"},
|
{id: "Mailtrap", name: "Mailtrap"},
|
||||||
{id: "Azure ACS", name: "Azure ACS"},
|
{id: "Azure ACS", name: "Azure ACS"},
|
||||||
|
{id: "SendGrid", name: "SendGrid"},
|
||||||
{id: "Custom HTTP Email", name: "Custom HTTP Email"},
|
{id: "Custom HTTP Email", name: "Custom HTTP Email"},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Card, Col, Form, Input, InputNumber, List, Result, Row, Select, Space, Spin, Switch, Tag} from "antd";
|
import {Button, Card, Col, Form, Input, InputNumber, List, Result, Row, Select, Space, Spin, Switch, Tag, Tooltip} from "antd";
|
||||||
import {withRouter} from "react-router-dom";
|
import {withRouter} from "react-router-dom";
|
||||||
import {TotpMfaType} from "./auth/MfaSetupPage";
|
import {TotpMfaType} from "./auth/MfaSetupPage";
|
||||||
import * as GroupBackend from "./backend/GroupBackend";
|
import * as GroupBackend from "./backend/GroupBackend";
|
||||||
@ -407,7 +407,17 @@ class UserEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<PasswordModal user={this.state.user} userName={this.state.userName} organization={this.getUserOrganization()} account={this.props.account} disabled={disabled} />
|
{
|
||||||
|
(this.state.user.name === this.state.userName) ? (
|
||||||
|
<PasswordModal user={this.state.user} userName={this.state.userName} organization={this.getUserOrganization()} account={this.props.account} disabled={disabled} />
|
||||||
|
) : (
|
||||||
|
<Tooltip placement={"topLeft"} title={i18next.t("user:You have changed the username, please save your change first before modifying the password")}>
|
||||||
|
<span>
|
||||||
|
<PasswordModal user={this.state.user} userName={this.state.userName} organization={this.getUserOrganization()} account={this.props.account} disabled={true} />
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@ import * as VerificationBackend from "./backend/VerificationBackend";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Table} from "antd";
|
import {Switch, Table} from "antd";
|
||||||
|
|
||||||
class VerificationListPage extends BaseListPage {
|
class VerificationListPage extends BaseListPage {
|
||||||
newVerification() {
|
newVerification() {
|
||||||
@ -35,26 +35,48 @@ class VerificationListPage extends BaseListPage {
|
|||||||
renderTable(verifications) {
|
renderTable(verifications) {
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Name"),
|
title: i18next.t("general:Organization"),
|
||||||
dataIndex: "name",
|
dataIndex: "owner",
|
||||||
key: "name",
|
key: "owner",
|
||||||
width: "150px",
|
width: "120px",
|
||||||
fixed: "left",
|
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("name"),
|
...this.getColumnSearchProps("owner"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
|
if (text === "admin") {
|
||||||
|
return `(${i18next.t("general:empty")})`;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/syncers/${text}`}>
|
<Link to={`/organizations/${text}`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Name"),
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
width: "260px",
|
||||||
|
fixed: "left",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("name"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Created time"),
|
||||||
|
dataIndex: "createdTime",
|
||||||
|
key: "createdTime",
|
||||||
|
width: "160px",
|
||||||
|
sorter: true,
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return Setting.getFormattedDate(text);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("provider:Type"),
|
title: i18next.t("provider:Type"),
|
||||||
dataIndex: "type",
|
dataIndex: "type",
|
||||||
key: "type",
|
key: "type",
|
||||||
width: "120px",
|
width: "90px",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("type"),
|
...this.getColumnSearchProps("type"),
|
||||||
},
|
},
|
||||||
@ -67,7 +89,7 @@ class VerificationListPage extends BaseListPage {
|
|||||||
...this.getColumnSearchProps("user"),
|
...this.getColumnSearchProps("user"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/users/${record.owner}/${text}`}>
|
<Link to={`/users/${text}`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -88,6 +110,26 @@ class VerificationListPage extends BaseListPage {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Client IP"),
|
||||||
|
dataIndex: "remoteAddr",
|
||||||
|
key: "remoteAddr",
|
||||||
|
width: "100px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("remoteAddr"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
let clientIp = text;
|
||||||
|
if (clientIp.endsWith(": ")) {
|
||||||
|
clientIp = clientIp.slice(0, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a target="_blank" rel="noreferrer" href={`https://db-ip.com/${clientIp}`}>
|
||||||
|
{clientIp}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("verification:Receiver"),
|
title: i18next.t("verification:Receiver"),
|
||||||
dataIndex: "receiver",
|
dataIndex: "receiver",
|
||||||
@ -100,28 +142,20 @@ class VerificationListPage extends BaseListPage {
|
|||||||
title: i18next.t("login:Verification code"),
|
title: i18next.t("login:Verification code"),
|
||||||
dataIndex: "code",
|
dataIndex: "code",
|
||||||
key: "code",
|
key: "code",
|
||||||
width: "120px",
|
width: "150px",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("code"),
|
...this.getColumnSearchProps("code"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Timestamp"),
|
title: i18next.t("verification:Is used"),
|
||||||
dataIndex: "time",
|
dataIndex: "isUsed",
|
||||||
key: "time",
|
key: "isUsed",
|
||||||
width: "160px",
|
width: "90px",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return Setting.getFormattedDate(text * 1000);
|
return (
|
||||||
},
|
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
|
||||||
},
|
);
|
||||||
{
|
|
||||||
title: i18next.t("general:Created time"),
|
|
||||||
dataIndex: "createdTime",
|
|
||||||
key: "createdTime",
|
|
||||||
width: "160px",
|
|
||||||
sorter: true,
|
|
||||||
render: (text, record, index) => {
|
|
||||||
return Setting.getFormattedDate(text);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -156,7 +190,7 @@ class VerificationListPage extends BaseListPage {
|
|||||||
value = params.type;
|
value = params.type;
|
||||||
}
|
}
|
||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
VerificationBackend.getVerifications("admin", Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
VerificationBackend.getVerifications("", 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,
|
||||||
|
@ -275,7 +275,7 @@ class WebhookEditPage extends React.Component {
|
|||||||
}} >
|
}} >
|
||||||
{
|
{
|
||||||
(
|
(
|
||||||
["signup", "login", "logout"].concat(this.getApiPaths()).map((option, index) => {
|
["signup", "login", "logout", "new-user"].concat(this.getApiPaths()).map((option, index) => {
|
||||||
return (
|
return (
|
||||||
<Option key={option} value={option}>{option}</Option>
|
<Option key={option} value={option}>{option}</Option>
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,7 @@ import * as Setting from "../Setting";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import {SendCodeInput} from "../common/SendCodeInput";
|
import {SendCodeInput} from "../common/SendCodeInput";
|
||||||
import * as UserBackend from "../backend/UserBackend";
|
import * as UserBackend from "../backend/UserBackend";
|
||||||
import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons";
|
import {ArrowLeftOutlined, CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons";
|
||||||
import CustomGithubCorner from "../common/CustomGithubCorner";
|
import CustomGithubCorner from "../common/CustomGithubCorner";
|
||||||
import {withRouter} from "react-router-dom";
|
import {withRouter} from "react-router-dom";
|
||||||
import * as PasswordChecker from "../common/PasswordChecker";
|
import * as PasswordChecker from "../common/PasswordChecker";
|
||||||
@ -443,6 +443,18 @@ class ForgetPage extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stepBack() {
|
||||||
|
if (this.state.current > 0) {
|
||||||
|
this.setState({
|
||||||
|
current: this.state.current - 1,
|
||||||
|
});
|
||||||
|
} else if (this.props.history.length > 1) {
|
||||||
|
this.props.history.goBack();
|
||||||
|
} else {
|
||||||
|
Setting.redirectToLoginPage(this.getApplicationObj(), this.props.history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const application = this.getApplicationObj();
|
const application = this.getApplicationObj();
|
||||||
if (application === undefined) {
|
if (application === undefined) {
|
||||||
@ -456,6 +468,9 @@ class ForgetPage extends React.Component {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CustomGithubCorner />
|
<CustomGithubCorner />
|
||||||
<div className="forget-content" style={{padding: Setting.isMobile() ? "0" : null, boxShadow: Setting.isMobile() ? "none" : null}}>
|
<div className="forget-content" style={{padding: Setting.isMobile() ? "0" : null, boxShadow: Setting.isMobile() ? "none" : null}}>
|
||||||
|
<Button type="text" style={{position: "relative", left: Setting.isMobile() ? "10px" : "-90px", top: 0}} size={"large"} onClick={() => {this.stepBack();}}>
|
||||||
|
<ArrowLeftOutlined style={{fontSize: "24px"}} />
|
||||||
|
</Button>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} style={{justifyContent: "center"}}>
|
<Col span={24} style={{justifyContent: "center"}}>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -52,7 +52,7 @@ export function GoogleOneTapLoginVirtualButton(prop) {
|
|||||||
redirectUri = `${redirectUri}?state=${state}&code=${encodeURIComponent(code)}`;
|
redirectUri = `${redirectUri}?state=${state}&code=${encodeURIComponent(code)}`;
|
||||||
Setting.goToLink(redirectUri);
|
Setting.goToLink(redirectUri);
|
||||||
},
|
},
|
||||||
disableCancelOnUnmount: true,
|
disableCancelOnUnmount: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1173,7 +1173,7 @@ class LoginPage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{height: 300}}>
|
<div style={{height: 300, minWidth: 320}}>
|
||||||
{renderChoiceBox()}
|
{renderChoiceBox()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Form, Input, Result} from "antd";
|
import {Button, Form, Input, Radio, Result, Row} from "antd";
|
||||||
import * as Setting from "../Setting";
|
import * as Setting from "../Setting";
|
||||||
import * as AuthBackend from "./AuthBackend";
|
import * as AuthBackend from "./AuthBackend";
|
||||||
import * as ProviderButton from "./ProviderButton";
|
import * as ProviderButton from "./ProviderButton";
|
||||||
@ -71,6 +71,7 @@ class SignupPage extends React.Component {
|
|||||||
applicationName: (props.applicationName ?? props.match?.params?.applicationName) ?? null,
|
applicationName: (props.applicationName ?? props.match?.params?.applicationName) ?? null,
|
||||||
email: "",
|
email: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
|
emailOrPhoneMode: "",
|
||||||
countryCode: "",
|
countryCode: "",
|
||||||
emailCode: "",
|
emailCode: "",
|
||||||
phoneCode: "",
|
phoneCode: "",
|
||||||
@ -360,130 +361,176 @@ class SignupPage extends React.Component {
|
|||||||
<RegionSelect onChange={(value) => {this.setState({region: value});}} />
|
<RegionSelect onChange={(value) => {this.setState({region: value});}} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
} else if (signupItem.name === "Email") {
|
} else if (signupItem.name === "Email" || signupItem.name === "Phone" || signupItem.name === "Email or Phone" || signupItem.name === "Phone or Email") {
|
||||||
return (
|
const renderEmailItem = () => {
|
||||||
<React.Fragment>
|
return (
|
||||||
<Form.Item
|
<React.Fragment>
|
||||||
name="email"
|
|
||||||
label={signupItem.label ? signupItem.label : i18next.t("general:Email")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: required,
|
|
||||||
message: i18next.t("signup:Please input your Email!"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
validator: (_, value) => {
|
|
||||||
if (this.state.email !== "" && !Setting.isValidEmail(this.state.email)) {
|
|
||||||
this.setState({validEmail: false});
|
|
||||||
return Promise.reject(i18next.t("signup:The input is not valid Email!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({validEmail: true});
|
|
||||||
return Promise.resolve();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input placeholder={signupItem.placeholder} disabled={this.state.invitation !== undefined && this.state.invitation.email !== ""} onChange={e => this.setState({email: e.target.value})} />
|
|
||||||
</Form.Item>
|
|
||||||
{
|
|
||||||
signupItem.rule !== "No verification" &&
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="emailCode"
|
name="email"
|
||||||
label={signupItem.label ? signupItem.label : i18next.t("code:Email code")}
|
label={signupItem.label ? signupItem.label : i18next.t("general:Email")}
|
||||||
rules={[{
|
|
||||||
required: required,
|
|
||||||
message: i18next.t("code:Please input your verification code!"),
|
|
||||||
}]}
|
|
||||||
>
|
|
||||||
<SendCodeInput
|
|
||||||
disabled={!this.state.validEmail}
|
|
||||||
method={"signup"}
|
|
||||||
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]}
|
|
||||||
application={application}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
} else if (signupItem.name === "Phone") {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<Form.Item label={signupItem.label ? signupItem.label : i18next.t("general:Phone")} required={required}>
|
|
||||||
<Input.Group compact>
|
|
||||||
<Form.Item
|
|
||||||
name="countryCode"
|
|
||||||
noStyle
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: required,
|
|
||||||
message: i18next.t("signup:Please select your country code!"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<CountryCodeSelect
|
|
||||||
style={{width: "35%"}}
|
|
||||||
countryCodes={this.getApplicationObj().organizationObj.countryCodes}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="phone"
|
|
||||||
dependencies={["countryCode"]}
|
|
||||||
noStyle
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: required,
|
|
||||||
message: i18next.t("signup:Please input your phone number!"),
|
|
||||||
},
|
|
||||||
({getFieldValue}) => ({
|
|
||||||
validator: (_, value) => {
|
|
||||||
if (!required && !value) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value && !Setting.isValidPhone(value, getFieldValue("countryCode"))) {
|
|
||||||
this.setState({validPhone: false});
|
|
||||||
return Promise.reject(i18next.t("signup:The input is not valid Phone!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({validPhone: true});
|
|
||||||
return Promise.resolve();
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
placeholder={signupItem.placeholder}
|
|
||||||
style={{width: "65%"}}
|
|
||||||
disabled={this.state.invitation !== undefined && this.state.invitation.phone !== ""}
|
|
||||||
onChange={e => this.setState({phone: e.target.value})}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Input.Group>
|
|
||||||
</Form.Item>
|
|
||||||
{
|
|
||||||
signupItem.rule !== "No verification" &&
|
|
||||||
<Form.Item
|
|
||||||
name="phoneCode"
|
|
||||||
label={signupItem.label ? signupItem.label : i18next.t("code:Phone code")}
|
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: required,
|
required: required,
|
||||||
message: i18next.t("code:Please input your phone verification code!"),
|
message: i18next.t("signup:Please input your Email!"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (this.state.email !== "" && !Setting.isValidEmail(this.state.email)) {
|
||||||
|
this.setState({validEmail: false});
|
||||||
|
return Promise.reject(i18next.t("signup:The input is not valid Email!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({validEmail: true});
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SendCodeInput
|
<Input placeholder={signupItem.placeholder} disabled={this.state.invitation !== undefined && this.state.invitation.email !== ""} onChange={e => this.setState({email: e.target.value})} />
|
||||||
disabled={!this.state.validPhone}
|
|
||||||
method={"signup"}
|
|
||||||
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
|
|
||||||
application={application}
|
|
||||||
countryCode={this.form.current?.getFieldValue("countryCode")}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
}
|
{
|
||||||
</React.Fragment>
|
signupItem.rule !== "No verification" &&
|
||||||
);
|
<Form.Item
|
||||||
|
name="emailCode"
|
||||||
|
label={signupItem.label ? signupItem.label : i18next.t("code:Email code")}
|
||||||
|
rules={[{
|
||||||
|
required: required,
|
||||||
|
message: i18next.t("code:Please input your verification code!"),
|
||||||
|
}]}
|
||||||
|
>
|
||||||
|
<SendCodeInput
|
||||||
|
disabled={!this.state.validEmail}
|
||||||
|
method={"signup"}
|
||||||
|
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]}
|
||||||
|
application={application}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderPhoneItem = () => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Form.Item label={signupItem.label ? signupItem.label : i18next.t("general:Phone")} required={required}>
|
||||||
|
<Input.Group compact>
|
||||||
|
<Form.Item
|
||||||
|
name="countryCode"
|
||||||
|
noStyle
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: required,
|
||||||
|
message: i18next.t("signup:Please select your country code!"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CountryCodeSelect
|
||||||
|
style={{width: "35%"}}
|
||||||
|
countryCodes={this.getApplicationObj().organizationObj.countryCodes}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="phone"
|
||||||
|
dependencies={["countryCode"]}
|
||||||
|
noStyle
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: required,
|
||||||
|
message: i18next.t("signup:Please input your phone number!"),
|
||||||
|
},
|
||||||
|
({getFieldValue}) => ({
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (!required && !value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value && !Setting.isValidPhone(value, getFieldValue("countryCode"))) {
|
||||||
|
this.setState({validPhone: false});
|
||||||
|
return Promise.reject(i18next.t("signup:The input is not valid Phone!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({validPhone: true});
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder={signupItem.placeholder}
|
||||||
|
style={{width: "65%"}}
|
||||||
|
disabled={this.state.invitation !== undefined && this.state.invitation.phone !== ""}
|
||||||
|
onChange={e => this.setState({phone: e.target.value})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Input.Group>
|
||||||
|
</Form.Item>
|
||||||
|
{
|
||||||
|
signupItem.rule !== "No verification" &&
|
||||||
|
<Form.Item
|
||||||
|
name="phoneCode"
|
||||||
|
label={signupItem.label ? signupItem.label : i18next.t("code:Phone code")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: required,
|
||||||
|
message: i18next.t("code:Please input your phone verification code!"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SendCodeInput
|
||||||
|
disabled={!this.state.validPhone}
|
||||||
|
method={"signup"}
|
||||||
|
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
|
||||||
|
application={application}
|
||||||
|
countryCode={this.form.current?.getFieldValue("countryCode")}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (signupItem.name === "Email") {
|
||||||
|
return renderEmailItem();
|
||||||
|
} else if (signupItem.name === "Phone") {
|
||||||
|
return renderPhoneItem();
|
||||||
|
} else if (signupItem.name === "Email or Phone" || signupItem.name === "Phone or Email") {
|
||||||
|
let emailOrPhoneMode = this.state.emailOrPhoneMode;
|
||||||
|
if (emailOrPhoneMode === "") {
|
||||||
|
emailOrPhoneMode = signupItem.name === "Email or Phone" ? "Email" : "Phone";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Row style={{marginTop: "30px", marginBottom: "20px"}} >
|
||||||
|
<Radio.Group style={{width: "400px"}} buttonStyle="solid" onChange={e => {
|
||||||
|
this.setState({
|
||||||
|
emailOrPhoneMode: e.target.value,
|
||||||
|
});
|
||||||
|
}} value={emailOrPhoneMode}>
|
||||||
|
{
|
||||||
|
signupItem.name === "Email or Phone" ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<Radio.Button value={"Email"}>{i18next.t("general:Email")}</Radio.Button>
|
||||||
|
<Radio.Button value={"Phone"}>{i18next.t("general:Phone")}</Radio.Button>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
|
<Radio.Button value={"Phone"}>{i18next.t("general:Phone")}</Radio.Button>
|
||||||
|
<Radio.Button value={"Email"}>{i18next.t("general:Email")}</Radio.Button>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Radio.Group>
|
||||||
|
</Row>
|
||||||
|
{
|
||||||
|
emailOrPhoneMode === "Email" ? renderEmailItem() : renderPhoneItem()
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} else if (signupItem.name === "Password") {
|
} else if (signupItem.name === "Password") {
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Kopiere den Link",
|
"Copy Link": "Kopiere den Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copiar enlace",
|
"Copy Link": "Copiar enlace",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copier le lien",
|
"Copy Link": "Copier le lien",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Salin Tautan",
|
"Copy Link": "Salin Tautan",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "コピー リンク",
|
"Copy Link": "コピー リンク",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "링크 복사하기",
|
"Copy Link": "링크 복사하기",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copiar Link",
|
"Copy Link": "Copiar Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Копировать ссылку",
|
"Copy Link": "Копировать ссылку",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Sao chép liên kết",
|
"Copy Link": "Sao chép liên kết",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "是否触发",
|
"Is triggered": "是否触发",
|
||||||
"Object": "实体"
|
"Object": "实体",
|
||||||
|
"Response": "响应"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "复制链接",
|
"Copy Link": "复制链接",
|
||||||
|
@ -81,10 +81,12 @@ class SignupTable extends React.Component {
|
|||||||
{name: "Affiliation", displayName: i18next.t("user:Affiliation")},
|
{name: "Affiliation", displayName: i18next.t("user:Affiliation")},
|
||||||
{name: "Country/Region", displayName: i18next.t("user:Country/Region")},
|
{name: "Country/Region", displayName: i18next.t("user:Country/Region")},
|
||||||
{name: "ID card", displayName: i18next.t("user:ID card")},
|
{name: "ID card", displayName: i18next.t("user:ID card")},
|
||||||
{name: "Email", displayName: i18next.t("general:Email")},
|
|
||||||
{name: "Password", displayName: i18next.t("general:Password")},
|
{name: "Password", displayName: i18next.t("general:Password")},
|
||||||
{name: "Confirm password", displayName: i18next.t("signup:Confirm")},
|
{name: "Confirm password", displayName: i18next.t("signup:Confirm")},
|
||||||
|
{name: "Email", displayName: i18next.t("general:Email")},
|
||||||
{name: "Phone", displayName: i18next.t("general:Phone")},
|
{name: "Phone", displayName: i18next.t("general:Phone")},
|
||||||
|
{name: "Email or Phone", displayName: i18next.t("general:Email or Phone")},
|
||||||
|
{name: "Phone or Email", displayName: i18next.t("general:Phone or Email")},
|
||||||
{name: "Invitation code", displayName: i18next.t("application:Invitation code")},
|
{name: "Invitation code", displayName: i18next.t("application:Invitation code")},
|
||||||
{name: "Agreement", displayName: i18next.t("signup:Agreement")},
|
{name: "Agreement", displayName: i18next.t("signup:Agreement")},
|
||||||
{name: "Text 1", displayName: i18next.t("signup:Text 1")},
|
{name: "Text 1", displayName: i18next.t("signup:Text 1")},
|
||||||
|
@ -183,19 +183,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@ctrl/tinycolor" "^3.4.0"
|
"@ctrl/tinycolor" "^3.4.0"
|
||||||
|
|
||||||
"@ant-design/cssinjs@1.16.1":
|
|
||||||
version "1.16.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.16.1.tgz#0032044db5678dd25ac12def1abb1d52e6a4d583"
|
|
||||||
integrity sha512-KKVB5Or6BDC1Bo3Y4KMlOkyQU0P+6GTodubrQ9YfrtXG1TgO4wpaEfg9I4ZA49R7M+Ij2KKNwb+5abvmXy6K8w==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.11.1"
|
|
||||||
"@emotion/hash" "^0.8.0"
|
|
||||||
"@emotion/unitless" "^0.7.5"
|
|
||||||
classnames "^2.3.1"
|
|
||||||
csstype "^3.0.10"
|
|
||||||
rc-util "^5.35.0"
|
|
||||||
stylis "^4.0.13"
|
|
||||||
|
|
||||||
"@ant-design/cssinjs@^1", "@ant-design/cssinjs@^1.10.1", "@ant-design/cssinjs@^1.5.6":
|
"@ant-design/cssinjs@^1", "@ant-design/cssinjs@^1.10.1", "@ant-design/cssinjs@^1.5.6":
|
||||||
version "1.10.1"
|
version "1.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.10.1.tgz#c9173f38e3d61f0883ca3c17d7cf1e30784e0dd7"
|
resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.10.1.tgz#c9173f38e3d61f0883ca3c17d7cf1e30784e0dd7"
|
||||||
@ -12408,14 +12395,6 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.
|
|||||||
"@babel/runtime" "^7.18.3"
|
"@babel/runtime" "^7.18.3"
|
||||||
react-is "^16.12.0"
|
react-is "^16.12.0"
|
||||||
|
|
||||||
rc-util@^5.35.0:
|
|
||||||
version "5.35.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.35.0.tgz#bed1986248b7be525cc0894109e609ac60207f29"
|
|
||||||
integrity sha512-MTXlixb3EoSTEchsOc7XWsVyoUQqoCsh2Z1a2IptwNgqleMF6ZgQeY52UzUbNj5CcVBg9YljOWjuOV07jSSm4Q==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.18.3"
|
|
||||||
react-is "^16.12.0"
|
|
||||||
|
|
||||||
rc-virtual-list@^3.4.13, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2:
|
rc-virtual-list@^3.4.13, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2:
|
||||||
version "3.5.2"
|
version "3.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.5.2.tgz#5e1028869bae900eacbae6788d4eca7210736006"
|
resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.5.2.tgz#5e1028869bae900eacbae6788d4eca7210736006"
|
||||||
|
Reference in New Issue
Block a user