mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-15 05:13:49 +08:00
Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
7df043fb15 | |||
cb542ae46a | |||
3699177837 | |||
3a6846b32c | |||
50586a9716 | |||
9201992140 | |||
eb39e9e044 | |||
5b27f939b8 | |||
69ee6a6f7e | |||
bf6d5e529b | |||
55fd31f575 | |||
05c063ac24 | |||
38da63e73c | |||
cb13d693e6 | |||
d699774179 | |||
84a7fdcd07 | |||
2cd6f9df8e | |||
eea2e1d271 | |||
48c5bd942c | |||
d01d63d82a | |||
e4fd9cca92 | |||
8d531b8880 | |||
b1589e11eb | |||
b32a772a77 | |||
7e4562efe1 | |||
3a6ab4cfc6 | |||
fba4801a41 | |||
da21c92815 | |||
66c15578b1 | |||
f272be67ab | |||
e4c36d407f | |||
4c1915b014 | |||
6c2b172aae | |||
95f4f4cb6d | |||
511aefb706 | |||
1003639e5b | |||
fe53e90d37 | |||
8c73cb5395 | |||
06ebc04032 | |||
0ee98e2582 | |||
d25508fa56 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -66,7 +66,7 @@ jobs:
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '^1.16.5'
|
||||
cache-dependency-path: ./go.mod
|
||||
cache: false
|
||||
|
||||
# gen a dummy config file
|
||||
- run: touch dummy.yml
|
||||
|
135
ai/ai.go
Normal file
135
ai/ai.go
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
func queryAnswer(authToken string, question string, timeout int) (string, error) {
|
||||
// fmt.Printf("Question: %s\n", question)
|
||||
|
||||
client := getProxyClientFromToken(authToken)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(2+timeout*2)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := client.CreateChatCompletion(
|
||||
ctx,
|
||||
openai.ChatCompletionRequest{
|
||||
Model: openai.GPT3Dot5Turbo,
|
||||
Messages: []openai.ChatCompletionMessage{
|
||||
{
|
||||
Role: openai.ChatMessageRoleUser,
|
||||
Content: question,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
res := resp.Choices[0].Message.Content
|
||||
res = strings.Trim(res, "\n")
|
||||
// fmt.Printf("Answer: %s\n\n", res)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func QueryAnswerSafe(authToken string, question string) string {
|
||||
var res string
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
res, err = queryAnswer(authToken, question, i)
|
||||
if err != nil {
|
||||
if i > 0 {
|
||||
fmt.Printf("\tFailed (%d): %s\n", i+1, err.Error())
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func QueryAnswerStream(authToken string, question string, writer io.Writer, builder *strings.Builder) error {
|
||||
client := getProxyClientFromToken(authToken)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// https://platform.openai.com/tokenizer
|
||||
// https://github.com/pkoukk/tiktoken-go#available-encodings
|
||||
promptTokens, err := getTokenSize(openai.GPT3TextDavinci003, question)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// https://platform.openai.com/docs/models/gpt-3-5
|
||||
maxTokens := 4097 - promptTokens
|
||||
|
||||
respStream, err := client.CreateCompletionStream(
|
||||
ctx,
|
||||
openai.CompletionRequest{
|
||||
Model: openai.GPT3TextDavinci003,
|
||||
Prompt: question,
|
||||
MaxTokens: maxTokens,
|
||||
Stream: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer respStream.Close()
|
||||
|
||||
isLeadingReturn := true
|
||||
for {
|
||||
completion, streamErr := respStream.Recv()
|
||||
if streamErr != nil {
|
||||
if streamErr == io.EOF {
|
||||
break
|
||||
}
|
||||
return streamErr
|
||||
}
|
||||
|
||||
data := completion.Choices[0].Text
|
||||
if isLeadingReturn && len(data) != 0 {
|
||||
if strings.Count(data, "\n") == len(data) {
|
||||
continue
|
||||
} else {
|
||||
isLeadingReturn = false
|
||||
}
|
||||
}
|
||||
|
||||
// Write the streamed data as Server-Sent Events
|
||||
if _, err = fmt.Fprintf(writer, "data: %s\n\n", data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Append the response to the strings.Builder
|
||||
builder.WriteString(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
42
ai/ai_test.go
Normal file
42
ai/ai_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package ai
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
object.InitConfig()
|
||||
proxy.InitHttpClient()
|
||||
|
||||
text, err := queryAnswer("", "hi", 5)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
println(text)
|
||||
}
|
||||
|
||||
func TestToken(t *testing.T) {
|
||||
println(getTokenSize(openai.GPT3TextDavinci003, ""))
|
||||
}
|
28
ai/proxy.go
Normal file
28
ai/proxy.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ai
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
func getProxyClientFromToken(authToken string) *openai.Client {
|
||||
config := openai.DefaultConfig(authToken)
|
||||
config.HTTPClient = proxy.ProxyHttpClient
|
||||
|
||||
c := openai.NewClientWithConfig(config)
|
||||
return c
|
||||
}
|
28
ai/util.go
Normal file
28
ai/util.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ai
|
||||
|
||||
import "github.com/pkoukk/tiktoken-go"
|
||||
|
||||
func getTokenSize(model string, prompt string) (int, error) {
|
||||
tkm, err := tiktoken.EncodingForModel(model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
token := tkm.Encode(prompt, nil, nil)
|
||||
res := len(token)
|
||||
return res, nil
|
||||
}
|
@ -121,6 +121,8 @@ p, *, *, *, /cas, *, *
|
||||
p, *, *, *, /api/webauthn, *, *
|
||||
p, *, *, GET, /api/get-release, *, *
|
||||
p, *, *, GET, /api/get-default-application, *, *
|
||||
p, *, *, GET, /api/get-prometheus-info, *, *
|
||||
p, *, *, *, /api/metrics, *, *
|
||||
`
|
||||
|
||||
sa := stringadapter.NewAdapter(ruleText)
|
||||
@ -149,7 +151,7 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
|
||||
|
||||
userId := fmt.Sprintf("%s/%s", subOwner, subName)
|
||||
user := object.GetUser(userId)
|
||||
if user != nil && user.IsAdmin && (subOwner == objOwner || (objOwner == "admin" && subOwner == objName)) {
|
||||
if user != nil && user.IsAdmin && (subOwner == objOwner || (objOwner == "admin")) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/form"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
@ -34,44 +35,6 @@ const (
|
||||
ResponseTypeCas = "cas"
|
||||
)
|
||||
|
||||
type RequestForm struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
Organization string `json:"organization"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Name string `json:"name"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Affiliation string `json:"affiliation"`
|
||||
IdCard string `json:"idCard"`
|
||||
Region string `json:"region"`
|
||||
|
||||
Application string `json:"application"`
|
||||
ClientId string `json:"clientId"`
|
||||
Provider string `json:"provider"`
|
||||
Code string `json:"code"`
|
||||
State string `json:"state"`
|
||||
RedirectUri string `json:"redirectUri"`
|
||||
Method string `json:"method"`
|
||||
|
||||
EmailCode string `json:"emailCode"`
|
||||
PhoneCode string `json:"phoneCode"`
|
||||
CountryCode string `json:"countryCode"`
|
||||
|
||||
AutoSignin bool `json:"autoSignin"`
|
||||
|
||||
RelayState string `json:"relayState"`
|
||||
SamlRequest string `json:"samlRequest"`
|
||||
SamlResponse string `json:"samlResponse"`
|
||||
|
||||
CaptchaType string `json:"captchaType"`
|
||||
CaptchaToken string `json:"captchaToken"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
@ -108,28 +71,28 @@ func (c *ApiController) Signup() {
|
||||
return
|
||||
}
|
||||
|
||||
var form RequestForm
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
|
||||
var authForm form.AuthForm
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if !application.EnableSignUp {
|
||||
c.ResponseError(c.T("account:The application does not allow to sign up new account"))
|
||||
return
|
||||
}
|
||||
|
||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization))
|
||||
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.CountryCode, form.Affiliation, c.GetAcceptLanguage())
|
||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", authForm.Organization))
|
||||
msg := object.CheckUserSignup(application, organization, &authForm, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && form.Email != "" {
|
||||
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode, c.GetAcceptLanguage())
|
||||
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && authForm.Email != "" {
|
||||
checkResult := object.CheckVerificationCode(authForm.Email, authForm.EmailCode, c.GetAcceptLanguage())
|
||||
if checkResult.Code != object.VerificationSuccess {
|
||||
c.ResponseError(checkResult.Msg)
|
||||
return
|
||||
@ -137,9 +100,9 @@ func (c *ApiController) Signup() {
|
||||
}
|
||||
|
||||
var checkPhone string
|
||||
if application.IsSignupItemVisible("Phone") && application.GetSignupItemRule("Phone") != "No verification" && form.Phone != "" {
|
||||
checkPhone, _ = util.GetE164Number(form.Phone, form.CountryCode)
|
||||
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
|
||||
if application.IsSignupItemVisible("Phone") && application.GetSignupItemRule("Phone") != "No verification" && authForm.Phone != "" {
|
||||
checkPhone, _ = util.GetE164Number(authForm.Phone, authForm.CountryCode)
|
||||
checkResult := object.CheckVerificationCode(checkPhone, authForm.PhoneCode, c.GetAcceptLanguage())
|
||||
if checkResult.Code != object.VerificationSuccess {
|
||||
c.ResponseError(checkResult.Msg)
|
||||
return
|
||||
@ -148,7 +111,7 @@ func (c *ApiController) Signup() {
|
||||
|
||||
id := util.GenerateId()
|
||||
if application.GetSignupItemRule("ID") == "Incremental" {
|
||||
lastUser := object.GetLastUser(form.Organization)
|
||||
lastUser := object.GetLastUser(authForm.Organization)
|
||||
|
||||
lastIdInt := -1
|
||||
if lastUser != nil {
|
||||
@ -158,7 +121,7 @@ func (c *ApiController) Signup() {
|
||||
id = strconv.Itoa(lastIdInt + 1)
|
||||
}
|
||||
|
||||
username := form.Username
|
||||
username := authForm.Username
|
||||
if !application.IsSignupItemVisible("Username") {
|
||||
username = id
|
||||
}
|
||||
@ -170,21 +133,21 @@ func (c *ApiController) Signup() {
|
||||
}
|
||||
|
||||
user := &object.User{
|
||||
Owner: form.Organization,
|
||||
Owner: authForm.Organization,
|
||||
Name: username,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Id: id,
|
||||
Type: "normal-user",
|
||||
Password: form.Password,
|
||||
DisplayName: form.Name,
|
||||
Password: authForm.Password,
|
||||
DisplayName: authForm.Name,
|
||||
Avatar: organization.DefaultAvatar,
|
||||
Email: form.Email,
|
||||
Phone: form.Phone,
|
||||
CountryCode: form.CountryCode,
|
||||
Email: authForm.Email,
|
||||
Phone: authForm.Phone,
|
||||
CountryCode: authForm.CountryCode,
|
||||
Address: []string{},
|
||||
Affiliation: form.Affiliation,
|
||||
IdCard: form.IdCard,
|
||||
Region: form.Region,
|
||||
Affiliation: authForm.Affiliation,
|
||||
IdCard: authForm.IdCard,
|
||||
Region: authForm.Region,
|
||||
Score: initScore,
|
||||
IsAdmin: false,
|
||||
IsGlobalAdmin: false,
|
||||
@ -203,10 +166,10 @@ func (c *ApiController) Signup() {
|
||||
}
|
||||
|
||||
if application.GetSignupItemRule("Display name") == "First, last" {
|
||||
if form.FirstName != "" || form.LastName != "" {
|
||||
user.DisplayName = fmt.Sprintf("%s %s", form.FirstName, form.LastName)
|
||||
user.FirstName = form.FirstName
|
||||
user.LastName = form.LastName
|
||||
if authForm.FirstName != "" || authForm.LastName != "" {
|
||||
user.DisplayName = fmt.Sprintf("%s %s", authForm.FirstName, authForm.LastName)
|
||||
user.FirstName = authForm.FirstName
|
||||
user.LastName = authForm.LastName
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,7 +186,7 @@ func (c *ApiController) Signup() {
|
||||
c.SetSessionUsername(user.GetId())
|
||||
}
|
||||
|
||||
object.DisableVerificationCode(form.Email)
|
||||
object.DisableVerificationCode(authForm.Email)
|
||||
object.DisableVerificationCode(checkPhone)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
|
@ -24,10 +24,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/captcha"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/form"
|
||||
"github.com/casdoor/casdoor/idp"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
@ -56,7 +56,7 @@ func tokenToResponse(token *object.Token) *Response {
|
||||
}
|
||||
|
||||
// HandleLoggedIn ...
|
||||
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *RequestForm) (resp *Response) {
|
||||
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) {
|
||||
userId := user.GetId()
|
||||
|
||||
allowed, err := object.CheckAccessPermission(userId, application)
|
||||
@ -69,6 +69,12 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
return
|
||||
}
|
||||
|
||||
if form.Password != "" && user.IsMfaEnabled() {
|
||||
c.setMfaSessionData(&object.MfaSessionData{UserId: userId})
|
||||
resp = &Response{Status: object.NextMfa, Data: user.GetPreferMfa(true)}
|
||||
return
|
||||
}
|
||||
|
||||
if form.Type == ResponseTypeLogin {
|
||||
c.SetSessionUsername(userId)
|
||||
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
|
||||
@ -132,14 +138,10 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
|
||||
// if user did not check auto signin
|
||||
if resp.Status == "ok" && !form.AutoSignin {
|
||||
timestamp := time.Now().Unix()
|
||||
timestamp += 3600 * 24
|
||||
c.SetSessionData(&SessionData{
|
||||
ExpireTime: timestamp,
|
||||
})
|
||||
c.setExpireForSession()
|
||||
}
|
||||
|
||||
if resp.Status == "ok" && user.Owner == object.CasdoorOrganization && application.Name == object.CasdoorApplication {
|
||||
if resp.Status == "ok" {
|
||||
object.AddSession(&object.Session{
|
||||
Owner: user.Owner,
|
||||
Name: user.Name,
|
||||
@ -221,21 +223,21 @@ func isProxyProviderType(providerType string) bool {
|
||||
// @Param nonce query string false nonce
|
||||
// @Param code_challenge_method query string false code_challenge_method
|
||||
// @Param code_challenge query string false code_challenge
|
||||
// @Param form body controllers.RequestForm true "Login information"
|
||||
// @Param form body controllers.AuthForm true "Login information"
|
||||
// @Success 200 {object} Response The Response object
|
||||
// @router /login [post]
|
||||
func (c *ApiController) Login() {
|
||||
resp := &Response{}
|
||||
|
||||
var form RequestForm
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
|
||||
var authForm form.AuthForm
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if form.Username != "" {
|
||||
if form.Type == ResponseTypeLogin {
|
||||
if authForm.Username != "" {
|
||||
if authForm.Type == ResponseTypeLogin {
|
||||
if c.GetSessionUsername() != "" {
|
||||
c.ResponseError(c.T("account:Please sign out first"), c.GetSessionUsername())
|
||||
return
|
||||
@ -245,25 +247,25 @@ func (c *ApiController) Login() {
|
||||
var user *object.User
|
||||
var msg string
|
||||
|
||||
if form.Password == "" {
|
||||
if user = object.GetUserByFields(form.Organization, form.Username); user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
|
||||
if authForm.Password == "" {
|
||||
if user = object.GetUserByFields(authForm.Organization, authForm.Username); user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(authForm.Organization, authForm.Username)))
|
||||
return
|
||||
}
|
||||
|
||||
verificationCodeType := object.GetVerifyType(form.Username)
|
||||
verificationCodeType := object.GetVerifyType(authForm.Username)
|
||||
var checkDest string
|
||||
if verificationCodeType == object.VerifyTypePhone {
|
||||
form.CountryCode = user.GetCountryCode(form.CountryCode)
|
||||
authForm.CountryCode = user.GetCountryCode(authForm.CountryCode)
|
||||
var ok bool
|
||||
if checkDest, ok = util.GetE164Number(form.Username, form.CountryCode); !ok {
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), form.CountryCode))
|
||||
if checkDest, ok = util.GetE164Number(authForm.Username, authForm.CountryCode); !ok {
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), authForm.CountryCode))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check result through Email or Phone
|
||||
checkResult := object.CheckSigninCode(user, checkDest, form.Code, c.GetAcceptLanguage())
|
||||
checkResult := object.CheckSigninCode(user, checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||
if len(checkResult) != 0 {
|
||||
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, checkResult))
|
||||
return
|
||||
@ -272,9 +274,9 @@ func (c *ApiController) Login() {
|
||||
// disable the verification code
|
||||
object.DisableVerificationCode(checkDest)
|
||||
} else {
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
if !application.EnablePassword {
|
||||
@ -282,8 +284,8 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
var enableCaptcha bool
|
||||
if enableCaptcha = object.CheckToEnableCaptcha(application, form.Organization, form.Username); enableCaptcha {
|
||||
isHuman, err := captcha.VerifyCaptchaByCaptchaType(form.CaptchaType, form.CaptchaToken, form.ClientSecret)
|
||||
if enableCaptcha = object.CheckToEnableCaptcha(application, authForm.Organization, authForm.Username); enableCaptcha {
|
||||
isHuman, err := captcha.VerifyCaptchaByCaptchaType(authForm.CaptchaType, authForm.CaptchaToken, authForm.ClientSecret)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -295,42 +297,41 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
}
|
||||
|
||||
password := form.Password
|
||||
user, msg = object.CheckUserPassword(form.Organization, form.Username, password, c.GetAcceptLanguage(), enableCaptcha)
|
||||
|
||||
password := authForm.Password
|
||||
user, msg = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha)
|
||||
}
|
||||
|
||||
if msg != "" {
|
||||
resp = &Response{Status: "error", Msg: msg}
|
||||
} else {
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
|
||||
resp = c.HandleLoggedIn(application, user, &form)
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
record.Organization = application.Organization
|
||||
record.User = user.Name
|
||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
||||
}
|
||||
} else if form.Provider != "" {
|
||||
} else if authForm.Provider != "" {
|
||||
var application *object.Application
|
||||
if form.ClientId != "" {
|
||||
application = object.GetApplicationByClientId(form.ClientId)
|
||||
if authForm.ClientId != "" {
|
||||
application = object.GetApplicationByClientId(authForm.ClientId)
|
||||
} else {
|
||||
application = object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||
application = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
|
||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", application.Organization))
|
||||
provider := object.GetProvider(util.GetId("admin", form.Provider))
|
||||
provider := object.GetProvider(util.GetId("admin", authForm.Provider))
|
||||
providerItem := application.GetProviderItem(provider.Name)
|
||||
if !providerItem.IsProviderVisible() {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The provider: %s is not enabled for the application"), provider.Name))
|
||||
@ -340,7 +341,7 @@ func (c *ApiController) Login() {
|
||||
userInfo := &idp.UserInfo{}
|
||||
if provider.Category == "SAML" {
|
||||
// SAML
|
||||
userInfo.Id, err = object.ParseSamlResponse(form.SamlResponse, provider, c.Ctx.Request.Host)
|
||||
userInfo.Id, err = object.ParseSamlResponse(authForm.SamlResponse, provider, c.Ctx.Request.Host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -355,7 +356,7 @@ func (c *ApiController) Login() {
|
||||
clientSecret = provider.ClientSecret2
|
||||
}
|
||||
|
||||
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, form.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl)
|
||||
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, authForm.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl)
|
||||
if idProvider == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("storage:The provider type: %s is not supported"), provider.Type))
|
||||
return
|
||||
@ -363,13 +364,13 @@ func (c *ApiController) Login() {
|
||||
|
||||
setHttpClient(idProvider, provider.Type)
|
||||
|
||||
if form.State != conf.GetConfigString("authState") && form.State != application.Name {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:State expected: %s, but got: %s"), conf.GetConfigString("authState"), form.State))
|
||||
if authForm.State != conf.GetConfigString("authState") && authForm.State != application.Name {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:State expected: %s, but got: %s"), conf.GetConfigString("authState"), authForm.State))
|
||||
return
|
||||
}
|
||||
|
||||
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
||||
token, err := idProvider.GetToken(form.Code)
|
||||
token, err := idProvider.GetToken(authForm.Code)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -387,7 +388,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
}
|
||||
|
||||
if form.Method == "signup" {
|
||||
if authForm.Method == "signup" {
|
||||
user := &object.User{}
|
||||
if provider.Category == "SAML" {
|
||||
user = object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Id))
|
||||
@ -402,7 +403,7 @@ func (c *ApiController) Login() {
|
||||
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
||||
}
|
||||
|
||||
resp = c.HandleLoggedIn(application, user, &form)
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
record.Organization = application.Organization
|
||||
@ -457,6 +458,9 @@ func (c *ApiController) Login() {
|
||||
Avatar: userInfo.AvatarUrl,
|
||||
Address: []string{},
|
||||
Email: userInfo.Email,
|
||||
Phone: userInfo.Phone,
|
||||
CountryCode: userInfo.CountryCode,
|
||||
Region: userInfo.CountryCode,
|
||||
Score: initScore,
|
||||
IsAdmin: false,
|
||||
IsGlobalAdmin: false,
|
||||
@ -477,7 +481,7 @@ func (c *ApiController) Login() {
|
||||
object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||
object.LinkUserAccount(user, provider.Type, userInfo.Id)
|
||||
|
||||
resp = c.HandleLoggedIn(application, user, &form)
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
record.Organization = application.Organization
|
||||
@ -493,7 +497,7 @@ func (c *ApiController) Login() {
|
||||
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: "ok", Msg: "", Data: res}
|
||||
} else { // form.Method != "signup"
|
||||
} else { // authForm.Method != "signup"
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id)), userInfo)
|
||||
@ -518,24 +522,56 @@ func (c *ApiController) Login() {
|
||||
resp = &Response{Status: "error", Msg: "Failed to link user account", Data: isLinked}
|
||||
}
|
||||
}
|
||||
} else if c.getMfaSessionData() != nil {
|
||||
mfaSession := c.getMfaSessionData()
|
||||
user := object.GetUser(mfaSession.UserId)
|
||||
|
||||
if authForm.Passcode != "" {
|
||||
MfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferMfa(false))
|
||||
err = MfaUtil.Verify(authForm.Passcode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if authForm.RecoveryCode != "" {
|
||||
err = object.RecoverTfs(user, authForm.RecoveryCode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
record.Organization = application.Organization
|
||||
record.User = user.Name
|
||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
||||
} else {
|
||||
if c.GetSessionUsername() != "" {
|
||||
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||
application := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
|
||||
user := c.getCurrentUser()
|
||||
resp = c.HandleLoggedIn(application, user, &form)
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
record.Organization = application.Organization
|
||||
record.User = user.Name
|
||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
||||
} else {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(form)))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(authForm)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -41,18 +41,44 @@ type SessionData struct {
|
||||
}
|
||||
|
||||
func (c *ApiController) IsGlobalAdmin() bool {
|
||||
username := c.GetSessionUsername()
|
||||
if strings.HasPrefix(username, "app/") {
|
||||
// e.g., "app/app-casnode"
|
||||
return true
|
||||
}
|
||||
isGlobalAdmin, _ := c.isGlobalAdmin()
|
||||
|
||||
user := object.GetUser(username)
|
||||
return isGlobalAdmin
|
||||
}
|
||||
|
||||
func (c *ApiController) IsAdmin() bool {
|
||||
isGlobalAdmin, user := c.isGlobalAdmin()
|
||||
if user == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return user.Owner == "built-in" || user.IsGlobalAdmin
|
||||
return isGlobalAdmin || user.IsAdmin
|
||||
}
|
||||
|
||||
func (c *ApiController) isGlobalAdmin() (bool, *object.User) {
|
||||
username := c.GetSessionUsername()
|
||||
if strings.HasPrefix(username, "app/") {
|
||||
// e.g., "app/app-casnode"
|
||||
return true, nil
|
||||
}
|
||||
|
||||
user := c.getCurrentUser()
|
||||
if user == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return user.Owner == "built-in" || user.IsGlobalAdmin, user
|
||||
}
|
||||
|
||||
func (c *ApiController) getCurrentUser() *object.User {
|
||||
var user *object.User
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
user = nil
|
||||
} else {
|
||||
user = object.GetUser(userId)
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
// GetSessionUsername ...
|
||||
@ -142,6 +168,30 @@ func (c *ApiController) SetSessionData(s *SessionData) {
|
||||
c.SetSession("SessionData", util.StructToJson(s))
|
||||
}
|
||||
|
||||
func (c *ApiController) setMfaSessionData(data *object.MfaSessionData) {
|
||||
c.SetSession(object.MfaSessionUserId, data.UserId)
|
||||
}
|
||||
|
||||
func (c *ApiController) getMfaSessionData() *object.MfaSessionData {
|
||||
userId := c.GetSession(object.MfaSessionUserId)
|
||||
if userId == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data := &object.MfaSessionData{
|
||||
UserId: userId.(string),
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (c *ApiController) setExpireForSession() {
|
||||
timestamp := time.Now().Unix()
|
||||
timestamp += 3600 * 24
|
||||
c.SetSessionData(&SessionData{
|
||||
ExpireTime: timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func wrapActionResponse(affected bool) *Response {
|
||||
if affected {
|
||||
return &Response{Status: "ok", Msg: "", Data: "Affected"}
|
||||
@ -157,3 +207,14 @@ func wrapErrorResponse(err error) *Response {
|
||||
return &Response{Status: "error", Msg: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ApiController) Finish() {
|
||||
if strings.HasPrefix(c.Ctx.Input.URL(), "/api") {
|
||||
startTime := c.Ctx.Input.GetData("startTime")
|
||||
if startTime != nil {
|
||||
latency := time.Since(startTime.(time.Time)).Milliseconds()
|
||||
object.ApiLatency.WithLabelValues(c.Ctx.Input.URL(), c.Ctx.Input.Method()).Observe(float64(latency))
|
||||
}
|
||||
}
|
||||
c.Controller.Finish()
|
||||
}
|
||||
|
@ -31,13 +31,14 @@ func (c *ApiController) GetCasbinAdapters() {
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
organization := c.Input().Get("organization")
|
||||
if limit == "" || page == "" {
|
||||
adapters := object.GetCasbinAdapters(owner)
|
||||
adapters := object.GetCasbinAdapters(owner, organization)
|
||||
c.ResponseOk(adapters)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetCasbinAdapterCount(owner, field, value)))
|
||||
adapters := object.GetPaginationCasbinAdapters(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetCasbinAdapterCount(owner, organization, field, value)))
|
||||
adapters := object.GetPaginationCasbinAdapters(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(adapters, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,30 @@ func (c *ApiController) GetCerts() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetGlobleCerts
|
||||
// @Title GetGlobleCerts
|
||||
// @Tag Cert API
|
||||
// @Description get globle certs
|
||||
// @Success 200 {array} object.Cert The Response object
|
||||
// @router /get-globle-certs [get]
|
||||
func (c *ApiController) GetGlobleCerts() {
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetMaskedCerts(object.GetGlobleCerts())
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetGlobalCertsCount(field, value)))
|
||||
certs := object.GetMaskedCerts(object.GetPaginationGlobalCerts(paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
c.ResponseOk(certs, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetCert
|
||||
// @Title GetCert
|
||||
// @Tag Cert API
|
||||
|
@ -16,8 +16,11 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/ai"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
@ -38,7 +41,7 @@ func (c *ApiController) GetMessages() {
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
chat := c.Input().Get("chat")
|
||||
|
||||
organization := c.Input().Get("organization")
|
||||
if limit == "" || page == "" {
|
||||
var messages []*object.Message
|
||||
if chat == "" {
|
||||
@ -51,8 +54,8 @@ func (c *ApiController) GetMessages() {
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetMessageCount(owner, field, value)))
|
||||
messages := object.GetMaskedMessages(object.GetPaginationMessages(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetMessageCount(owner, organization, field, value)))
|
||||
messages := object.GetMaskedMessages(object.GetPaginationMessages(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
c.ResponseOk(messages, paginator.Nums())
|
||||
}
|
||||
}
|
||||
@ -71,6 +74,97 @@ func (c *ApiController) GetMessage() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) ResponseErrorStream(errorText string) {
|
||||
event := fmt.Sprintf("event: myerror\ndata: %s\n\n", errorText)
|
||||
_, err := c.Ctx.ResponseWriter.Write([]byte(event))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetMessageAnswer
|
||||
// @Title GetMessageAnswer
|
||||
// @Tag Message API
|
||||
// @Description get message answer
|
||||
// @Param id query string true "The id ( owner/name ) of the message"
|
||||
// @Success 200 {object} object.Message The Response object
|
||||
// @router /get-message-answer [get]
|
||||
func (c *ApiController) GetMessageAnswer() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
|
||||
c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")
|
||||
|
||||
message := object.GetMessage(id)
|
||||
if message == nil {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The message: %s is not found"), id))
|
||||
return
|
||||
}
|
||||
|
||||
if message.Author != "AI" || message.ReplyTo == "" || message.Text != "" {
|
||||
c.ResponseErrorStream(c.T("chat:The message is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
chatId := util.GetId(message.Owner, message.Chat)
|
||||
chat := object.GetChat(chatId)
|
||||
if chat == nil {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The chat: %s is not found"), chatId))
|
||||
return
|
||||
}
|
||||
|
||||
if chat.Type != "AI" {
|
||||
c.ResponseErrorStream(c.T("chat:The chat type must be \"AI\""))
|
||||
return
|
||||
}
|
||||
|
||||
questionMessage := object.GetMessage(message.ReplyTo)
|
||||
if questionMessage == nil {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The message: %s is not found"), id))
|
||||
return
|
||||
}
|
||||
|
||||
providerId := util.GetId(chat.Owner, chat.User2)
|
||||
provider := object.GetProvider(providerId)
|
||||
if provider == nil {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The provider: %s is not found"), providerId))
|
||||
return
|
||||
}
|
||||
|
||||
if provider.Category != "AI" || provider.ClientSecret == "" {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The provider: %s is invalid"), providerId))
|
||||
return
|
||||
}
|
||||
|
||||
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
|
||||
c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")
|
||||
|
||||
authToken := provider.ClientSecret
|
||||
question := questionMessage.Text
|
||||
var stringBuilder strings.Builder
|
||||
err := ai.QueryAnswerStream(authToken, question, c.Ctx.ResponseWriter, &stringBuilder)
|
||||
if err != nil {
|
||||
c.ResponseErrorStream(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
event := fmt.Sprintf("event: end\ndata: %s\n\n", "end")
|
||||
_, err = c.Ctx.ResponseWriter.Write([]byte(event))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
answer := stringBuilder.String()
|
||||
|
||||
fmt.Printf("Question: [%s]\n", questionMessage.Text)
|
||||
fmt.Printf("Answer: [%s]\n", answer)
|
||||
|
||||
message.Text = answer
|
||||
object.UpdateMessage(message.GetId(), message)
|
||||
}
|
||||
|
||||
// UpdateMessage
|
||||
// @Title UpdateMessage
|
||||
// @Tag Message API
|
||||
@ -108,7 +202,26 @@ func (c *ApiController) AddMessage() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddMessage(&message))
|
||||
affected := object.AddMessage(&message)
|
||||
if affected {
|
||||
chatId := util.GetId(message.Owner, message.Chat)
|
||||
chat := object.GetChat(chatId)
|
||||
if chat != nil && chat.Type == "AI" {
|
||||
answerMessage := &object.Message{
|
||||
Owner: message.Owner,
|
||||
Name: fmt.Sprintf("message_%s", util.GetRandomName()),
|
||||
CreatedTime: util.GetCurrentTimeEx(message.CreatedTime),
|
||||
Organization: message.Organization,
|
||||
Chat: message.Chat,
|
||||
ReplyTo: message.GetId(),
|
||||
Author: "AI",
|
||||
Text: "",
|
||||
}
|
||||
object.AddMessage(answerMessage)
|
||||
}
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(affected)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
194
controllers/mfa.go
Normal file
194
controllers/mfa.go
Normal file
@ -0,0 +1,194 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/beego/beego"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// MfaSetupInitiate
|
||||
// @Title MfaSetupInitiate
|
||||
// @Tag MFA API
|
||||
// @Description setup MFA
|
||||
// @param owner form string true "owner of user"
|
||||
// @param name form string true "name of user"
|
||||
// @param type form string true "MFA auth type"
|
||||
// @Success 200 {object} The Response object
|
||||
// @router /mfa/setup/initiate [post]
|
||||
func (c *ApiController) MfaSetupInitiate() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
userId := util.GetId(owner, name)
|
||||
|
||||
if len(userId) == 0 {
|
||||
c.ResponseError(http.StatusText(http.StatusBadRequest))
|
||||
return
|
||||
}
|
||||
|
||||
MfaUtil := object.GetMfaUtil(authType, nil)
|
||||
if MfaUtil == nil {
|
||||
c.ResponseError("Invalid auth type")
|
||||
}
|
||||
user := object.GetUser(userId)
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
issuer := beego.AppConfig.String("appname")
|
||||
accountName := user.GetId()
|
||||
|
||||
mfaProps, err := MfaUtil.Initiate(c.Ctx, issuer, accountName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp := mfaProps
|
||||
c.ResponseOk(resp)
|
||||
}
|
||||
|
||||
// MfaSetupVerify
|
||||
// @Title MfaSetupVerify
|
||||
// @Tag MFA API
|
||||
// @Description setup verify totp
|
||||
// @param secret form string true "MFA secret"
|
||||
// @param passcode form string true "MFA passcode"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /mfa/setup/verify [post]
|
||||
func (c *ApiController) MfaSetupVerify() {
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
passcode := c.Ctx.Request.Form.Get("passcode")
|
||||
|
||||
if authType == "" || passcode == "" {
|
||||
c.ResponseError("missing auth type or passcode")
|
||||
return
|
||||
}
|
||||
MfaUtil := object.GetMfaUtil(authType, nil)
|
||||
|
||||
err := MfaUtil.SetupVerify(c.Ctx, passcode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
} else {
|
||||
c.ResponseOk(http.StatusText(http.StatusOK))
|
||||
}
|
||||
}
|
||||
|
||||
// MfaSetupEnable
|
||||
// @Title MfaSetupEnable
|
||||
// @Tag MFA API
|
||||
// @Description enable totp
|
||||
// @param owner form string true "owner of user"
|
||||
// @param name form string true "name of user"
|
||||
// @param type form string true "MFA auth type"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /mfa/setup/enable [post]
|
||||
func (c *ApiController) MfaSetupEnable() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
|
||||
user := object.GetUser(util.GetId(owner, name))
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
twoFactor := object.GetMfaUtil(authType, nil)
|
||||
err := twoFactor.Enable(c.Ctx, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(http.StatusText(http.StatusOK))
|
||||
}
|
||||
|
||||
// DeleteMfa
|
||||
// @Title DeleteMfa
|
||||
// @Tag MFA API
|
||||
// @Description: Delete MFA
|
||||
// @param owner form string true "owner of user"
|
||||
// @param name form string true "name of user"
|
||||
// @param id form string true "id of user's MFA props"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /delete-mfa/ [post]
|
||||
func (c *ApiController) DeleteMfa() {
|
||||
id := c.Ctx.Request.Form.Get("id")
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
userId := util.GetId(owner, name)
|
||||
|
||||
user := object.GetUser(userId)
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
mfaProps := user.MultiFactorAuths[:0]
|
||||
i := 0
|
||||
for _, mfaProp := range mfaProps {
|
||||
if mfaProp.Id != id {
|
||||
mfaProps[i] = mfaProp
|
||||
i++
|
||||
}
|
||||
}
|
||||
user.MultiFactorAuths = mfaProps
|
||||
object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
|
||||
c.ResponseOk(user.MultiFactorAuths)
|
||||
}
|
||||
|
||||
// SetPreferredMfa
|
||||
// @Title SetPreferredMfa
|
||||
// @Tag MFA API
|
||||
// @Description: Set specific Mfa Preferred
|
||||
// @param owner form string true "owner of user"
|
||||
// @param name form string true "name of user"
|
||||
// @param id form string true "id of user's MFA props"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /set-preferred-mfa [post]
|
||||
func (c *ApiController) SetPreferredMfa() {
|
||||
id := c.Ctx.Request.Form.Get("id")
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
userId := util.GetId(owner, name)
|
||||
|
||||
user := object.GetUser(userId)
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
mfaProps := user.MultiFactorAuths
|
||||
for i, mfaProp := range user.MultiFactorAuths {
|
||||
if mfaProp.Id == id {
|
||||
mfaProps[i].IsPreferred = true
|
||||
} else {
|
||||
mfaProps[i].IsPreferred = false
|
||||
}
|
||||
}
|
||||
|
||||
object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
|
||||
|
||||
for i, mfaProp := range mfaProps {
|
||||
mfaProps[i] = object.GetMaskedProps(mfaProp)
|
||||
}
|
||||
c.ResponseOk(mfaProps)
|
||||
}
|
39
controllers/prometheus.go
Normal file
39
controllers/prometheus.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// GetPrometheusInfo
|
||||
// @Title GetPrometheusInfo
|
||||
// @Tag Prometheus API
|
||||
// @Description get Prometheus Info
|
||||
// @Success 200 {object} object.PrometheusInfo The Response object
|
||||
// @router /get-prometheus-info [get]
|
||||
func (c *ApiController) GetPrometheusInfo() {
|
||||
_, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
prometheusInfo, err := object.GetPrometheusInfo()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(prometheusInfo)
|
||||
}
|
@ -37,13 +37,14 @@ func (c *ApiController) GetSyncers() {
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
organization := c.Input().Get("organization")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetSyncers(owner)
|
||||
c.Data["json"] = object.GetOrganizationSyncers(owner, organization)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetSyncerCount(owner, field, value)))
|
||||
syncers := object.GetPaginationSyncers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetSyncerCount(owner, organization, field, value)))
|
||||
syncers := object.GetPaginationSyncers(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(syncers, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
@ -39,13 +39,14 @@ func (c *ApiController) GetTokens() {
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
organization := c.Input().Get("organization")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetTokens(owner)
|
||||
c.Data["json"] = object.GetTokens(owner, organization)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetTokenCount(owner, field, value)))
|
||||
tokens := object.GetPaginationTokens(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetTokenCount(owner, organization, field, value)))
|
||||
tokens := object.GetPaginationTokens(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(tokens, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
@ -158,11 +158,18 @@ func (c *ApiController) UpdateUser() {
|
||||
return
|
||||
}
|
||||
|
||||
if oldUser.Owner == "built-in" && oldUser.Name == "admin" && (user.Owner != "built-in" || user.Name != "admin") {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
if pass, err := checkPermissionForUpdateUser(oldUser, &user, c); !pass {
|
||||
|
||||
isAdmin := c.IsAdmin()
|
||||
if pass, err := object.CheckPermissionForUpdateUser(oldUser, &user, isAdmin, c.GetAcceptLanguage()); !pass {
|
||||
c.ResponseError(err)
|
||||
return
|
||||
}
|
||||
@ -172,9 +179,7 @@ func (c *ApiController) UpdateUser() {
|
||||
columns = strings.Split(columnsStr, ",")
|
||||
}
|
||||
|
||||
isGlobalAdmin := c.IsGlobalAdmin()
|
||||
|
||||
affected := object.UpdateUser(id, &user, columns, isGlobalAdmin)
|
||||
affected := object.UpdateUser(id, &user, columns, isAdmin)
|
||||
if affected {
|
||||
object.UpdateUserToOriginalDatabase(&user)
|
||||
}
|
||||
@ -229,6 +234,11 @@ func (c *ApiController) DeleteUser() {
|
||||
return
|
||||
}
|
||||
|
||||
if user.Owner == "built-in" && user.Name == "admin" {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteUser(&user))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@ -286,6 +296,11 @@ func (c *ApiController) SetPassword() {
|
||||
newPassword := c.Ctx.Request.Form.Get("newPassword")
|
||||
code := c.Ctx.Request.Form.Get("code")
|
||||
|
||||
//if userOwner == "built-in" && userName == "admin" {
|
||||
// c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
// return
|
||||
//}
|
||||
|
||||
if strings.Contains(newPassword, " ") {
|
||||
c.ResponseError(c.T("user:New password cannot contain blank space."))
|
||||
return
|
||||
|
@ -1,138 +0,0 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
func checkPermissionForUpdateUser(oldUser, newUser *object.User, c *ApiController) (bool, string) {
|
||||
organization := object.GetOrganizationByUser(oldUser)
|
||||
var itemsChanged []*object.AccountItem
|
||||
|
||||
if oldUser.Owner != newUser.Owner {
|
||||
item := object.GetAccountItemByName("Organization", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Name != newUser.Name {
|
||||
item := object.GetAccountItemByName("Name", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Id != newUser.Id {
|
||||
item := object.GetAccountItemByName("ID", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.DisplayName != newUser.DisplayName {
|
||||
item := object.GetAccountItemByName("Display name", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Avatar != newUser.Avatar {
|
||||
item := object.GetAccountItemByName("Avatar", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Type != newUser.Type {
|
||||
item := object.GetAccountItemByName("User type", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
// The password is *** when not modified
|
||||
if oldUser.Password != newUser.Password && newUser.Password != "***" {
|
||||
item := object.GetAccountItemByName("Password", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Email != newUser.Email {
|
||||
item := object.GetAccountItemByName("Email", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Phone != newUser.Phone {
|
||||
item := object.GetAccountItemByName("Phone", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.CountryCode != newUser.CountryCode {
|
||||
item := object.GetAccountItemByName("Country code", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Region != newUser.Region {
|
||||
item := object.GetAccountItemByName("Country/Region", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Location != newUser.Location {
|
||||
item := object.GetAccountItemByName("Location", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Affiliation != newUser.Affiliation {
|
||||
item := object.GetAccountItemByName("Affiliation", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Title != newUser.Title {
|
||||
item := object.GetAccountItemByName("Title", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Homepage != newUser.Homepage {
|
||||
item := object.GetAccountItemByName("Homepage", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Bio != newUser.Bio {
|
||||
item := object.GetAccountItemByName("Bio", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Tag != newUser.Tag {
|
||||
item := object.GetAccountItemByName("Tag", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.SignupApplication != newUser.SignupApplication {
|
||||
item := object.GetAccountItemByName("Signup application", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
|
||||
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
|
||||
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
|
||||
item := object.GetAccountItemByName("Properties", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.IsAdmin != newUser.IsAdmin {
|
||||
item := object.GetAccountItemByName("Is admin", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.IsGlobalAdmin != newUser.IsGlobalAdmin {
|
||||
item := object.GetAccountItemByName("Is global admin", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.IsForbidden != newUser.IsForbidden {
|
||||
item := object.GetAccountItemByName("Is forbidden", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.IsDeleted != newUser.IsDeleted {
|
||||
item := object.GetAccountItemByName("Is deleted", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
currentUser := c.getCurrentUser()
|
||||
if currentUser == nil && c.IsGlobalAdmin() {
|
||||
currentUser = &object.User{
|
||||
IsGlobalAdmin: true,
|
||||
}
|
||||
}
|
||||
|
||||
for i := range itemsChanged {
|
||||
if pass, err := object.CheckAccountItemModifyRule(itemsChanged[i], currentUser, c.GetAcceptLanguage()); !pass {
|
||||
return pass, err
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
@ -128,7 +128,7 @@ func (c *ApiController) GetProviderFromContext(category string) (*object.Provide
|
||||
if providerName != "" {
|
||||
provider := object.GetProvider(util.GetId("admin", providerName))
|
||||
if provider == nil {
|
||||
c.ResponseError(c.T("util:The provider: %s is not found"), providerName)
|
||||
c.ResponseError(fmt.Sprintf(c.T("util:The provider: %s is not found"), providerName))
|
||||
return nil, nil, false
|
||||
}
|
||||
return provider, nil, true
|
||||
|
@ -21,71 +21,43 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/captcha"
|
||||
"github.com/casdoor/casdoor/form"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
const (
|
||||
SignupVerification = "signup"
|
||||
ResetVerification = "reset"
|
||||
LoginVerification = "login"
|
||||
ForgetVerification = "forget"
|
||||
SignupVerification = "signup"
|
||||
ResetVerification = "reset"
|
||||
LoginVerification = "login"
|
||||
ForgetVerification = "forget"
|
||||
MfaSetupVerification = "mfaSetup"
|
||||
MfaAuthVerification = "mfaAuth"
|
||||
)
|
||||
|
||||
func (c *ApiController) getCurrentUser() *object.User {
|
||||
var user *object.User
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
user = nil
|
||||
} else {
|
||||
user = object.GetUser(userId)
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
// SendVerificationCode ...
|
||||
// @Title SendVerificationCode
|
||||
// @Tag Verification API
|
||||
// @router /send-verification-code [post]
|
||||
func (c *ApiController) SendVerificationCode() {
|
||||
destType := c.Ctx.Request.Form.Get("type")
|
||||
dest := c.Ctx.Request.Form.Get("dest")
|
||||
countryCode := c.Ctx.Request.Form.Get("countryCode")
|
||||
checkType := c.Ctx.Request.Form.Get("checkType")
|
||||
clientSecret := c.Ctx.Request.Form.Get("clientSecret")
|
||||
captchaToken := c.Ctx.Request.Form.Get("captchaToken")
|
||||
applicationId := c.Ctx.Request.Form.Get("applicationId")
|
||||
method := c.Ctx.Request.Form.Get("method")
|
||||
checkUser := c.Ctx.Request.Form.Get("checkUser")
|
||||
var vform form.VerificationForm
|
||||
err := c.ParseForm(&vform)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
|
||||
|
||||
if dest == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter") + ": dest.")
|
||||
return
|
||||
}
|
||||
if applicationId == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter") + ": applicationId.")
|
||||
return
|
||||
}
|
||||
if checkType == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter") + ": checkType.")
|
||||
return
|
||||
}
|
||||
if !strings.Contains(applicationId, "/") {
|
||||
c.ResponseError(c.T("verification:Wrong parameter") + ": applicationId.")
|
||||
if msg := vform.CheckParameter(form.SendVerifyCode, c.GetAcceptLanguage()); msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
if checkType != "none" {
|
||||
if captchaToken == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter") + ": captchaToken.")
|
||||
if vform.CaptchaType != "none" {
|
||||
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||
return
|
||||
}
|
||||
|
||||
if captchaProvider := captcha.GetCaptchaProvider(checkType); captchaProvider == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + checkType)
|
||||
return
|
||||
} else if isHuman, err := captchaProvider.VerifyCaptcha(captchaToken, clientSecret); err != nil {
|
||||
} else if isHuman, err := captchaProvider.VerifyCaptcha(vform.CaptchaToken, vform.ClientSecret); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if !isHuman {
|
||||
@ -94,7 +66,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
}
|
||||
|
||||
application := object.GetApplication(applicationId)
|
||||
application := object.GetApplication(vform.ApplicationId)
|
||||
organization := object.GetOrganization(util.GetId(application.Owner, application.Organization))
|
||||
if organization == nil {
|
||||
c.ResponseError(c.T("check:Organization does not exist"))
|
||||
@ -103,63 +75,85 @@ func (c *ApiController) SendVerificationCode() {
|
||||
|
||||
var user *object.User
|
||||
// checkUser != "", means method is ForgetVerification
|
||||
if checkUser != "" {
|
||||
if vform.CheckUser != "" {
|
||||
owner := application.Organization
|
||||
user = object.GetUser(util.GetId(owner, checkUser))
|
||||
user = object.GetUser(util.GetId(owner, vform.CheckUser))
|
||||
}
|
||||
|
||||
// mfaSessionData != nil, means method is MfaSetupVerification
|
||||
if mfaSessionData := c.getMfaSessionData(); mfaSessionData != nil {
|
||||
user = object.GetUser(mfaSessionData.UserId)
|
||||
}
|
||||
|
||||
sendResp := errors.New("invalid dest type")
|
||||
|
||||
switch destType {
|
||||
switch vform.Type {
|
||||
case object.VerifyTypeEmail:
|
||||
if !util.IsEmailValid(dest) {
|
||||
if !util.IsEmailValid(vform.Dest) {
|
||||
c.ResponseError(c.T("check:Email is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
if method == LoginVerification || method == ForgetVerification {
|
||||
if user != nil && util.GetMaskedEmail(user.Email) == dest {
|
||||
dest = user.Email
|
||||
if vform.Method == LoginVerification || vform.Method == ForgetVerification {
|
||||
if user != nil && util.GetMaskedEmail(user.Email) == vform.Dest {
|
||||
vform.Dest = user.Email
|
||||
}
|
||||
|
||||
user = object.GetUserByEmail(organization.Name, dest)
|
||||
user = object.GetUserByEmail(organization.Name, vform.Dest)
|
||||
if user == nil {
|
||||
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
|
||||
return
|
||||
}
|
||||
} else if method == ResetVerification {
|
||||
} else if vform.Method == ResetVerification {
|
||||
user = c.getCurrentUser()
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferMfa(false)
|
||||
if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
}
|
||||
|
||||
provider := application.GetEmailProvider()
|
||||
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest)
|
||||
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, vform.Dest)
|
||||
case object.VerifyTypePhone:
|
||||
if method == LoginVerification || method == ForgetVerification {
|
||||
if user != nil && util.GetMaskedPhone(user.Phone) == dest {
|
||||
dest = user.Phone
|
||||
if vform.Method == LoginVerification || vform.Method == ForgetVerification {
|
||||
if user != nil && util.GetMaskedPhone(user.Phone) == vform.Dest {
|
||||
vform.Dest = user.Phone
|
||||
}
|
||||
|
||||
if user = object.GetUserByPhone(organization.Name, dest); user == nil {
|
||||
if user = object.GetUserByPhone(organization.Name, vform.Dest); user == nil {
|
||||
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
|
||||
return
|
||||
}
|
||||
|
||||
countryCode = user.GetCountryCode(countryCode)
|
||||
} else if method == ResetVerification {
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
} else if vform.Method == ResetVerification {
|
||||
if user = c.getCurrentUser(); user != nil {
|
||||
countryCode = user.GetCountryCode(countryCode)
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
}
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferMfa(false)
|
||||
if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
|
||||
vform.CountryCode = mfaProps.CountryCode
|
||||
}
|
||||
|
||||
provider := application.GetSmsProvider()
|
||||
if phone, ok := util.GetE164Number(dest, countryCode); !ok {
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), countryCode))
|
||||
if phone, ok := util.GetE164Number(vform.Dest, vform.CountryCode); !ok {
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), vform.CountryCode))
|
||||
return
|
||||
} else {
|
||||
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, phone)
|
||||
}
|
||||
}
|
||||
|
||||
if vform.Method == MfaSetupVerification {
|
||||
c.SetSession(object.MfaSmsCountryCodeSession, vform.CountryCode)
|
||||
c.SetSession(object.MfaSmsDestSession, vform.Dest)
|
||||
}
|
||||
|
||||
if sendResp != nil {
|
||||
c.ResponseError(sendResp.Error())
|
||||
} else {
|
||||
@ -167,6 +161,38 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyCaptcha ...
|
||||
// @Title VerifyCaptcha
|
||||
// @Tag Verification API
|
||||
// @router /verify-captcha [post]
|
||||
func (c *ApiController) VerifyCaptcha() {
|
||||
var vform form.VerificationForm
|
||||
err := c.ParseForm(&vform)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if msg := vform.CheckParameter(form.VerifyCaptcha, c.GetAcceptLanguage()); msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
provider := captcha.GetCaptchaProvider(vform.CaptchaType)
|
||||
if provider == nil {
|
||||
c.ResponseError(c.T("verification:Invalid captcha provider."))
|
||||
return
|
||||
}
|
||||
|
||||
isValid, err := provider.VerifyCaptcha(vform.CaptchaToken, vform.ClientSecret)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(isValid)
|
||||
}
|
||||
|
||||
// ResetEmailOrPhone ...
|
||||
// @Tag Account API
|
||||
// @Title ResetEmailOrPhone
|
||||
@ -200,7 +226,7 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
return
|
||||
}
|
||||
|
||||
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user, c.GetAcceptLanguage()); !pass {
|
||||
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user.IsAdminUser(), c.GetAcceptLanguage()); !pass {
|
||||
c.ResponseError(errMsg)
|
||||
return
|
||||
}
|
||||
@ -220,11 +246,12 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
return
|
||||
}
|
||||
|
||||
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user, c.GetAcceptLanguage()); !pass {
|
||||
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user.IsAdminUser(), c.GetAcceptLanguage()); !pass {
|
||||
c.ResponseError(errMsg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess {
|
||||
c.ResponseError(result.Msg)
|
||||
return
|
||||
@ -247,88 +274,55 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
}
|
||||
|
||||
// VerifyCode
|
||||
// @Tag Account API
|
||||
// @Tag Verification API
|
||||
// @Title VerifyCode
|
||||
// @router /api/verify-code [post]
|
||||
func (c *ApiController) VerifyCode() {
|
||||
var form RequestForm
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
|
||||
var authForm form.AuthForm
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
if form.Name != "" {
|
||||
user = object.GetUserByFields(form.Organization, form.Name)
|
||||
if authForm.Name != "" {
|
||||
user = object.GetUserByFields(authForm.Organization, authForm.Name)
|
||||
}
|
||||
|
||||
var checkDest string
|
||||
if strings.Contains(form.Username, "@") {
|
||||
if user != nil && util.GetMaskedEmail(user.Email) == form.Username {
|
||||
form.Username = user.Email
|
||||
if strings.Contains(authForm.Username, "@") {
|
||||
if user != nil && util.GetMaskedEmail(user.Email) == authForm.Username {
|
||||
authForm.Username = user.Email
|
||||
}
|
||||
checkDest = form.Username
|
||||
checkDest = authForm.Username
|
||||
} else {
|
||||
if user != nil && util.GetMaskedPhone(user.Phone) == form.Username {
|
||||
form.Username = user.Phone
|
||||
if user != nil && util.GetMaskedPhone(user.Phone) == authForm.Username {
|
||||
authForm.Username = user.Phone
|
||||
}
|
||||
}
|
||||
|
||||
if user = object.GetUserByFields(form.Organization, form.Username); user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
|
||||
if user = object.GetUserByFields(authForm.Organization, authForm.Username); user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(authForm.Organization, authForm.Username)))
|
||||
return
|
||||
}
|
||||
|
||||
verificationCodeType := object.GetVerifyType(form.Username)
|
||||
verificationCodeType := object.GetVerifyType(authForm.Username)
|
||||
if verificationCodeType == object.VerifyTypePhone {
|
||||
form.CountryCode = user.GetCountryCode(form.CountryCode)
|
||||
authForm.CountryCode = user.GetCountryCode(authForm.CountryCode)
|
||||
var ok bool
|
||||
if checkDest, ok = util.GetE164Number(form.Username, form.CountryCode); !ok {
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), form.CountryCode))
|
||||
if checkDest, ok = util.GetE164Number(authForm.Username, authForm.CountryCode); !ok {
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), authForm.CountryCode))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result := object.CheckVerificationCode(checkDest, form.Code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess {
|
||||
if result := object.CheckVerificationCode(checkDest, authForm.Code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess {
|
||||
c.ResponseError(result.Msg)
|
||||
return
|
||||
}
|
||||
object.DisableVerificationCode(checkDest)
|
||||
c.SetSession("verifiedCode", form.Code)
|
||||
c.SetSession("verifiedCode", authForm.Code)
|
||||
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
||||
// VerifyCaptcha ...
|
||||
// @Title VerifyCaptcha
|
||||
// @Tag Verification API
|
||||
// @router /verify-captcha [post]
|
||||
func (c *ApiController) VerifyCaptcha() {
|
||||
captchaType := c.Ctx.Request.Form.Get("captchaType")
|
||||
|
||||
captchaToken := c.Ctx.Request.Form.Get("captchaToken")
|
||||
clientSecret := c.Ctx.Request.Form.Get("clientSecret")
|
||||
if captchaToken == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter") + ": captchaToken.")
|
||||
return
|
||||
}
|
||||
if clientSecret == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter") + ": clientSecret.")
|
||||
return
|
||||
}
|
||||
|
||||
provider := captcha.GetCaptchaProvider(captchaType)
|
||||
if provider == nil {
|
||||
c.ResponseError(c.T("verification:Invalid captcha provider."))
|
||||
return
|
||||
}
|
||||
|
||||
isValid, err := provider.VerifyCaptcha(captchaToken, clientSecret)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(isValid)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/casdoor/casdoor/form"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
@ -147,9 +148,9 @@ func (c *ApiController) WebAuthnSigninFinish() {
|
||||
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
|
||||
|
||||
application := object.GetApplicationByUser(user)
|
||||
var form RequestForm
|
||||
form.Type = responseType
|
||||
resp := c.HandleLoggedIn(application, user, &form)
|
||||
var authForm form.AuthForm
|
||||
authForm.Type = responseType
|
||||
resp := c.HandleLoggedIn(application, user, &authForm)
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
@ -37,13 +37,14 @@ func (c *ApiController) GetWebhooks() {
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
organization := c.Input().Get("organization")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetWebhooks(owner)
|
||||
c.Data["json"] = object.GetWebhooks(owner, organization)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetWebhookCount(owner, field, value)))
|
||||
webhooks := object.GetPaginationWebhooks(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetWebhookCount(owner, organization, field, value)))
|
||||
webhooks := object.GetPaginationWebhooks(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(webhooks, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
57
form/auth.go
Normal file
57
form/auth.go
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package form
|
||||
|
||||
type AuthForm struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
Organization string `json:"organization"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Name string `json:"name"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Affiliation string `json:"affiliation"`
|
||||
IdCard string `json:"idCard"`
|
||||
Region string `json:"region"`
|
||||
|
||||
Application string `json:"application"`
|
||||
ClientId string `json:"clientId"`
|
||||
Provider string `json:"provider"`
|
||||
Code string `json:"code"`
|
||||
State string `json:"state"`
|
||||
RedirectUri string `json:"redirectUri"`
|
||||
Method string `json:"method"`
|
||||
|
||||
EmailCode string `json:"emailCode"`
|
||||
PhoneCode string `json:"phoneCode"`
|
||||
CountryCode string `json:"countryCode"`
|
||||
|
||||
AutoSignin bool `json:"autoSignin"`
|
||||
|
||||
RelayState string `json:"relayState"`
|
||||
SamlRequest string `json:"samlRequest"`
|
||||
SamlResponse string `json:"samlResponse"`
|
||||
|
||||
CaptchaType string `json:"captchaType"`
|
||||
CaptchaToken string `json:"captchaToken"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
|
||||
MfaType string `json:"mfaType"`
|
||||
Passcode string `json:"passcode"`
|
||||
RecoveryCode string `json:"recoveryCode"`
|
||||
}
|
67
form/verification.go
Normal file
67
form/verification.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package form
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
)
|
||||
|
||||
type VerificationForm struct {
|
||||
Dest string `form:"dest"`
|
||||
Type string `form:"type"`
|
||||
CountryCode string `form:"countryCode"`
|
||||
ApplicationId string `form:"applicationId"`
|
||||
Method string `form:"method"`
|
||||
CheckUser string `form:"checkUser"`
|
||||
|
||||
CaptchaType string `form:"captchaType"`
|
||||
ClientSecret string `form:"clientSecret"`
|
||||
CaptchaToken string `form:"captchaToken"`
|
||||
}
|
||||
|
||||
const (
|
||||
SendVerifyCode = 0
|
||||
VerifyCaptcha = 1
|
||||
)
|
||||
|
||||
func (form *VerificationForm) CheckParameter(checkType int, lang string) string {
|
||||
if checkType == SendVerifyCode {
|
||||
if form.Type == "" {
|
||||
return i18n.Translate(lang, "general:Missing parameter") + ": type."
|
||||
}
|
||||
if form.Dest == "" {
|
||||
return i18n.Translate(lang, "general:Missing parameter") + ": dest."
|
||||
}
|
||||
if form.CaptchaType == "" {
|
||||
return i18n.Translate(lang, "general:Missing parameter") + ": checkType."
|
||||
}
|
||||
if !strings.Contains(form.ApplicationId, "/") {
|
||||
return i18n.Translate(lang, "verification:Wrong parameter") + ": applicationId."
|
||||
}
|
||||
}
|
||||
|
||||
if form.CaptchaType != "none" {
|
||||
if form.CaptchaToken == "" {
|
||||
return i18n.Translate(lang, "general:Missing parameter") + ": captchaToken."
|
||||
}
|
||||
if form.ClientSecret == "" {
|
||||
return i18n.Translate(lang, "general:Missing parameter") + ": clientSecret."
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
7
go.mod
7
go.mod
@ -17,6 +17,7 @@ require (
|
||||
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
github.com/dlclark/regexp2 v1.9.0 // indirect
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/forestmgy/ldapserver v1.1.0
|
||||
github.com/go-git/go-git/v5 v5.6.0
|
||||
@ -36,15 +37,19 @@ require (
|
||||
github.com/markbates/goth v1.75.2
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/nyaruka/phonenumbers v1.1.5
|
||||
github.com/pkoukk/tiktoken-go v0.1.1
|
||||
github.com/prometheus/client_golang v1.7.0
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/russellhaering/gosaml2 v0.6.0
|
||||
github.com/russellhaering/goxmldsig v1.1.1
|
||||
github.com/sashabaranov/go-openai v1.9.1
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/tealeg/xlsx v1.0.5
|
||||
github.com/thanhpk/randstr v1.0.4
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
|
10
go.sum
10
go.sum
@ -161,6 +161,9 @@ github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D
|
||||
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI=
|
||||
github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
@ -476,6 +479,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkoukk/tiktoken-go v0.1.1 h1:jtkYlIECjyM9OW1w4rjPmTohK4arORP9V25y6TM6nXo=
|
||||
github.com/pkoukk/tiktoken-go v0.1.1/go.mod h1:boMWvk9pQCOTx11pgu0DrIdrAKgQzzJKUP6vLXaz7Rw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@ -520,6 +525,8 @@ github.com/russellhaering/goxmldsig v1.1.1 h1:vI0r2osGF1A9PLvsGdPUAGwEIrKa4Pj5se
|
||||
github.com/russellhaering/goxmldsig v1.1.1/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sashabaranov/go-openai v1.9.1 h1:3N52HkJKo9Zlo/oe1AVv5ZkCOny0ra58/ACvAxkN3MM=
|
||||
github.com/sashabaranov/go-openai v1.9.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
@ -571,8 +578,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Zugehörigkeit darf nicht leer sein",
|
||||
"DisplayName cannot be blank": "Anzeigename kann nicht leer sein",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "Nicht in der Lage, die E-Mail-Änderungsregel zu erhalten.",
|
||||
"Unable to get the phone modify rule.": "Nicht in der Lage, die Telefon-Änderungsregel zu erhalten.",
|
||||
"Unknown type": "Unbekannter Typ",
|
||||
"Wrong parameter": "Falscher Parameter",
|
||||
"Wrong verification code!": "Falscher Bestätigungscode!",
|
||||
"You should verify your code in %d min!": "Du solltest deinen Code in %d Minuten verifizieren!",
|
||||
"the user does not exist, please sign up first": "Der Benutzer existiert nicht, bitte zuerst anmelden"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
|
||||
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong parameter": "Wrong parameter",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Afiliación no puede estar en blanco",
|
||||
"DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "No se puede obtener la regla de modificación de correo electrónico.",
|
||||
"Unable to get the phone modify rule.": "No se pudo obtener la regla de modificación del teléfono.",
|
||||
"Unknown type": "Tipo desconocido",
|
||||
"Wrong parameter": "Parámetro incorrecto",
|
||||
"Wrong verification code!": "¡Código de verificación incorrecto!",
|
||||
"You should verify your code in %d min!": "¡Deberías verificar tu código en %d minutos!",
|
||||
"the user does not exist, please sign up first": "El usuario no existe, por favor regístrese primero"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation ne peut pas être vide",
|
||||
"DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "Incapable d'obtenir la règle de modification de courriel.",
|
||||
"Unable to get the phone modify rule.": "Impossible d'obtenir la règle de modification de téléphone.",
|
||||
"Unknown type": "Type inconnu",
|
||||
"Wrong parameter": "Mauvais paramètre",
|
||||
"Wrong verification code!": "Mauvais code de vérification !",
|
||||
"You should verify your code in %d min!": "Vous devriez vérifier votre code en %d min !",
|
||||
"the user does not exist, please sign up first": "L'utilisateur n'existe pas, veuillez vous inscrire d'abord"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Keterkaitan tidak boleh kosong",
|
||||
"DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "Tidak dapat memperoleh aturan modifikasi email.",
|
||||
"Unable to get the phone modify rule.": "Tidak dapat memodifikasi aturan telepon.",
|
||||
"Unknown type": "Tipe tidak diketahui",
|
||||
"Wrong parameter": "Parameter yang salah",
|
||||
"Wrong verification code!": "Kode verifikasi salah!",
|
||||
"You should verify your code in %d min!": "Anda harus memverifikasi kode Anda dalam %d menit!",
|
||||
"the user does not exist, please sign up first": "Pengguna tidak ada, silakan daftar terlebih dahulu"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "サービス%sと%sは一致しません"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "所属は空白にできません",
|
||||
"DisplayName cannot be blank": "表示名は空白にできません",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "電子メール変更規則を取得できません。",
|
||||
"Unable to get the phone modify rule.": "電話の変更ルールを取得できません。",
|
||||
"Unknown type": "不明なタイプ",
|
||||
"Wrong parameter": "誤ったパラメータ",
|
||||
"Wrong verification code!": "誤った検証コードです!",
|
||||
"You should verify your code in %d min!": "あなたは%d分であなたのコードを確認する必要があります!",
|
||||
"the user does not exist, please sign up first": "ユーザーは存在しません。まず登録してください"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "소속은 비워 둘 수 없습니다",
|
||||
"DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "이메일 수정 규칙을 가져올 수 없습니다.",
|
||||
"Unable to get the phone modify rule.": "전화 수정 규칙을 가져올 수 없습니다.",
|
||||
"Unknown type": "알 수 없는 유형",
|
||||
"Wrong parameter": "잘못된 매개 변수입니다",
|
||||
"Wrong verification code!": "잘못된 인증 코드입니다!",
|
||||
"You should verify your code in %d min!": "당신은 %d분 안에 코드를 검증해야 합니다!",
|
||||
"the user does not exist, please sign up first": "사용자가 존재하지 않습니다. 먼저 회원 가입 해주세요"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Принадлежность не может быть пустым значением",
|
||||
"DisplayName cannot be blank": "Имя отображения не может быть пустым",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "Невозможно получить правило изменения электронной почты.",
|
||||
"Unable to get the phone modify rule.": "Невозможно получить правило изменения телефона.",
|
||||
"Unknown type": "Неизвестный тип",
|
||||
"Wrong parameter": "Неправильный параметр",
|
||||
"Wrong verification code!": "Неправильный код подтверждения!",
|
||||
"You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!",
|
||||
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Tình trạng liên kết không thể để trống",
|
||||
"DisplayName cannot be blank": "Tên hiển thị không thể để trống",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "Không thể lấy quy tắc sửa đổi email.",
|
||||
"Unable to get the phone modify rule.": "Không thể thay đổi quy tắc trên điện thoại.",
|
||||
"Unknown type": "Loại không xác định",
|
||||
"Wrong parameter": "Tham số không đúng",
|
||||
"Wrong verification code!": "Mã xác thực sai!",
|
||||
"You should verify your code in %d min!": "Bạn nên kiểm tra mã của mình trong %d phút!",
|
||||
"the user does not exist, please sign up first": "Người dùng không tồn tại, vui lòng đăng ký trước"
|
||||
|
@ -23,6 +23,14 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "服务%s与%s不匹配"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "工作单位不可为空",
|
||||
"DisplayName cannot be blank": "显示名称不可为空",
|
||||
@ -130,7 +138,6 @@
|
||||
"Unable to get the email modify rule.": "无法获取邮箱修改规则",
|
||||
"Unable to get the phone modify rule.": "无法获取手机号修改规则",
|
||||
"Unknown type": "未知类型",
|
||||
"Wrong parameter": "参数错误",
|
||||
"Wrong verification code!": "验证码错误!",
|
||||
"You should verify your code in %d min!": "请在 %d 分钟内输入正确验证码",
|
||||
"the user does not exist, please sign up first": "用户不存在,请先注册"
|
||||
|
16
i18n/util.go
16
i18n/util.go
@ -74,13 +74,12 @@ func applyData(data1 *I18nData, data2 *I18nData) {
|
||||
}
|
||||
|
||||
func Translate(lang string, error string) string {
|
||||
parts := strings.SplitN(error, ":", 2)
|
||||
if !strings.Contains(error, ":") || len(parts) != 2 {
|
||||
tokens := strings.SplitN(error, ":", 2)
|
||||
if !strings.Contains(error, ":") || len(tokens) != 2 {
|
||||
return "Translate Error: " + error
|
||||
}
|
||||
if langMap[lang] != nil {
|
||||
return langMap[lang][parts[0]][parts[1]]
|
||||
} else {
|
||||
|
||||
if langMap[lang] == nil {
|
||||
file, _ := f.ReadFile("locales/" + lang + "/data.json")
|
||||
data := I18nData{}
|
||||
err := util.JsonToStruct(string(file), &data)
|
||||
@ -88,6 +87,11 @@ func Translate(lang string, error string) string {
|
||||
panic(err)
|
||||
}
|
||||
langMap[lang] = data
|
||||
return langMap[lang][parts[0]][parts[1]]
|
||||
}
|
||||
|
||||
res := langMap[lang][tokens[0]][tokens[1]]
|
||||
if res == "" {
|
||||
res = tokens[1]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
@ -125,8 +126,8 @@ type DingTalkUserResponse struct {
|
||||
UnionId string `json:"unionId"`
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
Email string `json:"email"`
|
||||
Errmsg string `json:"message"`
|
||||
Errcode string `json:"code"`
|
||||
Mobile string `json:"mobile"`
|
||||
StateCode string `json:"stateCode"`
|
||||
}
|
||||
|
||||
// GetUserInfo Use access_token to get UserInfo
|
||||
@ -156,8 +157,9 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dtUserInfo.Errmsg != "" {
|
||||
return nil, fmt.Errorf("userIdResp.Errcode = %s, userIdResp.Errmsg = %s", dtUserInfo.Errcode, dtUserInfo.Errmsg)
|
||||
countryCode, err := util.GetCountryCode(dtUserInfo.StateCode, dtUserInfo.Mobile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfo := UserInfo{
|
||||
@ -166,6 +168,8 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
DisplayName: dtUserInfo.Nick,
|
||||
UnionId: dtUserInfo.UnionId,
|
||||
Email: dtUserInfo.Email,
|
||||
Phone: dtUserInfo.Mobile,
|
||||
CountryCode: countryCode,
|
||||
AvatarUrl: dtUserInfo.AvatarUrl,
|
||||
}
|
||||
|
||||
@ -175,9 +179,15 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
corpEmail, err := idp.getUserCorpEmail(userId, corpAccessToken)
|
||||
if err == nil && corpEmail != "" {
|
||||
userInfo.Email = corpEmail
|
||||
corpEmail, jobNumber, err := idp.getUserCorpEmail(userId, corpAccessToken)
|
||||
if err == nil {
|
||||
if corpEmail != "" {
|
||||
userInfo.Email = corpEmail
|
||||
}
|
||||
|
||||
if jobNumber != "" {
|
||||
userInfo.Username = jobNumber
|
||||
}
|
||||
}
|
||||
|
||||
return &userInfo, nil
|
||||
@ -247,33 +257,34 @@ func (idp *DingTalkIdProvider) getUserId(unionId string, accessToken string) (st
|
||||
return "", err
|
||||
}
|
||||
if data.ErrCode == 60121 {
|
||||
return "", fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
|
||||
return "", fmt.Errorf("该应用只允许本企业内部用户登录,您不属于该企业,无法登录")
|
||||
} else if data.ErrCode != 0 {
|
||||
return "", fmt.Errorf(data.ErrMessage)
|
||||
}
|
||||
return data.Result.UserId, nil
|
||||
}
|
||||
|
||||
func (idp *DingTalkIdProvider) getUserCorpEmail(userId string, accessToken string) (string, error) {
|
||||
func (idp *DingTalkIdProvider) getUserCorpEmail(userId string, accessToken string) (string, string, error) {
|
||||
body := make(map[string]string)
|
||||
body["userid"] = userId
|
||||
respBytes, err := idp.postWithBody(body, "https://oapi.dingtalk.com/topapi/v2/user/get?access_token="+accessToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var data struct {
|
||||
ErrMessage string `json:"errmsg"`
|
||||
Result struct {
|
||||
Email string `json:"email"`
|
||||
Email string `json:"email"`
|
||||
JobNumber string `json:"job_number"`
|
||||
} `json:"result"`
|
||||
}
|
||||
err = json.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
if data.ErrMessage != "ok" {
|
||||
return "", fmt.Errorf(data.ErrMessage)
|
||||
return "", "", fmt.Errorf(data.ErrMessage)
|
||||
}
|
||||
return data.Result.Email, nil
|
||||
return data.Result.Email, data.Result.JobNumber, nil
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ type UserInfo struct {
|
||||
DisplayName string
|
||||
UnionId string
|
||||
Email string
|
||||
Phone string
|
||||
CountryCode string
|
||||
AvatarUrl string
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
}
|
||||
|
||||
bindPassword := string(r.AuthenticationSimple())
|
||||
bindUser, err := object.CheckUserPassword(object.CasdoorOrganization, bindUsername, bindPassword, "en")
|
||||
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
|
||||
if err != "" {
|
||||
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
|
||||
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
|
||||
|
13
ldap/util.go
13
ldap/util.go
@ -57,11 +57,18 @@ func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
||||
func getUsername(filter string) string {
|
||||
nameIndex := strings.Index(filter, "cn=")
|
||||
if nameIndex == -1 {
|
||||
return "*"
|
||||
nameIndex = strings.Index(filter, "uid=")
|
||||
if nameIndex == -1 {
|
||||
return "*"
|
||||
} else {
|
||||
nameIndex += 4
|
||||
}
|
||||
} else {
|
||||
nameIndex += 3
|
||||
}
|
||||
|
||||
var name string
|
||||
for i := nameIndex + 3; filter[i] != ')'; i++ {
|
||||
for i := nameIndex; filter[i] != ')'; i++ {
|
||||
name = name + string(filter[i])
|
||||
}
|
||||
return name
|
||||
@ -125,6 +132,8 @@ func getAttribute(attributeName string, user *object.User) message.AttributeValu
|
||||
return message.AttributeValue(user.Name)
|
||||
case "email":
|
||||
return message.AttributeValue(user.Email)
|
||||
case "mail":
|
||||
return message.AttributeValue(user.Email)
|
||||
case "mobile":
|
||||
return message.AttributeValue(user.Phone)
|
||||
case "userPassword":
|
||||
|
2
main.go
2
main.go
@ -60,6 +60,7 @@ func main() {
|
||||
beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
|
||||
beego.InsertFilter("*", beego.BeforeRouter, routers.AuthzFilter)
|
||||
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
|
||||
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
|
||||
|
||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||
beego.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"
|
||||
@ -82,6 +83,7 @@ func main() {
|
||||
logs.SetLogFuncCall(false)
|
||||
|
||||
go ldap.StartLdapServer()
|
||||
go object.ClearThroughputPerSecond()
|
||||
|
||||
beego.Run(fmt.Sprintf(":%v", port))
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func GetApplications(owner string) []*Application {
|
||||
|
||||
func GetOrganizationApplications(owner string, organization string) []*Application {
|
||||
applications := []*Application{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization})
|
||||
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -131,7 +131,7 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
|
||||
func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Application {
|
||||
applications := []*Application{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&applications, &Application{Owner: owner, Organization: organization})
|
||||
err := session.Find(&applications, &Application{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -46,9 +46,9 @@ type CasbinAdapter struct {
|
||||
Adapter *xormadapter.Adapter `xorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
func GetCasbinAdapterCount(owner, field, value string) int {
|
||||
func GetCasbinAdapterCount(owner, organization, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&CasbinAdapter{})
|
||||
count, err := session.Count(&CasbinAdapter{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -56,9 +56,9 @@ func GetCasbinAdapterCount(owner, field, value string) int {
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetCasbinAdapters(owner string) []*CasbinAdapter {
|
||||
func GetCasbinAdapters(owner string, organization string) []*CasbinAdapter {
|
||||
adapters := []*CasbinAdapter{}
|
||||
err := adapter.Engine.Where("owner = ?", owner).Find(&adapters)
|
||||
err := adapter.Engine.Where("owner = ? and organization = ?", owner, organization).Find(&adapters)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -66,10 +66,10 @@ func GetCasbinAdapters(owner string) []*CasbinAdapter {
|
||||
return adapters
|
||||
}
|
||||
|
||||
func GetPaginationCasbinAdapters(owner string, page, limit int, field, value, sort, order string) []*CasbinAdapter {
|
||||
func GetPaginationCasbinAdapters(owner, organization string, page, limit int, field, value, sort, order string) []*CasbinAdapter {
|
||||
session := GetSession(owner, page, limit, field, value, sort, order)
|
||||
adapters := []*CasbinAdapter{}
|
||||
err := session.Find(&adapters)
|
||||
err := session.Find(&adapters, &CasbinAdapter{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -55,8 +55,8 @@ func GetMaskedCerts(certs []*Cert) []*Cert {
|
||||
}
|
||||
|
||||
func GetCertCount(owner, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Cert{})
|
||||
session := GetSession("", -1, -1, field, value, "", "")
|
||||
count, err := session.Where("owner = ? or owner = ? ", "admin", owner).Count(&Cert{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -66,7 +66,7 @@ func GetCertCount(owner, field, value string) int {
|
||||
|
||||
func GetCerts(owner string) []*Cert {
|
||||
certs := []*Cert{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&certs, &Cert{Owner: owner})
|
||||
err := adapter.Engine.Where("owner = ? or owner = ? ", "admin", owner).Desc("created_time").Find(&certs, &Cert{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -76,7 +76,38 @@ func GetCerts(owner string) []*Cert {
|
||||
|
||||
func GetPaginationCerts(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Cert {
|
||||
certs := []*Cert{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Where("owner = ? or owner = ? ", "admin", owner).Find(&certs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return certs
|
||||
}
|
||||
|
||||
func GetGlobalCertsCount(field, value string) int {
|
||||
session := GetSession("", -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Cert{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetGlobleCerts() []*Cert {
|
||||
certs := []*Cert{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&certs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return certs
|
||||
}
|
||||
|
||||
func GetPaginationGlobalCerts(offset, limit int, field, value, sortField, sortOrder string) []*Cert {
|
||||
certs := []*Cert{}
|
||||
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&certs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -121,6 +121,13 @@ func UpdateChat(id string, chat *Chat) bool {
|
||||
}
|
||||
|
||||
func AddChat(chat *Chat) bool {
|
||||
if chat.Type == "AI" && chat.User2 == "" {
|
||||
provider := getDefaultAiProvider()
|
||||
if provider != nil {
|
||||
chat.User2 = provider.Name
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.Insert(chat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -135,6 +142,10 @@ func DeleteChat(chat *Chat) bool {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
return DeleteChatMessages(chat.Name)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/casdoor/casdoor/cred"
|
||||
"github.com/casdoor/casdoor/form"
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
@ -42,86 +43,86 @@ func init() {
|
||||
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
|
||||
}
|
||||
|
||||
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, countryCode string, affiliation string, lang string) string {
|
||||
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
|
||||
if organization == nil {
|
||||
return i18n.Translate(lang, "check:Organization does not exist")
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Username") {
|
||||
if len(username) <= 1 {
|
||||
if len(form.Username) <= 1 {
|
||||
return i18n.Translate(lang, "check:Username must have at least 2 characters")
|
||||
}
|
||||
if unicode.IsDigit(rune(username[0])) {
|
||||
if unicode.IsDigit(rune(form.Username[0])) {
|
||||
return i18n.Translate(lang, "check:Username cannot start with a digit")
|
||||
}
|
||||
if util.IsEmailValid(username) {
|
||||
if util.IsEmailValid(form.Username) {
|
||||
return i18n.Translate(lang, "check:Username cannot be an email address")
|
||||
}
|
||||
if reWhiteSpace.MatchString(username) {
|
||||
if reWhiteSpace.MatchString(form.Username) {
|
||||
return i18n.Translate(lang, "check:Username cannot contain white spaces")
|
||||
}
|
||||
|
||||
if msg := CheckUsername(username, lang); msg != "" {
|
||||
if msg := CheckUsername(form.Username, lang); msg != "" {
|
||||
return msg
|
||||
}
|
||||
|
||||
if HasUserByField(organization.Name, "name", username) {
|
||||
if HasUserByField(organization.Name, "name", form.Username) {
|
||||
return i18n.Translate(lang, "check:Username already exists")
|
||||
}
|
||||
if HasUserByField(organization.Name, "email", email) {
|
||||
if HasUserByField(organization.Name, "email", form.Email) {
|
||||
return i18n.Translate(lang, "check:Email already exists")
|
||||
}
|
||||
if HasUserByField(organization.Name, "phone", phone) {
|
||||
if HasUserByField(organization.Name, "phone", form.Phone) {
|
||||
return i18n.Translate(lang, "check:Phone already exists")
|
||||
}
|
||||
}
|
||||
|
||||
if len(password) <= 5 {
|
||||
if len(form.Password) <= 5 {
|
||||
return i18n.Translate(lang, "check:Password must have at least 6 characters")
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Email") {
|
||||
if email == "" {
|
||||
if form.Email == "" {
|
||||
if application.IsSignupItemRequired("Email") {
|
||||
return i18n.Translate(lang, "check:Email cannot be empty")
|
||||
}
|
||||
} else {
|
||||
if HasUserByField(organization.Name, "email", email) {
|
||||
if HasUserByField(organization.Name, "email", form.Email) {
|
||||
return i18n.Translate(lang, "check:Email already exists")
|
||||
} else if !util.IsEmailValid(email) {
|
||||
} else if !util.IsEmailValid(form.Email) {
|
||||
return i18n.Translate(lang, "check:Email is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Phone") {
|
||||
if phone == "" {
|
||||
if form.Phone == "" {
|
||||
if application.IsSignupItemRequired("Phone") {
|
||||
return i18n.Translate(lang, "check:Phone cannot be empty")
|
||||
}
|
||||
} else {
|
||||
if HasUserByField(organization.Name, "phone", phone) {
|
||||
if HasUserByField(organization.Name, "phone", form.Phone) {
|
||||
return i18n.Translate(lang, "check:Phone already exists")
|
||||
} else if !util.IsPhoneAllowInRegin(countryCode, organization.CountryCodes) {
|
||||
} else if !util.IsPhoneAllowInRegin(form.CountryCode, organization.CountryCodes) {
|
||||
return i18n.Translate(lang, "check:Your region is not allow to signup by phone")
|
||||
} else if !util.IsPhoneValid(phone, countryCode) {
|
||||
} else if !util.IsPhoneValid(form.Phone, form.CountryCode) {
|
||||
return i18n.Translate(lang, "check:Phone number is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Display name") {
|
||||
if application.GetSignupItemRule("Display name") == "First, last" && (firstName != "" || lastName != "") {
|
||||
if firstName == "" {
|
||||
if application.GetSignupItemRule("Display name") == "First, last" && (form.FirstName != "" || form.LastName != "") {
|
||||
if form.FirstName == "" {
|
||||
return i18n.Translate(lang, "check:FirstName cannot be blank")
|
||||
} else if lastName == "" {
|
||||
} else if form.LastName == "" {
|
||||
return i18n.Translate(lang, "check:LastName cannot be blank")
|
||||
}
|
||||
} else {
|
||||
if displayName == "" {
|
||||
if form.Name == "" {
|
||||
return i18n.Translate(lang, "check:DisplayName cannot be blank")
|
||||
} else if application.GetSignupItemRule("Display name") == "Real name" {
|
||||
if !isValidRealName(displayName) {
|
||||
if !isValidRealName(form.Name) {
|
||||
return i18n.Translate(lang, "check:DisplayName is not valid real name")
|
||||
}
|
||||
}
|
||||
@ -129,7 +130,7 @@ func CheckUserSignup(application *Application, organization *Organization, usern
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Affiliation") {
|
||||
if affiliation == "" {
|
||||
if form.Affiliation == "" {
|
||||
return i18n.Translate(lang, "check:Affiliation cannot be blank")
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,9 @@ import (
|
||||
|
||||
func getDialer(provider *Provider) *gomail.Dialer {
|
||||
dialer := &gomail.Dialer{}
|
||||
dialer = gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret)
|
||||
if provider.Type == "SUBMAIL" {
|
||||
dialer = gomail.NewDialer(provider.Host, provider.Port, provider.AppId, provider.ClientSecret)
|
||||
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
} else {
|
||||
dialer = gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret)
|
||||
}
|
||||
|
||||
dialer.SSL = !provider.DisableSsl
|
||||
@ -40,14 +38,23 @@ func SendEmail(provider *Provider, title string, content string, dest string, se
|
||||
dialer := getDialer(provider)
|
||||
|
||||
message := gomail.NewMessage()
|
||||
message.SetAddressHeader("From", provider.ClientId, sender)
|
||||
|
||||
fromAddress := provider.ClientId2
|
||||
if fromAddress == "" {
|
||||
fromAddress = provider.ClientId
|
||||
}
|
||||
|
||||
fromName := provider.ClientSecret2
|
||||
if fromName == "" {
|
||||
fromName = sender
|
||||
}
|
||||
|
||||
message.SetAddressHeader("From", fromAddress, fromName)
|
||||
message.SetHeader("To", dest)
|
||||
message.SetHeader("Subject", title)
|
||||
message.SetBody("text/html", content)
|
||||
|
||||
if provider.Type == "Mailtrap" {
|
||||
message.SkipUsernameCheck = true
|
||||
}
|
||||
message.SkipUsernameCheck = true
|
||||
|
||||
return dialer.DialAndSend(message)
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ func getBuiltInAccountItems() []*AccountItem {
|
||||
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ type Message struct {
|
||||
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
Chat string `xorm:"varchar(100) index" json:"chat"`
|
||||
ReplyTo string `xorm:"varchar(100) index" json:"replyTo"`
|
||||
Author string `xorm:"varchar(100)" json:"author"`
|
||||
Text string `xorm:"mediumtext" json:"text"`
|
||||
}
|
||||
@ -47,9 +48,9 @@ func GetMaskedMessages(messages []*Message) []*Message {
|
||||
return messages
|
||||
}
|
||||
|
||||
func GetMessageCount(owner, field, value string) int {
|
||||
func GetMessageCount(owner, organization, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Message{})
|
||||
count, err := session.Count(&Message{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -77,10 +78,10 @@ func GetChatMessages(chat string) []*Message {
|
||||
return messages
|
||||
}
|
||||
|
||||
func GetPaginationMessages(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Message {
|
||||
func GetPaginationMessages(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Message {
|
||||
messages := []*Message{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&messages)
|
||||
err := session.Find(&messages, &Message{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -143,6 +144,15 @@ func DeleteMessage(message *Message) bool {
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func DeleteChatMessages(chat string) bool {
|
||||
affected, err := adapter.Engine.Delete(&Message{Chat: chat})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func (p *Message) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
||||
|
107
object/mfa.go
Normal file
107
object/mfa.go
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
)
|
||||
|
||||
type MfaSessionData struct {
|
||||
UserId string
|
||||
}
|
||||
|
||||
type MfaProps struct {
|
||||
Id string `json:"id"`
|
||||
IsPreferred bool `json:"isPreferred"`
|
||||
AuthType string `json:"type" form:"type"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
CountryCode string `json:"countryCode,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
RecoveryCodes []string `json:"recoveryCodes,omitempty"`
|
||||
}
|
||||
|
||||
type MfaInterface interface {
|
||||
SetupVerify(ctx *context.Context, passCode string) error
|
||||
Verify(passCode string) error
|
||||
Initiate(ctx *context.Context, name1 string, name2 string) (*MfaProps, error)
|
||||
Enable(ctx *context.Context, user *User) error
|
||||
}
|
||||
|
||||
const (
|
||||
SmsType = "sms"
|
||||
TotpType = "app"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaSessionUserId = "MfaSessionUserId"
|
||||
NextMfa = "NextMfa"
|
||||
)
|
||||
|
||||
func GetMfaUtil(providerType string, config *MfaProps) MfaInterface {
|
||||
switch providerType {
|
||||
case SmsType:
|
||||
return NewSmsTwoFactor(config)
|
||||
case TotpType:
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RecoverTfs(user *User, recoveryCode string) error {
|
||||
hit := false
|
||||
|
||||
twoFactor := user.GetPreferMfa(false)
|
||||
if len(twoFactor.RecoveryCodes) == 0 {
|
||||
return fmt.Errorf("do not have recovery codes")
|
||||
}
|
||||
|
||||
for _, code := range twoFactor.RecoveryCodes {
|
||||
if code == recoveryCode {
|
||||
hit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hit {
|
||||
return fmt.Errorf("recovery code not found")
|
||||
}
|
||||
|
||||
affected := UpdateUser(user.GetId(), user, []string{"two_factor_auth"}, user.IsAdminUser())
|
||||
if !affected {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetMaskedProps(props *MfaProps) *MfaProps {
|
||||
maskedProps := &MfaProps{
|
||||
AuthType: props.AuthType,
|
||||
Id: props.Id,
|
||||
IsPreferred: props.IsPreferred,
|
||||
}
|
||||
|
||||
if props.AuthType == SmsType {
|
||||
if !util.IsEmailValid(props.Secret) {
|
||||
maskedProps.Secret = util.GetMaskedPhone(props.Secret)
|
||||
} else {
|
||||
maskedProps.Secret = util.GetMaskedEmail(props.Secret)
|
||||
}
|
||||
}
|
||||
return maskedProps
|
||||
}
|
120
object/mfa_sms.go
Normal file
120
object/mfa_sms.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaSmsCountryCodeSession = "mfa_country_code"
|
||||
MfaSmsDestSession = "mfa_dest"
|
||||
MfaSmsRecoveryCodesSession = "mfa_recovery_codes"
|
||||
)
|
||||
|
||||
type SmsMfa struct {
|
||||
Config *MfaProps
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
|
||||
dest := ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
|
||||
countryCode := ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
|
||||
if !util.IsEmailValid(dest) {
|
||||
dest, _ = util.GetE164Number(dest, countryCode)
|
||||
}
|
||||
|
||||
if result := CheckVerificationCode(dest, passCode, "en"); result.Code != VerificationSuccess {
|
||||
return errors.New(result.Msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Verify(passCode string) error {
|
||||
if !util.IsEmailValid(mfa.Config.Secret) {
|
||||
mfa.Config.Secret, _ = util.GetE164Number(mfa.Config.Secret, mfa.Config.CountryCode)
|
||||
}
|
||||
if result := CheckVerificationCode(mfa.Config.Secret, passCode, "en"); result.Code != VerificationSuccess {
|
||||
return errors.New(result.Msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Initiate(ctx *context.Context, name string, secret string) (*MfaProps, error) {
|
||||
recoveryCode, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.Input.CruSession.Set(MfaSmsRecoveryCodesSession, []string{recoveryCode.String()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mfaProps := MfaProps{
|
||||
AuthType: SmsType,
|
||||
RecoveryCodes: []string{recoveryCode.String()},
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
|
||||
dest := ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
|
||||
recoveryCodes := ctx.Input.CruSession.Get(MfaSmsRecoveryCodesSession).([]string)
|
||||
countryCode := ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
|
||||
|
||||
if dest == "" || len(recoveryCodes) == 0 {
|
||||
return fmt.Errorf("MFA dest or recovery codes is empty")
|
||||
}
|
||||
|
||||
if !util.IsEmailValid(dest) {
|
||||
mfa.Config.CountryCode = countryCode
|
||||
}
|
||||
|
||||
mfa.Config.AuthType = SmsType
|
||||
mfa.Config.Id = uuid.NewString()
|
||||
mfa.Config.Secret = dest
|
||||
mfa.Config.RecoveryCodes = recoveryCodes
|
||||
|
||||
for i, mfaProp := range user.MultiFactorAuths {
|
||||
if mfaProp.Secret == mfa.Config.Secret {
|
||||
user.MultiFactorAuths = append(user.MultiFactorAuths[:i], user.MultiFactorAuths[i+1:]...)
|
||||
}
|
||||
}
|
||||
user.MultiFactorAuths = append(user.MultiFactorAuths, mfa.Config)
|
||||
|
||||
affected := UpdateUser(user.GetId(), user, []string{"multi_factor_auths"}, user.IsAdminUser())
|
||||
if !affected {
|
||||
return fmt.Errorf("failed to enable two factor authentication")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSmsTwoFactor(config *MfaProps) *SmsMfa {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
AuthType: SmsType,
|
||||
}
|
||||
}
|
||||
return &SmsMfa{
|
||||
Config: config,
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ type Organization struct {
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
||||
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
|
||||
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||
Languages []string `xorm:"varchar(255)" json:"languages"`
|
||||
@ -209,14 +209,14 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
|
||||
func CheckAccountItemModifyRule(accountItem *AccountItem, isAdmin bool, lang string) (bool, string) {
|
||||
if accountItem == nil {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
switch accountItem.ModifyRule {
|
||||
case "Admin":
|
||||
if user == nil || !user.IsAdmin && !user.IsGlobalAdmin {
|
||||
if !isAdmin {
|
||||
return false, fmt.Sprintf(i18n.Translate(lang, "organization:Only admin can modify the %s."), accountItem.Name)
|
||||
}
|
||||
case "Immutable":
|
||||
|
@ -239,7 +239,7 @@ func DeletePermission(permission *Permission) bool {
|
||||
|
||||
func GetPermissionsByUser(userId string) []*Permission {
|
||||
permissions := []*Permission{}
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"\"%").Find(&permissions)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -253,7 +253,7 @@ func GetPermissionsByUser(userId string) []*Permission {
|
||||
|
||||
func GetPermissionsByRole(roleId string) []*Permission {
|
||||
permissions := []*Permission{}
|
||||
err := adapter.Engine.Where("roles like ?", "%"+roleId+"%").Find(&permissions)
|
||||
err := adapter.Engine.Where("roles like ?", "%"+roleId+"\"%").Find(&permissions)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -118,35 +118,53 @@ func getPolicies(permission *Permission) [][]string {
|
||||
return policies
|
||||
}
|
||||
|
||||
func getRolesInRole(roleId string, visited map[string]struct{}) []*Role {
|
||||
role := GetRole(roleId)
|
||||
if role == nil {
|
||||
return []*Role{}
|
||||
}
|
||||
visited[roleId] = struct{}{}
|
||||
|
||||
roles := []*Role{role}
|
||||
for _, subRole := range role.Roles {
|
||||
if _, ok := visited[subRole]; !ok {
|
||||
roles = append(roles, getRolesInRole(subRole, visited)...)
|
||||
}
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
func getGroupingPolicies(permission *Permission) [][]string {
|
||||
var groupingPolicies [][]string
|
||||
|
||||
domainExist := len(permission.Domains) > 0
|
||||
permissionId := permission.GetId()
|
||||
|
||||
for _, role := range permission.Roles {
|
||||
roleObj := GetRole(role)
|
||||
if roleObj == nil {
|
||||
continue
|
||||
}
|
||||
for _, roleId := range permission.Roles {
|
||||
visited := map[string]struct{}{}
|
||||
rolesInRole := getRolesInRole(roleId, visited)
|
||||
|
||||
for _, subUser := range roleObj.Users {
|
||||
if domainExist {
|
||||
for _, domain := range permission.Domains {
|
||||
groupingPolicies = append(groupingPolicies, []string{subUser, role, domain, "", "", permissionId})
|
||||
for _, role := range rolesInRole {
|
||||
roleId := role.GetId()
|
||||
for _, subUser := range role.Users {
|
||||
if domainExist {
|
||||
for _, domain := range permission.Domains {
|
||||
groupingPolicies = append(groupingPolicies, []string{subUser, roleId, domain, "", "", permissionId})
|
||||
}
|
||||
} else {
|
||||
groupingPolicies = append(groupingPolicies, []string{subUser, roleId, "", "", "", permissionId})
|
||||
}
|
||||
} else {
|
||||
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
|
||||
}
|
||||
}
|
||||
|
||||
for _, subRole := range roleObj.Roles {
|
||||
if domainExist {
|
||||
for _, domain := range permission.Domains {
|
||||
groupingPolicies = append(groupingPolicies, []string{subRole, role, domain, "", "", permissionId})
|
||||
for _, subRole := range role.Roles {
|
||||
if domainExist {
|
||||
for _, domain := range permission.Domains {
|
||||
groupingPolicies = append(groupingPolicies, []string{subRole, roleId, domain, "", "", permissionId})
|
||||
}
|
||||
} else {
|
||||
groupingPolicies = append(groupingPolicies, []string{subRole, roleId, "", "", "", permissionId})
|
||||
}
|
||||
} else {
|
||||
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
129
object/prometheus.go
Normal file
129
object/prometheus.go
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
type PrometheusInfo struct {
|
||||
ApiThroughput []GaugeVecInfo `json:"apiThroughput"`
|
||||
ApiLatency []HistogramVecInfo `json:"apiLatency"`
|
||||
TotalThroughput float64 `json:"totalThroughput"`
|
||||
}
|
||||
|
||||
type GaugeVecInfo struct {
|
||||
Method string `json:"method"`
|
||||
Name string `json:"name"`
|
||||
Throughput float64 `json:"throughput"`
|
||||
}
|
||||
|
||||
type HistogramVecInfo struct {
|
||||
Method string `json:"method"`
|
||||
Name string `json:"name"`
|
||||
Count uint64 `json:"count"`
|
||||
Latency string `json:"latency"`
|
||||
}
|
||||
|
||||
var (
|
||||
ApiThroughput = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "casdoor_api_throughput",
|
||||
Help: "The throughput of each api access",
|
||||
}, []string{"path", "method"})
|
||||
|
||||
ApiLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "casdoor_api_latency",
|
||||
Help: "API processing latency in milliseconds",
|
||||
}, []string{"path", "method"})
|
||||
|
||||
CpuUsage = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "casdoor_cpu_usage",
|
||||
Help: "Casdoor cpu usage",
|
||||
}, []string{"cpuNum"})
|
||||
|
||||
MemoryUsage = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "casdoor_memory_usage",
|
||||
Help: "Casdoor memory usage in Byte",
|
||||
}, []string{"type"})
|
||||
|
||||
TotalThroughput = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "casdoor_total_throughput",
|
||||
Help: "The total throughput of casdoor",
|
||||
})
|
||||
)
|
||||
|
||||
func ClearThroughputPerSecond() {
|
||||
// Clear the throughput every second
|
||||
ticker := time.NewTicker(time.Second)
|
||||
for range ticker.C {
|
||||
ApiThroughput.Reset()
|
||||
TotalThroughput.Set(0)
|
||||
}
|
||||
}
|
||||
|
||||
func GetPrometheusInfo() (*PrometheusInfo, error) {
|
||||
res := &PrometheusInfo{}
|
||||
metricFamilies, err := prometheus.DefaultGatherer.Gather()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, metricFamily := range metricFamilies {
|
||||
switch metricFamily.GetName() {
|
||||
case "casdoor_api_throughput":
|
||||
res.ApiThroughput = getGaugeVecInfo(metricFamily)
|
||||
case "casdoor_api_latency":
|
||||
res.ApiLatency = getHistogramVecInfo(metricFamily)
|
||||
case "casdoor_total_throughput":
|
||||
res.TotalThroughput = metricFamily.GetMetric()[0].GetGauge().GetValue()
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getHistogramVecInfo(metricFamily *io_prometheus_client.MetricFamily) []HistogramVecInfo {
|
||||
var histogramVecInfos []HistogramVecInfo
|
||||
for _, metric := range metricFamily.GetMetric() {
|
||||
sampleCount := metric.GetHistogram().GetSampleCount()
|
||||
sampleSum := metric.GetHistogram().GetSampleSum()
|
||||
latency := sampleSum / float64(sampleCount)
|
||||
histogramVecInfo := HistogramVecInfo{
|
||||
Method: metric.Label[0].GetValue(),
|
||||
Name: metric.Label[1].GetValue(),
|
||||
Count: sampleCount,
|
||||
Latency: fmt.Sprintf("%.3f", latency),
|
||||
}
|
||||
histogramVecInfos = append(histogramVecInfos, histogramVecInfo)
|
||||
}
|
||||
return histogramVecInfos
|
||||
}
|
||||
|
||||
func getGaugeVecInfo(metricFamily *io_prometheus_client.MetricFamily) []GaugeVecInfo {
|
||||
var counterVecInfos []GaugeVecInfo
|
||||
for _, metric := range metricFamily.GetMetric() {
|
||||
counterVecInfo := GaugeVecInfo{
|
||||
Method: metric.Label[0].GetValue(),
|
||||
Name: metric.Label[1].GetValue(),
|
||||
Throughput: metric.Gauge.GetValue(),
|
||||
}
|
||||
counterVecInfos = append(counterVecInfos, counterVecInfo)
|
||||
}
|
||||
return counterVecInfos
|
||||
}
|
@ -78,8 +78,11 @@ func GetMaskedProvider(provider *Provider) *Provider {
|
||||
if provider.ClientSecret != "" {
|
||||
provider.ClientSecret = "***"
|
||||
}
|
||||
if provider.ClientSecret2 != "" {
|
||||
provider.ClientSecret2 = "***"
|
||||
|
||||
if provider.Category != "Email" {
|
||||
if provider.ClientSecret2 != "" {
|
||||
provider.ClientSecret2 = "***"
|
||||
}
|
||||
}
|
||||
|
||||
return provider
|
||||
@ -177,8 +180,8 @@ func GetProvider(id string) *Provider {
|
||||
return getProvider(owner, name)
|
||||
}
|
||||
|
||||
func GetDefaultCaptchaProvider() *Provider {
|
||||
provider := Provider{Owner: "admin", Category: "Captcha"}
|
||||
func getDefaultAiProvider() *Provider {
|
||||
provider := Provider{Owner: "admin", Category: "AI"}
|
||||
existed, err := adapter.Engine.Get(&provider)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -94,10 +94,25 @@ func UpdateRole(id string, role *Role) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
visited := map[string]struct{}{}
|
||||
|
||||
permissions := GetPermissionsByRole(id)
|
||||
for _, permission := range permissions {
|
||||
removeGroupingPolicies(permission)
|
||||
removePolicies(permission)
|
||||
visited[permission.GetId()] = struct{}{}
|
||||
}
|
||||
|
||||
ancestorRoles := GetAncestorRoles(id)
|
||||
for _, r := range ancestorRoles {
|
||||
permissions := GetPermissionsByRole(r.GetId())
|
||||
for _, permission := range permissions {
|
||||
permissionId := permission.GetId()
|
||||
if _, ok := visited[permissionId]; !ok {
|
||||
removeGroupingPolicies(permission)
|
||||
visited[permissionId] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if name != role.Name {
|
||||
@ -112,11 +127,25 @@ func UpdateRole(id string, role *Role) bool {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
visited = map[string]struct{}{}
|
||||
newRoleID := role.GetId()
|
||||
permissions = GetPermissionsByRole(newRoleID)
|
||||
for _, permission := range permissions {
|
||||
addGroupingPolicies(permission)
|
||||
addPolicies(permission)
|
||||
visited[permission.GetId()] = struct{}{}
|
||||
}
|
||||
|
||||
ancestorRoles = GetAncestorRoles(newRoleID)
|
||||
for _, r := range ancestorRoles {
|
||||
permissions := GetPermissionsByRole(r.GetId())
|
||||
for _, permission := range permissions {
|
||||
permissionId := permission.GetId()
|
||||
if _, ok := visited[permissionId]; !ok {
|
||||
addGroupingPolicies(permission)
|
||||
visited[permissionId] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
@ -153,7 +182,7 @@ func (role *Role) GetId() string {
|
||||
|
||||
func GetRolesByUser(userId string) []*Role {
|
||||
roles := []*Role{}
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&roles)
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"\"%").Find(&roles)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -221,3 +250,64 @@ func GetMaskedRoles(roles []*Role) []*Role {
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
func GetRolesByNamePrefix(owner string, prefix string) []*Role {
|
||||
roles := []*Role{}
|
||||
err := adapter.Engine.Where("owner=? and name like ?", owner, prefix+"%").Find(&roles)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
func GetAncestorRoles(roleId string) []*Role {
|
||||
var (
|
||||
result []*Role
|
||||
roleMap = make(map[string]*Role)
|
||||
visited = make(map[string]bool)
|
||||
)
|
||||
|
||||
owner, _ := util.GetOwnerAndNameFromIdNoCheck(roleId)
|
||||
|
||||
allRoles := GetRoles(owner)
|
||||
for _, r := range allRoles {
|
||||
roleMap[r.GetId()] = r
|
||||
}
|
||||
|
||||
// Second, find all the roles that contain father roles
|
||||
for _, r := range allRoles {
|
||||
isContain, ok := visited[r.GetId()]
|
||||
if isContain {
|
||||
result = append(result, r)
|
||||
} else if !ok {
|
||||
rId := r.GetId()
|
||||
visited[rId] = containsRole(r, roleId, roleMap, visited)
|
||||
if visited[rId] {
|
||||
result = append(result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// containsRole is a helper function to check if a slice of roles contains a specific roleId
|
||||
func containsRole(role *Role, roleId string, roleMap map[string]*Role, visited map[string]bool) bool {
|
||||
if isContain, ok := visited[role.GetId()]; ok {
|
||||
return isContain
|
||||
}
|
||||
|
||||
for _, subRole := range role.Roles {
|
||||
if subRole == roleId {
|
||||
return true
|
||||
}
|
||||
|
||||
r, ok := roleMap[subRole]
|
||||
if ok && containsRole(r, roleId, roleMap, visited) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -55,9 +55,9 @@ type Syncer struct {
|
||||
Adapter *Adapter `xorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
func GetSyncerCount(owner, field, value string) int {
|
||||
func GetSyncerCount(owner, organization, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Syncer{})
|
||||
count, err := session.Count(&Syncer{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -75,10 +75,20 @@ func GetSyncers(owner string) []*Syncer {
|
||||
return syncers
|
||||
}
|
||||
|
||||
func GetPaginationSyncers(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Syncer {
|
||||
func GetOrganizationSyncers(owner, organization string) []*Syncer {
|
||||
syncers := []*Syncer{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&syncers, &Syncer{Owner: owner, Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return syncers
|
||||
}
|
||||
|
||||
func GetPaginationSyncers(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Syncer {
|
||||
syncers := []*Syncer{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&syncers)
|
||||
err := session.Find(&syncers, &Syncer{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -91,9 +91,9 @@ type IntrospectionResponse struct {
|
||||
Jti string `json:"jti,omitempty"`
|
||||
}
|
||||
|
||||
func GetTokenCount(owner, field, value string) int {
|
||||
func GetTokenCount(owner, organization, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Token{})
|
||||
count, err := session.Count(&Token{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -101,9 +101,9 @@ func GetTokenCount(owner, field, value string) int {
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetTokens(owner string) []*Token {
|
||||
func GetTokens(owner string, organization string) []*Token {
|
||||
tokens := []*Token{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&tokens, &Token{Owner: owner})
|
||||
err := adapter.Engine.Desc("created_time").Find(&tokens, &Token{Owner: owner, Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -111,10 +111,10 @@ func GetTokens(owner string) []*Token {
|
||||
return tokens
|
||||
}
|
||||
|
||||
func GetPaginationTokens(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Token {
|
||||
func GetPaginationTokens(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Token {
|
||||
tokens := []*Token{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&tokens)
|
||||
err := session.Find(&tokens, &Token{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -157,6 +157,7 @@ type User struct {
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
|
||||
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
|
||||
MultiFactorAuths []*MfaProps `json:"multiFactorAuths"`
|
||||
|
||||
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
||||
Properties map[string]string `json:"properties"`
|
||||
@ -401,6 +402,12 @@ func GetMaskedUser(user *User) *User {
|
||||
manageAccount.Password = "***"
|
||||
}
|
||||
}
|
||||
|
||||
if user.MultiFactorAuths != nil {
|
||||
for i, props := range user.MultiFactorAuths {
|
||||
user.MultiFactorAuths[i] = GetMaskedProps(props)
|
||||
}
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
@ -425,7 +432,7 @@ func GetLastUser(owner string) *User {
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) bool {
|
||||
func UpdateUser(id string, user *User, columns []string, isAdmin bool) bool {
|
||||
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
oldUser := getUser(owner, name)
|
||||
if oldUser == nil {
|
||||
@ -456,7 +463,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
|
||||
"signin_wrong_times", "last_signin_wrong_time",
|
||||
}
|
||||
}
|
||||
if isGlobalAdmin {
|
||||
if isAdmin {
|
||||
columns = append(columns, "name", "email", "phone", "country_code")
|
||||
}
|
||||
|
||||
@ -733,3 +740,35 @@ func (user *User) refreshAvatar() bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (user *User) IsMfaEnabled() bool {
|
||||
return len(user.MultiFactorAuths) > 0
|
||||
}
|
||||
|
||||
func (user *User) GetPreferMfa(masked bool) *MfaProps {
|
||||
if len(user.MultiFactorAuths) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if masked {
|
||||
if len(user.MultiFactorAuths) == 1 {
|
||||
return GetMaskedProps(user.MultiFactorAuths[0])
|
||||
}
|
||||
for _, v := range user.MultiFactorAuths {
|
||||
if v.IsPreferred {
|
||||
return GetMaskedProps(v)
|
||||
}
|
||||
}
|
||||
return GetMaskedProps(user.MultiFactorAuths[0])
|
||||
} else {
|
||||
if len(user.MultiFactorAuths) == 1 {
|
||||
return user.MultiFactorAuths[0]
|
||||
}
|
||||
for _, v := range user.MultiFactorAuths {
|
||||
if v.IsPreferred {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return user.MultiFactorAuths[0]
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -179,6 +180,123 @@ func ClearUserOAuthProperties(user *User, providerType string) bool {
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang string) (bool, string) {
|
||||
organization := GetOrganizationByUser(oldUser)
|
||||
var itemsChanged []*AccountItem
|
||||
|
||||
if oldUser.Owner != newUser.Owner {
|
||||
item := GetAccountItemByName("Organization", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Name != newUser.Name {
|
||||
item := GetAccountItemByName("Name", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Id != newUser.Id {
|
||||
item := GetAccountItemByName("ID", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.DisplayName != newUser.DisplayName {
|
||||
item := GetAccountItemByName("Display name", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Avatar != newUser.Avatar {
|
||||
item := GetAccountItemByName("Avatar", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Type != newUser.Type {
|
||||
item := GetAccountItemByName("User type", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
// The password is *** when not modified
|
||||
if oldUser.Password != newUser.Password && newUser.Password != "***" {
|
||||
item := GetAccountItemByName("Password", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Email != newUser.Email {
|
||||
item := GetAccountItemByName("Email", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Phone != newUser.Phone {
|
||||
item := GetAccountItemByName("Phone", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.CountryCode != newUser.CountryCode {
|
||||
item := GetAccountItemByName("Country code", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Region != newUser.Region {
|
||||
item := GetAccountItemByName("Country/Region", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Location != newUser.Location {
|
||||
item := GetAccountItemByName("Location", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Affiliation != newUser.Affiliation {
|
||||
item := GetAccountItemByName("Affiliation", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Title != newUser.Title {
|
||||
item := GetAccountItemByName("Title", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Homepage != newUser.Homepage {
|
||||
item := GetAccountItemByName("Homepage", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Bio != newUser.Bio {
|
||||
item := GetAccountItemByName("Bio", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.Tag != newUser.Tag {
|
||||
item := GetAccountItemByName("Tag", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.SignupApplication != newUser.SignupApplication {
|
||||
item := GetAccountItemByName("Signup application", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
|
||||
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
|
||||
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
|
||||
item := GetAccountItemByName("Properties", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
oldUserTwoFactorAuthJson, _ := json.Marshal(oldUser.MultiFactorAuths)
|
||||
newUserTwoFactorAuthJson, _ := json.Marshal(newUser.MultiFactorAuths)
|
||||
if string(oldUserTwoFactorAuthJson) != string(newUserTwoFactorAuthJson) {
|
||||
item := GetAccountItemByName("Multi-factor authentication", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.IsAdmin != newUser.IsAdmin {
|
||||
item := GetAccountItemByName("Is admin", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.IsGlobalAdmin != newUser.IsGlobalAdmin {
|
||||
item := GetAccountItemByName("Is global admin", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.IsForbidden != newUser.IsForbidden {
|
||||
item := GetAccountItemByName("Is forbidden", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
if oldUser.IsDeleted != newUser.IsDeleted {
|
||||
item := GetAccountItemByName("Is deleted", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
for i := range itemsChanged {
|
||||
if pass, err := CheckAccountItemModifyRule(itemsChanged[i], isAdmin, lang); !pass {
|
||||
return pass, err
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (user *User) GetCountryCode(countryCode string) string {
|
||||
if countryCode != "" {
|
||||
return countryCode
|
||||
@ -193,3 +311,11 @@ func (user *User) GetCountryCode(countryCode string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (user *User) IsAdminUser() bool {
|
||||
if user == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return user.IsAdmin || user.IsGlobalAdmin
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ type Webhook struct {
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
}
|
||||
|
||||
func GetWebhookCount(owner, field, value string) int {
|
||||
func GetWebhookCount(owner, organization, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Webhook{})
|
||||
count, err := session.Count(&Webhook{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -52,9 +52,9 @@ func GetWebhookCount(owner, field, value string) int {
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetWebhooks(owner string) []*Webhook {
|
||||
func GetWebhooks(owner string, organization string) []*Webhook {
|
||||
webhooks := []*Webhook{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&webhooks, &Webhook{Owner: owner})
|
||||
err := adapter.Engine.Desc("created_time").Find(&webhooks, &Webhook{Owner: owner, Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -62,10 +62,10 @@ func GetWebhooks(owner string) []*Webhook {
|
||||
return webhooks
|
||||
}
|
||||
|
||||
func GetPaginationWebhooks(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Webhook {
|
||||
func GetPaginationWebhooks(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Webhook {
|
||||
webhooks := []*Webhook{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&webhooks)
|
||||
err := session.Find(&webhooks, &Webhook{Organization: organization})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ func getObject(ctx *context.Context) (string, string) {
|
||||
body := ctx.Input.RequestBody
|
||||
|
||||
if len(body) == 0 {
|
||||
return "", ""
|
||||
return ctx.Request.Form.Get("owner"), ctx.Request.Form.Get("name")
|
||||
}
|
||||
|
||||
var obj Object
|
||||
|
37
routers/prometheus_filter.go
Normal file
37
routers/prometheus_filter.go
Normal file
@ -0,0 +1,37 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func recordSystemInfo(systemInfo *util.SystemInfo) {
|
||||
for i, value := range systemInfo.CpuUsage {
|
||||
object.CpuUsage.WithLabelValues(fmt.Sprintf("%d", i)).Set(value)
|
||||
}
|
||||
object.MemoryUsage.WithLabelValues("memoryUsed").Set(float64(systemInfo.MemoryUsed))
|
||||
object.MemoryUsage.WithLabelValues("memoryTotal").Set(float64(systemInfo.MemoryTotal))
|
||||
}
|
||||
|
||||
func PrometheusFilter(ctx *context.Context) {
|
||||
method := ctx.Input.Method()
|
||||
path := ctx.Input.URL()
|
||||
if strings.HasPrefix(path, "/api/metrics") {
|
||||
systemInfo, err := util.GetSystemInfo()
|
||||
if err == nil {
|
||||
recordSystemInfo(systemInfo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(path, "/api") {
|
||||
ctx.Input.SetData("startTime", time.Now())
|
||||
object.TotalThroughput.Inc()
|
||||
object.ApiThroughput.WithLabelValues(path, method).Inc()
|
||||
}
|
||||
}
|
@ -21,8 +21,8 @@ package routers
|
||||
|
||||
import (
|
||||
"github.com/beego/beego"
|
||||
|
||||
"github.com/casdoor/casdoor/controllers"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -184,6 +184,7 @@ func initAPI() {
|
||||
beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer")
|
||||
|
||||
beego.Router("/api/get-certs", &controllers.ApiController{}, "GET:GetCerts")
|
||||
beego.Router("/api/get-globle-certs", &controllers.ApiController{}, "GET:GetGlobleCerts")
|
||||
beego.Router("/api/get-cert", &controllers.ApiController{}, "GET:GetCert")
|
||||
beego.Router("/api/update-cert", &controllers.ApiController{}, "POST:UpdateCert")
|
||||
beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
|
||||
@ -197,6 +198,7 @@ func initAPI() {
|
||||
|
||||
beego.Router("/api/get-messages", &controllers.ApiController{}, "GET:GetMessages")
|
||||
beego.Router("/api/get-message", &controllers.ApiController{}, "GET:GetMessage")
|
||||
beego.Router("/api/get-message-answer", &controllers.ApiController{}, "GET:GetMessageAnswer")
|
||||
beego.Router("/api/update-message", &controllers.ApiController{}, "POST:UpdateMessage")
|
||||
beego.Router("/api/add-message", &controllers.ApiController{}, "POST:AddMessage")
|
||||
beego.Router("/api/delete-message", &controllers.ApiController{}, "POST:DeleteMessage")
|
||||
@ -237,6 +239,15 @@ func initAPI() {
|
||||
beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "Get:WebAuthnSigninBegin")
|
||||
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "Post:WebAuthnSigninFinish")
|
||||
|
||||
beego.Router("/api/mfa/setup/initiate", &controllers.ApiController{}, "POST:MfaSetupInitiate")
|
||||
beego.Router("/api/mfa/setup/verify", &controllers.ApiController{}, "POST:MfaSetupVerify")
|
||||
beego.Router("/api/mfa/setup/enable", &controllers.ApiController{}, "POST:MfaSetupEnable")
|
||||
beego.Router("/api/delete-mfa", &controllers.ApiController{}, "POST:DeleteMfa")
|
||||
beego.Router("/api/set-preferred-mfa", &controllers.ApiController{}, "POST:SetPreferredMfa")
|
||||
|
||||
beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo")
|
||||
beego.Router("/api/get-version-info", &controllers.ApiController{}, "GET:GetVersionInfo")
|
||||
beego.Router("/api/get-prometheus-info", &controllers.ApiController{}, "GET:GetPrometheusInfo")
|
||||
|
||||
beego.Handler("/api/metrics", promhttp.Handler())
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func NewMinIOS3StorageProvider(clientId string, clientSecret string, region stri
|
||||
Endpoint: endpoint,
|
||||
S3Endpoint: endpoint,
|
||||
ACL: awss3.BucketCannedACLPublicRead,
|
||||
S3ForcePathStyle: true,
|
||||
S3ForcePathStyle: false,
|
||||
})
|
||||
|
||||
return sp
|
||||
|
@ -551,15 +551,33 @@
|
||||
"operationId": "ApiController.GetCaptcha"
|
||||
}
|
||||
},
|
||||
"/api/api/get-webhook-event": {
|
||||
"/api/api/get-captcha-status": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"GetCaptchaStatus API"
|
||||
"Token API"
|
||||
],
|
||||
"operationId": "ApiController.GetCaptchaStatus"
|
||||
"description": "Get Login Error Counts",
|
||||
"operationId": "ApiController.GetCaptchaStatus",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "id",
|
||||
"description": "The id ( owner/name ) of user",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The Response object",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/api/get-captcha-status": {
|
||||
"/api/api/get-webhook-event": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"GetWebhookEventType API"
|
||||
@ -662,7 +680,7 @@
|
||||
"/api/api/verify-code": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Account API"
|
||||
"Verification API"
|
||||
],
|
||||
"operationId": "ApiController.VerifyCode"
|
||||
}
|
||||
@ -2752,7 +2770,7 @@
|
||||
"description": "Login information",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controllers.RequestForm"
|
||||
"$ref": "#/definitions/controllers.AuthForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -3891,11 +3909,11 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"2306.0xc0004a1410.false": {
|
||||
"1183.0xc000455050.false": {
|
||||
"title": "false",
|
||||
"type": "object"
|
||||
},
|
||||
"2340.0xc0004a1440.false": {
|
||||
"1217.0xc000455080.false": {
|
||||
"title": "false",
|
||||
"type": "object"
|
||||
},
|
||||
@ -3907,6 +3925,10 @@
|
||||
"title": "Response",
|
||||
"type": "object"
|
||||
},
|
||||
"controllers.AuthForm": {
|
||||
"title": "AuthForm",
|
||||
"type": "object"
|
||||
},
|
||||
"controllers.EmailForm": {
|
||||
"title": "EmailForm",
|
||||
"type": "object",
|
||||
@ -3931,108 +3953,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"controllers.RequestForm": {
|
||||
"title": "RequestForm",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"affiliation": {
|
||||
"type": "string"
|
||||
},
|
||||
"application": {
|
||||
"type": "string"
|
||||
},
|
||||
"autoSignin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"captchaToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"captchaType": {
|
||||
"type": "string"
|
||||
},
|
||||
"clientId": {
|
||||
"type": "string"
|
||||
},
|
||||
"clientSecret": {
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"countryCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"firstName": {
|
||||
"type": "string"
|
||||
},
|
||||
"idCard": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"type": "string"
|
||||
},
|
||||
"method": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organization": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"redirectUri": {
|
||||
"type": "string"
|
||||
},
|
||||
"region": {
|
||||
"type": "string"
|
||||
},
|
||||
"relayState": {
|
||||
"type": "string"
|
||||
},
|
||||
"samlRequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"samlResponse": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"controllers.Response": {
|
||||
"title": "Response",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/2306.0xc0004a1410.false"
|
||||
"$ref": "#/definitions/1183.0xc000455050.false"
|
||||
},
|
||||
"data2": {
|
||||
"$ref": "#/definitions/2340.0xc0004a1440.false"
|
||||
"$ref": "#/definitions/1217.0xc000455080.false"
|
||||
},
|
||||
"msg": {
|
||||
"type": "string"
|
||||
|
@ -355,16 +355,28 @@ paths:
|
||||
tags:
|
||||
- Login API
|
||||
operationId: ApiController.GetCaptcha
|
||||
/api/api/get-captcha-status:
|
||||
get:
|
||||
tags:
|
||||
- Token API
|
||||
description: Get Login Error Counts
|
||||
operationId: ApiController.GetCaptchaStatus
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: The id ( owner/name ) of user
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: The Response object
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.Response'
|
||||
/api/api/get-webhook-event:
|
||||
get:
|
||||
tags:
|
||||
- GetWebhookEventType API
|
||||
operationId: ApiController.GetWebhookEventType
|
||||
/api/api/get-captcha-status:
|
||||
get:
|
||||
tags:
|
||||
- GetCaptchaStatus API
|
||||
operationId: ApiController.GetCaptchaStatus
|
||||
/api/api/reset-email-or-phone:
|
||||
post:
|
||||
tags:
|
||||
@ -429,7 +441,7 @@ paths:
|
||||
/api/api/verify-code:
|
||||
post:
|
||||
tags:
|
||||
- Account API
|
||||
- Verification API
|
||||
operationId: ApiController.VerifyCode
|
||||
/api/api/webhook:
|
||||
post:
|
||||
@ -1798,7 +1810,7 @@ paths:
|
||||
description: Login information
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.RequestForm'
|
||||
$ref: '#/definitions/controllers.AuthForm'
|
||||
responses:
|
||||
"200":
|
||||
description: The Response object
|
||||
@ -2543,10 +2555,10 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/Response'
|
||||
definitions:
|
||||
2306.0xc0004a1410.false:
|
||||
1183.0xc000455050.false:
|
||||
title: "false"
|
||||
type: object
|
||||
2340.0xc0004a1440.false:
|
||||
1217.0xc000455080.false:
|
||||
title: "false"
|
||||
type: object
|
||||
LaravelResponse:
|
||||
@ -2555,6 +2567,9 @@ definitions:
|
||||
Response:
|
||||
title: Response
|
||||
type: object
|
||||
controllers.AuthForm:
|
||||
title: AuthForm
|
||||
type: object
|
||||
controllers.EmailForm:
|
||||
title: EmailForm
|
||||
type: object
|
||||
@ -2571,76 +2586,14 @@ definitions:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
controllers.RequestForm:
|
||||
title: RequestForm
|
||||
type: object
|
||||
properties:
|
||||
affiliation:
|
||||
type: string
|
||||
application:
|
||||
type: string
|
||||
autoSignin:
|
||||
type: boolean
|
||||
captchaToken:
|
||||
type: string
|
||||
captchaType:
|
||||
type: string
|
||||
clientId:
|
||||
type: string
|
||||
clientSecret:
|
||||
type: string
|
||||
code:
|
||||
type: string
|
||||
countryCode:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
emailCode:
|
||||
type: string
|
||||
firstName:
|
||||
type: string
|
||||
idCard:
|
||||
type: string
|
||||
lastName:
|
||||
type: string
|
||||
method:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
organization:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
phone:
|
||||
type: string
|
||||
phoneCode:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
redirectUri:
|
||||
type: string
|
||||
region:
|
||||
type: string
|
||||
relayState:
|
||||
type: string
|
||||
samlRequest:
|
||||
type: string
|
||||
samlResponse:
|
||||
type: string
|
||||
state:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
controllers.Response:
|
||||
title: Response
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/definitions/2306.0xc0004a1410.false'
|
||||
$ref: '#/definitions/1183.0xc000455050.false'
|
||||
data2:
|
||||
$ref: '#/definitions/2340.0xc0004a1440.false'
|
||||
$ref: '#/definitions/1217.0xc000455080.false'
|
||||
msg:
|
||||
type: string
|
||||
name:
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -141,6 +142,16 @@ func GenerateSimpleTimeId() string {
|
||||
return t
|
||||
}
|
||||
|
||||
func GetRandomName() string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
const charset = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
result := make([]byte, 6)
|
||||
for i := range result {
|
||||
result[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func GetId(owner, name string) string {
|
||||
return fmt.Sprintf("%s/%s", owner, name)
|
||||
}
|
||||
|
14
util/time.go
14
util/time.go
@ -25,6 +25,20 @@ func GetCurrentTime() string {
|
||||
return tm.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func GetCurrentTimeEx(timestamp string) string {
|
||||
tm := time.Now()
|
||||
inputTime, err := time.Parse(time.RFC3339, timestamp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if !tm.After(inputTime) {
|
||||
tm = inputTime.Add(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
return tm.Format("2006-01-02T15:04:05.999Z07:00")
|
||||
}
|
||||
|
||||
func GetCurrentUnixTime() string {
|
||||
return strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"regexp"
|
||||
|
||||
@ -48,3 +49,24 @@ func GetE164Number(phone string, countryCode string) (string, bool) {
|
||||
phoneNumber, _ := phonenumbers.Parse(phone, countryCode)
|
||||
return phonenumbers.Format(phoneNumber, phonenumbers.E164), phonenumbers.IsValidNumber(phoneNumber)
|
||||
}
|
||||
|
||||
func GetCountryCode(prefix string, phone string) (string, error) {
|
||||
if prefix == "" || phone == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
phoneNumber, err := phonenumbers.Parse(fmt.Sprintf("+%s%s", prefix, phone), "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
countryCode := phonenumbers.GetRegionCodeForNumber(phoneNumber)
|
||||
if countryCode == "" {
|
||||
return "", fmt.Errorf("country code not found for phone prefix: %s", prefix)
|
||||
}
|
||||
|
||||
return countryCode, nil
|
||||
}
|
||||
|
@ -82,6 +82,12 @@
|
||||
}
|
||||
],
|
||||
"no-multi-spaces": ["error", { "ignoreEOLComments": true }],
|
||||
"react/no-unknown-property": [
|
||||
"error",
|
||||
{
|
||||
"ignore": ["css"]
|
||||
}
|
||||
],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
@ -98,7 +104,6 @@
|
||||
"no-console": "error",
|
||||
"eqeqeq": "error",
|
||||
"keyword-spacing": "error",
|
||||
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
|
@ -1,16 +1,14 @@
|
||||
describe('Test adapter', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test adapter", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/adapters");
|
||||
cy.url().should("eq", "http://localhost:7001/adapters");
|
||||
cy.get(selector.add).click();
|
||||
cy.get(selector.add,{timeout:10000}).click();
|
||||
cy.url().should("include","http://localhost:7001/adapters/built-in/")
|
||||
});
|
||||
})
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test aplication', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test aplication", () => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test certs', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test certs", () => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test models', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test org", () => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test Orgnazition', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test org", () => {
|
||||
|
@ -1,16 +1,14 @@
|
||||
describe('Test payments', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test payments", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/payments");
|
||||
cy.url().should("eq", "http://localhost:7001/payments");
|
||||
cy.get(selector.add).click();
|
||||
cy.get(selector.add,{timeout:10000}).click();
|
||||
cy.url().should("include","http://localhost:7001/payments/")
|
||||
});
|
||||
})
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test permissions', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test permissions", () => {
|
||||
|
@ -1,16 +1,14 @@
|
||||
describe('Test products', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn > span"
|
||||
};
|
||||
it("test products", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/products");
|
||||
cy.url().should("eq", "http://localhost:7001/products");
|
||||
cy.get(selector.add).click();
|
||||
cy.get(selector.add,{timeout:10000}).click();
|
||||
cy.url().should("include","http://localhost:7001/products/")
|
||||
});
|
||||
})
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test providers', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test providers", () => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test records', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test records", () => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test resource', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test resource", () => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test roles', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test role", () => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test sessions', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test sessions", () => {
|
||||
|
@ -1,16 +1,14 @@
|
||||
describe('Test syncers', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test syncers", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/syncers");
|
||||
cy.url().should("eq", "http://localhost:7001/syncers");
|
||||
cy.get(selector.add).click();
|
||||
cy.get(selector.add,{timeout:10000}).click();
|
||||
cy.url().should("include","http://localhost:7001/syncers/")
|
||||
});
|
||||
})
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test sysinfo', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test sysinfo", () => {
|
||||
|
@ -1,16 +1,14 @@
|
||||
describe('Test tokens', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test records", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/tokens");
|
||||
cy.url().should("eq", "http://localhost:7001/tokens");
|
||||
cy.get(selector.add).click();
|
||||
cy.get(selector.add,{timeout:10000}).click();
|
||||
cy.url().should("include","http://localhost:7001/tokens/")
|
||||
});
|
||||
})
|
||||
|
@ -1,6 +1,5 @@
|
||||
describe('Test User', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test user", () => {
|
||||
|
@ -7,10 +7,10 @@ describe('Test webhooks', () => {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test webhooks", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
|
||||
cy.visit("http://localhost:7001/webhooks");
|
||||
cy.url().should("eq", "http://localhost:7001/webhooks");
|
||||
cy.get(selector.add).click();
|
||||
cy.url().should("include","http://localhost:7001/webhooks/")
|
||||
cy.get(selector.add,{timeout:10000}).click();
|
||||
cy.url().should("include","http://localhost:7001/webhooks/");
|
||||
});
|
||||
})
|
||||
|
@ -23,19 +23,15 @@
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
const selector = {
|
||||
username: "#input",
|
||||
password: "#normal_login_password",
|
||||
loginButton: ".ant-btn",
|
||||
};
|
||||
Cypress.Commands.add('login', ()=>{
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: "http://localhost:7001/api/login",
|
||||
body: {
|
||||
"application": "app-built-in",
|
||||
"organization": "built-in",
|
||||
"username": "admin",
|
||||
"password": "123",
|
||||
"autoSignin": true,
|
||||
"type": "login",
|
||||
},
|
||||
}).then((Response) => {
|
||||
expect(Response).property("body").property("status").to.equal("ok");
|
||||
});
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.get(selector.username).type("admin");
|
||||
cy.get(selector.password).type("123");
|
||||
cy.get(selector.loginButton).click();
|
||||
cy.url().should("eq", "http://localhost:7001/");
|
||||
})
|
||||
|
@ -24,6 +24,7 @@
|
||||
"i18next": "^19.8.9",
|
||||
"libphonenumber-js": "^1.10.19",
|
||||
"moment": "^2.29.1",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"qs": "^6.10.2",
|
||||
"react": "^18.2.0",
|
||||
"react-app-polyfill": "^3.0.0",
|
||||
|
@ -47,7 +47,7 @@ class AdapterEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getAdapter() {
|
||||
AdapterBackend.getAdapter(this.state.owner, this.state.adapterName)
|
||||
AdapterBackend.getAdapter("admin", this.state.adapterName)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
@ -60,7 +60,7 @@ class AdapterEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getOrganizations() {
|
||||
OrganizationBackend.getOrganizations("admin")
|
||||
OrganizationBackend.getOrganizations(this.state.organizationName)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
organizations: (res.msg === undefined) ? res : [],
|
||||
@ -109,7 +109,7 @@ class AdapterEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.adapter.organization} onChange={(value => {
|
||||
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.adapter.organization} onChange={(value => {
|
||||
this.getModels(value);
|
||||
this.updateAdapterField("organization", value);
|
||||
this.updateAdapterField("owner", value);
|
||||
|
@ -20,16 +20,16 @@ import * as Setting from "./Setting";
|
||||
import * as AdapterBackend from "./backend/AdapterBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
|
||||
class AdapterListPage extends BaseListPage {
|
||||
newAdapter() {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: "built-in",
|
||||
owner: "admin",
|
||||
name: `adapter_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
organization: "built-in",
|
||||
organization: this.props.account.owner,
|
||||
type: "Database",
|
||||
host: "localhost",
|
||||
port: 3306,
|
||||
@ -247,7 +247,7 @@ class AdapterListPage extends BaseListPage {
|
||||
value = params.type;
|
||||
}
|
||||
this.setState({loading: true});
|
||||
AdapterBackend.getAdapters("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
AdapterBackend.getAdapters("admin", Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
|
@ -62,7 +62,6 @@ import * as Conf from "./Conf";
|
||||
|
||||
import * as Auth from "./auth/Auth";
|
||||
import EntryPage from "./EntryPage";
|
||||
import ResultPage from "./auth/ResultPage";
|
||||
import * as AuthBackend from "./auth/AuthBackend";
|
||||
import AuthCallback from "./auth/AuthCallback";
|
||||
import LanguageSelect from "./common/select/LanguageSelect";
|
||||
@ -77,6 +76,7 @@ import AdapterEditPage from "./AdapterEditPage";
|
||||
import {withTranslation} from "react-i18next";
|
||||
import ThemeSelect from "./common/select/ThemeSelect";
|
||||
import SessionListPage from "./SessionListPage";
|
||||
import MfaSetupPage from "./auth/MfaSetupPage";
|
||||
|
||||
const {Header, Footer, Content} = Layout;
|
||||
|
||||
@ -410,7 +410,7 @@ class App extends Component {
|
||||
));
|
||||
}
|
||||
|
||||
if (Setting.isAdminUser(this.state.account)) {
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>,
|
||||
"/models"
|
||||
));
|
||||
@ -446,7 +446,7 @@ class App extends Component {
|
||||
));
|
||||
}
|
||||
|
||||
if (Setting.isAdminUser(this.state.account)) {
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>,
|
||||
"/tokens"
|
||||
));
|
||||
@ -475,11 +475,13 @@ class App extends Component {
|
||||
res.push(Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>,
|
||||
"/payments"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>,
|
||||
"/sysinfo"
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
if (Setting.isAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>,
|
||||
"/sysinfo"
|
||||
));
|
||||
res.push(Setting.getItem(<a target="_blank" rel="noreferrer"
|
||||
href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>,
|
||||
"/swagger"
|
||||
@ -517,8 +519,6 @@ class App extends Component {
|
||||
renderRouter() {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...props} />)} />
|
||||
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...props} />)} />
|
||||
<Route exact path="/" render={(props) => this.renderLoginIfNotLoggedIn(<HomePage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/account" render={(props) => this.renderLoginIfNotLoggedIn(<AccountPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
|
||||
@ -563,6 +563,7 @@ class App extends Component {
|
||||
<Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/mfa-authentication/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
|
||||
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
|
||||
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
|
||||
@ -665,6 +666,7 @@ class App extends Component {
|
||||
window.location.pathname.startsWith("/login") ||
|
||||
window.location.pathname.startsWith("/forget") ||
|
||||
window.location.pathname.startsWith("/prompt") ||
|
||||
window.location.pathname.startsWith("/result") ||
|
||||
window.location.pathname.startsWith("/cas") ||
|
||||
window.location.pathname.startsWith("/auto-signup");
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ class ApplicationEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getCerts() {
|
||||
CertBackend.getCerts("admin")
|
||||
CertBackend.getCerts(this.props.account.owner)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
certs: (res.msg === undefined) ? res : [],
|
||||
@ -767,7 +767,15 @@ class ApplicationEditPage extends React.Component {
|
||||
renderSignupSigninPreview() {
|
||||
const themeData = this.state.application.themeData ?? Conf.ThemeDefault;
|
||||
let signUpUrl = `/signup/${this.state.application.name}`;
|
||||
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`;
|
||||
|
||||
let redirectUri;
|
||||
if (this.state.application.redirectUris.length !== 0) {
|
||||
redirectUri = this.state.application.redirectUris[0];
|
||||
} else {
|
||||
redirectUri = "\"ERROR: You must specify at least one Redirect URL in 'Redirect URLs'\"";
|
||||
}
|
||||
|
||||
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
|
||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||
if (!this.state.application.enablePassword) {
|
||||
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
|
||||
|
@ -21,7 +21,7 @@ import * as Setting from "./Setting";
|
||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
|
||||
class ApplicationListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
@ -273,7 +273,7 @@ class ApplicationListPage extends BaseListPage {
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
this.setState({loading: true});
|
||||
(Setting.isAdminUser(this.props.account) ? ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) :
|
||||
ApplicationBackend.getApplicationsByOrganization("admin", this.state.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||
ApplicationBackend.getApplicationsByOrganization("admin", this.props.account.organization.name, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
|
@ -30,6 +30,7 @@ class CertEditPage extends React.Component {
|
||||
classes: props,
|
||||
certName: props.match.params.certName,
|
||||
cert: null,
|
||||
organizations: [],
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
};
|
||||
}
|
||||
@ -39,7 +40,7 @@ class CertEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getCert() {
|
||||
CertBackend.getCert("admin", this.state.certName)
|
||||
CertBackend.getCert(this.props.account.owner, this.state.certName)
|
||||
.then((cert) => {
|
||||
this.setState({
|
||||
cert: cert,
|
||||
@ -75,6 +76,19 @@ class CertEditPage extends React.Component {
|
||||
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteCert()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||
</div>
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.cert.owner} onChange={(value => {this.updateCertField("owner", value);})}>
|
||||
{Setting.isAdminUser(this.props.account) ? <Option key={"admin"} value={"admin"}>{i18next.t("cert:admin (Shared)")}</Option> : null}
|
||||
{
|
||||
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
|
||||
|
@ -20,13 +20,13 @@ import * as Setting from "./Setting";
|
||||
import * as CertBackend from "./backend/CertBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
|
||||
class CertListPage extends BaseListPage {
|
||||
newCert() {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: "admin", // this.props.account.certname,
|
||||
owner: this.props.account.owner, // this.props.account.certname,
|
||||
name: `cert_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Cert - ${randomName}`,
|
||||
@ -92,6 +92,14 @@ class CertListPage extends BaseListPage {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Organization"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
width: "150px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("organization"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Created time"),
|
||||
dataIndex: "createdTime",
|
||||
@ -168,8 +176,9 @@ class CertListPage extends BaseListPage {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/certs/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/certs/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<PopconfirmModal
|
||||
disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)}
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteCert(index)}
|
||||
>
|
||||
@ -214,7 +223,8 @@ class CertListPage extends BaseListPage {
|
||||
value = params.type;
|
||||
}
|
||||
this.setState({loading: true});
|
||||
CertBackend.getCerts("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
(Setting.isAdminUser(this.props.account) ? CertBackend.getGlobleCerts(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
: CertBackend.getCerts(this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user