Compare commits

...

11 Commits

Author SHA1 Message Date
Yang Luo
b158b840bd Add "new-user" to webhook event list 2024-03-27 15:23:06 +08:00
Yang Luo
b16f1807b3 fix: fix bug in "new-user" record 2024-03-27 15:15:40 +08:00
Yang Luo
d0cce1bf7a Order by "id" in GetPaginationRecords() 2024-03-27 15:14:41 +08:00
Yang Luo
9892cd20ab Improve erorr message in CheckVerificationCode() 2024-03-27 15:14:20 +08:00
Yang Luo
d1f31dd327 feat: fix linter 2024-03-26 23:24:53 +08:00
Yang Luo
94743246a1 Improve "%{user.friendlyName}" handling 2024-03-25 21:26:36 +08:00
Yang Luo
39ad1bc593 Add signup's object in AfterRecordMessage() 2024-03-25 21:20:33 +08:00
Will.Feng
d97f833d2a feat: Add 'owner' and 'name' Parameters to /api/get-captcha Response for /api/verify-captcha Usage (#2834) 2024-03-25 16:34:42 +08:00
Yang Luo
948fa911e2 feat: add users to getGroups() and getGroup() APIs 2024-03-22 23:32:30 +08:00
Yang Luo
6073a0f63d Rename GroupListPage and GroupEditPage 2024-03-22 23:14:05 +08:00
Yang Luo
91268bca70 Improve enableAutoSignin option UI 2024-03-22 22:55:10 +08:00
13 changed files with 187 additions and 27 deletions

View File

@@ -44,6 +44,8 @@ type Response struct {
}
type Captcha struct {
Owner string `json:"owner"`
Name string `json:"name"`
Type string `json:"type"`
AppKey string `json:"appKey"`
Scene string `json:"scene"`
@@ -271,7 +273,8 @@ func (c *ApiController) Signup() {
return
}
c.Ctx.Input.SetParam("recordUserId", fmt.Sprintf("%s/%s", application.Organization, user.Name))
c.Ctx.Input.SetParam("recordUserId", user.GetId())
c.Ctx.Input.SetParam("recordSignup", "true")
userId := user.GetId()
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
@@ -529,10 +532,12 @@ func (c *ApiController) GetCaptcha() {
return
}
c.ResponseOk(Captcha{Type: captchaProvider.Type, CaptchaId: id, CaptchaImage: img})
c.ResponseOk(Captcha{Owner: captchaProvider.Owner, Name: captchaProvider.Name, Type: captchaProvider.Type, CaptchaId: id, CaptchaImage: img})
return
} else if captchaProvider.Type != "" {
c.ResponseOk(Captcha{
Owner: captchaProvider.Owner,
Name: captchaProvider.Name,
Type: captchaProvider.Type,
SubType: captchaProvider.SubType,
ClientId: captchaProvider.ClientId,

View File

@@ -43,13 +43,20 @@ func (c *ApiController) GetGroups() {
if err != nil {
c.ResponseError(err.Error())
return
} else {
if withTree == "true" {
c.ResponseOk(object.ConvertToTreeData(groups, owner))
return
}
c.ResponseOk(groups)
}
err = object.ExtendGroupsWithUsers(groups)
if err != nil {
c.ResponseError(err.Error())
return
}
if withTree == "true" {
c.ResponseOk(object.ConvertToTreeData(groups, owner))
return
}
c.ResponseOk(groups)
} else {
limit := util.ParseInt(limit)
count, err := object.GetGroupCount(owner, field, value)
@@ -64,6 +71,12 @@ func (c *ApiController) GetGroups() {
c.ResponseError(err.Error())
return
} else {
err = object.ExtendGroupsWithUsers(groups)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(groups, paginator.Nums())
}
}
@@ -84,6 +97,13 @@ func (c *ApiController) GetGroup() {
c.ResponseError(err.Error())
return
}
err = object.ExtendGroupWithUsers(group)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(group)
}

View File

@@ -125,9 +125,11 @@ func (c *ApiController) SendEmail() {
return
}
userString := "Hi"
if user != nil {
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
userString = user.GetFriendlyName()
}
content = strings.Replace(content, "%{user.friendlyName}", userString, 1)
}
}

View File

@@ -17,6 +17,7 @@ package object
import (
"errors"
"fmt"
"sync"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
@@ -30,13 +31,13 @@ type Group struct {
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Manager string `xorm:"varchar(100)" json:"manager"`
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
Type string `xorm:"varchar(100)" json:"type"`
ParentId string `xorm:"varchar(100)" json:"parentId"`
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
Users []*User `xorm:"-" json:"users"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Manager string `xorm:"varchar(100)" json:"manager"`
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
Type string `xorm:"varchar(100)" json:"type"`
ParentId string `xorm:"varchar(100)" json:"parentId"`
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
Users []string `xorm:"-" json:"users"`
Title string `json:"title,omitempty"`
Key string `json:"key,omitempty"`
@@ -288,6 +289,55 @@ func GetGroupUsers(groupId string) ([]*User, error) {
return users, nil
}
func ExtendGroupWithUsers(group *Group) error {
if group == nil {
return nil
}
users, err := GetUsers(group.Owner)
if err != nil {
return err
}
groupId := group.GetId()
userIds := []string{}
for _, user := range users {
if util.InSlice(user.Groups, groupId) {
userIds = append(userIds, user.GetId())
}
}
group.Users = userIds
return nil
}
func ExtendGroupsWithUsers(groups []*Group) error {
var wg sync.WaitGroup
errChan := make(chan error, len(groups))
for _, group := range groups {
wg.Add(1)
go func(group *Group) {
defer wg.Done()
err := ExtendGroupWithUsers(group)
if err != nil {
errChan <- err
}
}(group)
}
wg.Wait()
close(errChan)
for err := range errChan {
if err != nil {
return err
}
}
return nil
}
func GroupChangeTrigger(oldName, newName string) error {
session := ormer.Engine.NewSession()
defer session.Close()

View File

@@ -140,6 +140,12 @@ func GetRecords() ([]*casvisorsdk.Record, error) {
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
records := []*casvisorsdk.Record{}
if sortField == "" || sortOrder == "" {
sortField = "id"
sortOrder = "descend"
}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&records, filterRecord)
if err != nil {
@@ -159,6 +165,25 @@ func GetRecordsByField(record *casvisorsdk.Record) ([]*casvisorsdk.Record, error
return records, nil
}
func CopyRecord(record *casvisorsdk.Record) *casvisorsdk.Record {
res := &casvisorsdk.Record{
Owner: record.Owner,
Name: record.Name,
CreatedTime: record.CreatedTime,
Organization: record.Organization,
ClientIp: record.ClientIp,
User: record.User,
Method: record.Method,
RequestUri: record.RequestUri,
Action: record.Action,
Language: record.Language,
Object: record.Object,
Response: record.Response,
IsTriggered: record.IsTriggered,
}
return res
}
func getFilteredWebhooks(webhooks []*Webhook, organization string, action string) []*Webhook {
res := []*Webhook{}
for _, webhook := range webhooks {

View File

@@ -92,9 +92,12 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
content := strings.Replace(provider.Content, "%s", code, 1)
userString := "Hi"
if user != nil {
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
userString = user.GetFriendlyName()
}
content = strings.Replace(content, "%{user.friendlyName}", userString, 1)
err := IsAllowSend(user, remoteAddr, provider.Category)
if err != nil {
@@ -187,14 +190,17 @@ func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet, or has already been used!")}, nil
}
timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
timeoutInMinutes, err := conf.GetConfigInt64("verificationCodeTimeout")
if err != nil {
return nil, err
}
now := time.Now().Unix()
if now-record.Time > timeout*60 {
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)}, nil
if now-record.Time > timeoutInMinutes*60*10 {
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet!")}, nil
}
if now-record.Time > timeoutInMinutes*60 {
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeoutInMinutes)}, nil
}
if record.Code != code {

View File

@@ -15,9 +15,12 @@
package routers
import (
"fmt"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
)
func getUser(ctx *context.Context) (username string) {
@@ -68,6 +71,7 @@ func RecordMessage(ctx *context.Context) {
func AfterRecordMessage(ctx *context.Context) {
record, err := object.NewRecord(ctx)
if err != nil {
fmt.Printf("AfterRecordMessage() error: %s\n", err.Error())
return
}
@@ -76,14 +80,32 @@ func AfterRecordMessage(ctx *context.Context) {
record.Organization, record.User = util.GetOwnerAndNameFromId(userId)
}
var record2 *casvisorsdk.Record
recordSignup := ctx.Input.Params()["recordSignup"]
if recordSignup == "true" {
record2 := *record
record2.Action = "signup"
util.SafeGoroutine(func() { object.AddRecord(&record2) })
record2 = object.CopyRecord(record)
record2.Action = "new-user"
var user *object.User
user, err = object.GetUser(userId)
if err != nil {
fmt.Printf("AfterRecordMessage() error: %s\n", err.Error())
return
}
if user == nil {
err = fmt.Errorf("the user: %s is not found", userId)
fmt.Printf("AfterRecordMessage() error: %s\n", err.Error())
return
}
record2.Object = util.StructToJson(user)
}
util.SafeGoroutine(func() {
object.AddRecord(record)
if record2 != nil {
object.AddRecord(record2)
}
})
}

View File

@@ -456,6 +456,10 @@ class ApplicationEditPage extends React.Component {
</Col>
<Col span={1} >
<Switch checked={this.state.application.enableSigninSession} onChange={checked => {
if (!checked) {
this.updateApplicationField("enableAutoSignin", false);
}
this.updateApplicationField("enableSigninSession", checked);
}} />
</Col>
@@ -466,6 +470,11 @@ class ApplicationEditPage extends React.Component {
</Col>
<Col span={1} >
<Switch checked={this.state.application.enableAutoSignin} onChange={checked => {
if (!this.state.application.enableSigninSession && checked) {
Setting.showMessage("error", i18next.t("application:Please enable \"Signin session\" first before enabling \"Auto signin\""));
return;
}
this.updateApplicationField("enableAutoSignin", checked);
}} />
</Col>

View File

@@ -177,6 +177,16 @@ class GroupEditPage extends React.Component {
)} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Users"), i18next.t("general:Users - Tooltip"))} :
</Col>
<Col style={{marginTop: "5px"}} span={22} >
{
Setting.getTags(this.state.group.users, "users")
}
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :

View File

@@ -195,6 +195,17 @@ class GroupListPage extends BaseListPage {
</Link>;
},
},
{
title: i18next.t("general:Users"),
dataIndex: "users",
key: "users",
// width: "200px",
sorter: true,
...this.getColumnSearchProps("users"),
render: (text, record, index) => {
return Setting.getTags(text, "users");
},
},
{
title: i18next.t("general:Action"),
dataIndex: "",

View File

@@ -34,8 +34,8 @@ import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
import UserListPage from "./UserListPage";
import GroupTreePage from "./GroupTreePage";
import GroupListPage from "./GroupList";
import GroupEditPage from "./GroupEdit";
import GroupListPage from "./GroupListPage";
import GroupEditPage from "./GroupEditPage";
import UserEditPage from "./UserEditPage";
import InvitationListPage from "./InvitationListPage";
import InvitationEditPage from "./InvitationEditPage";

View File

@@ -187,7 +187,7 @@ class RecordListPage extends BaseListPage {
sorter: true,
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
if (!["signup", "login", "logout", "update-user"].includes(record.action)) {
if (!["signup", "login", "logout", "update-user", "new-user"].includes(record.action)) {
return null;
}

View File

@@ -275,7 +275,7 @@ class WebhookEditPage extends React.Component {
}} >
{
(
["signup", "login", "logout"].concat(this.getApiPaths()).map((option, index) => {
["signup", "login", "logout", "new-user"].concat(this.getApiPaths()).map((option, index) => {
return (
<Option key={option} value={option}>{option}</Option>
);