Compare commits

..

1 Commits

Author SHA1 Message Date
hsluoyz
138ca9d0c9 Revert "fix: fix URL path in MinIO storage provider(#1818)"
This reverts commit 3699177837.
2023-06-18 22:46:43 +08:00
199 changed files with 3570 additions and 7348 deletions

View File

@@ -37,8 +37,8 @@
<a href="https://crowdin.com/project/casdoor-site">
<img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg">
</a>
<a href="https://discord.gg/5rPsrAzK7S">
<img alt="Discord" src="https://img.shields.io/discord/1022748306096537660?style=flat-square&logo=discord&label=discord&color=5865F2">
<a href="https://gitter.im/casbin/casdoor">
<img alt="Gitter" src="https://badges.gitter.im/casbin/casdoor.svg">
</a>
</p>
@@ -71,7 +71,7 @@ https://casdoor.org/docs/category/integrations
## How to contact?
- Discord: https://discord.gg/5rPsrAzK7S
- Gitter: https://gitter.im/casbin/casdoor
- Forum: https://forum.casbin.com
- Contact: https://tawk.to/chat/623352fea34c2456412b8c51/1fuc7od6e

View File

@@ -129,7 +129,7 @@ func QueryAnswerStream(authToken string, question string, writer io.Writer, buil
fmt.Printf("%s", data)
// Write the streamed data as Server-Sent Events
if _, err = fmt.Fprintf(writer, "event: message\ndata: %s\n\n", data); err != nil {
if _, err = fmt.Fprintf(writer, "data: %s\n\n", data); err != nil {
return err
}
flusher.Flush()

View File

@@ -110,7 +110,7 @@ func GetLanguage(language string) string {
return "en"
}
if len(language) != 2 || language == "nu" {
if len(language) != 2 {
return "en"
} else {
return language

View File

@@ -368,11 +368,8 @@ func (c *ApiController) GetAccount() {
return
}
if user != nil {
user.Permissions = object.GetMaskedPermissions(user.Permissions)
user.Roles = object.GetMaskedRoles(user.Roles)
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
}
user.Permissions = object.GetMaskedPermissions(user.Permissions)
user.Roles = object.GetMaskedRoles(user.Roles)
organization, err := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
if err != nil {
@@ -380,8 +377,7 @@ func (c *ApiController) GetAccount() {
return
}
isAdminOrSelf := c.IsAdminOrSelf(user)
u, err := object.GetMaskedUser(user, isAdminOrSelf)
u, err := object.GetMaskedUser(user)
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -48,24 +48,24 @@ func (c *ApiController) GetApplications() {
} else {
applications, err = object.GetOrganizationApplications(owner, organization)
}
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedApplications(applications, userId))
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetApplicationCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
app, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
applications := object.GetMaskedApplications(app, userId)
@@ -83,14 +83,13 @@ func (c *ApiController) GetApplications() {
func (c *ApiController) GetApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
app, err := object.GetApplication(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedApplication(app, userId))
c.Data["json"] = object.GetMaskedApplication(app, userId)
c.ServeJSON()
}
// GetUserApplication
@@ -103,24 +102,23 @@ func (c *ApiController) GetApplication() {
func (c *ApiController) GetUserApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
user, err := object.GetUser(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), id))
return
}
application, err := object.GetApplicationByUser(user)
app, err := object.GetApplicationByUser(user)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedApplication(application, userId))
c.Data["json"] = object.GetMaskedApplication(app, userId)
c.ServeJSON()
}
// GetOrganizationApplications
@@ -149,11 +147,11 @@ func (c *ApiController) GetOrganizationApplications() {
if limit == "" || page == "" {
applications, err := object.GetOrganizationApplications(owner, organization)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedApplications(applications, userId))
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
} else {
limit := util.ParseInt(limit)

View File

@@ -69,13 +69,10 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return
}
// check user's tag
if !user.IsGlobalAdmin && !user.IsAdmin && len(application.Tags) > 0 {
// only users with the tag that is listed in the application tags can login
if !util.InSlice(application.Tags, user.Tag) {
c.ResponseError(fmt.Sprintf(c.T("auth:User's tag: %s is not listed in the application's tags"), user.Tag))
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 {
@@ -123,11 +120,6 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return
}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}}
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in
c.SetSessionUsername(userId)
}
} else if form.Type == ResponseTypeCas {
// not oauth but CAS SSO protocol
service := c.Input().Get("service")
@@ -140,11 +132,11 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
resp.Data = st
}
}
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in
c.SetSessionUsername(userId)
}
} else {
resp = wrapErrorResponse(fmt.Errorf("unknown response type: %s", form.Type))
}
@@ -246,7 +238,7 @@ func isProxyProviderType(providerType string) bool {
// @Param code_challenge_method query string false code_challenge_method
// @Param code_challenge query string false code_challenge
// @Param form body controllers.AuthForm true "Login information"
// @Success 200 {object} controllers.Response The Response object
// @Success 200 {object} Response The Response object
// @router /login [post]
func (c *ApiController) Login() {
resp := &Response{}
@@ -352,26 +344,17 @@ func (c *ApiController) Login() {
return
}
resp = c.HandleLoggedIn(application, user, &authForm)
organization, err := object.GetOrganizationByUser(user)
if err != nil {
c.ResponseError(err.Error())
}
if object.IsNeedPromptMfa(organization, user) {
// The prompt page needs the user to be signed in
c.SetSessionUsername(user.GetId())
c.ResponseOk(object.RequiredMfa)
return
if user != nil && organization.HasRequiredMfa() && !user.IsMfaEnabled() {
resp.Msg = object.RequiredMfa
}
if user.IsMfaEnabled() {
c.setMfaUserSession(user.GetId())
c.ResponseOk(object.NextMfa, user.GetPreferredMfaProps(true))
return
}
resp = c.HandleLoggedIn(application, user, &authForm)
record := object.NewRecord(c.Ctx)
record.Organization = application.Organization
record.User = user.Name
@@ -422,10 +405,17 @@ func (c *ApiController) Login() {
c.ResponseError(err.Error())
return
}
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
} else if provider.Category == "OAuth" {
// OAuth
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
idProvider := idp.GetIdProvider(idpInfo, authForm.RedirectUri)
clientId := provider.ClientId
clientSecret := provider.ClientSecret
if provider.Type == "WeChat" && strings.Contains(c.Ctx.Request.UserAgent(), "MicroMessenger") {
clientId = provider.ClientId2
clientSecret = provider.ClientSecret2
}
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
@@ -465,7 +455,7 @@ func (c *ApiController) Login() {
c.ResponseError(err.Error())
return
}
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
} else if provider.Category == "OAuth" {
user, err = object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
if err != nil {
c.ResponseError(err.Error())
@@ -486,7 +476,7 @@ func (c *ApiController) Login() {
record.Organization = application.Organization
record.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record) })
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
} else if provider.Category == "OAuth" {
// Sign up via OAuth
if application.EnableLinkWithEmail {
if userInfo.Email != "" {
@@ -657,38 +647,28 @@ func (c *ApiController) Login() {
resp = &Response{Status: "error", Msg: "Failed to link user account", Data: isLinked}
}
}
} else if c.getMfaUserSession() != "" {
user, err := object.GetUser(c.getMfaUserSession())
} else if c.getMfaSessionData() != nil {
mfaSession := c.getMfaSessionData()
user, err := object.GetUser(mfaSession.UserId)
if err != nil {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError("expired user session")
return
}
if authForm.Passcode != "" {
mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferredMfaProps(false))
if mfaUtil == nil {
c.ResponseError("Invalid multi-factor authentication type")
return
}
err = mfaUtil.Verify(authForm.Passcode)
MfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferMfa(false))
err = MfaUtil.Verify(authForm.Passcode)
if err != nil {
c.ResponseError(err.Error())
return
}
} else if authForm.RecoveryCode != "" {
err = object.MfaRecover(user, authForm.RecoveryCode)
}
if authForm.RecoveryCode != "" {
err = object.RecoverTfs(user, authForm.RecoveryCode)
if err != nil {
c.ResponseError(err.Error())
return
}
} else {
c.ResponseError("missing passcode or recovery code")
return
}
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
@@ -703,7 +683,6 @@ func (c *ApiController) Login() {
}
resp = c.HandleLoggedIn(application, user, &authForm)
c.setMfaUserSession("")
record := object.NewRecord(c.Ctx)
record.Organization = application.Organization
@@ -772,8 +751,7 @@ func (c *ApiController) HandleSamlLogin() {
func (c *ApiController) HandleOfficialAccountEvent() {
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
var data struct {
@@ -783,8 +761,7 @@ func (c *ApiController) HandleOfficialAccountEvent() {
}
err = xml.Unmarshal(respBytes, &data)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
lock.Lock()

View File

@@ -55,18 +55,6 @@ func (c *ApiController) IsAdmin() bool {
return isGlobalAdmin || user.IsAdmin
}
func (c *ApiController) IsAdminOrSelf(user2 *object.User) bool {
isGlobalAdmin, user := c.isGlobalAdmin()
if isGlobalAdmin || (user != nil && user.IsAdmin) {
return true
}
if user.Owner == user2.Owner && user.Name == user2.Name {
return true
}
return false
}
func (c *ApiController) isGlobalAdmin() (bool, *object.User) {
username := c.GetSessionUsername()
if strings.HasPrefix(username, "app/") {
@@ -91,8 +79,7 @@ func (c *ApiController) getCurrentUser() *object.User {
} else {
user, err = object.GetUser(userId)
if err != nil {
c.ResponseError(err.Error())
return nil
panic(err)
}
}
return user
@@ -125,8 +112,7 @@ func (c *ApiController) GetSessionApplication() *object.Application {
}
application, err := object.GetApplicationByClientId(clientId.(string))
if err != nil {
c.ResponseError(err.Error())
return nil
panic(err)
}
return application
@@ -190,16 +176,20 @@ func (c *ApiController) SetSessionData(s *SessionData) {
c.SetSession("SessionData", util.StructToJson(s))
}
func (c *ApiController) setMfaUserSession(userId string) {
c.SetSession(object.MfaSessionUserId, userId)
func (c *ApiController) setMfaSessionData(data *object.MfaSessionData) {
c.SetSession(object.MfaSessionUserId, data.UserId)
}
func (c *ApiController) getMfaUserSession() string {
userId := c.Ctx.Input.CruSession.Get(object.MfaSessionUserId)
func (c *ApiController) getMfaSessionData() *object.MfaSessionData {
userId := c.GetSession(object.MfaSessionUserId)
if userId == nil {
return ""
return nil
}
return userId.(string)
data := &object.MfaSessionData{
UserId: userId.(string),
}
return data
}
func (c *ApiController) setExpireForSession() {

View File

@@ -41,24 +41,22 @@ func (c *ApiController) GetCerts() {
if limit == "" || page == "" {
maskedCerts, err := object.GetMaskedCerts(object.GetCerts(owner))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedCerts)
c.Data["json"] = maskedCerts
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetCertCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
certs, err := object.GetMaskedCerts(object.GetPaginationCerts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(certs, paginator.Nums())
@@ -82,24 +80,22 @@ func (c *ApiController) GetGlobleCerts() {
if limit == "" || page == "" {
maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts())
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedCerts)
c.Data["json"] = maskedCerts
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetGlobalCertsCount(field, value)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
certs, err := object.GetMaskedCerts(object.GetPaginationGlobalCerts(paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(certs, paginator.Nums())
@@ -117,11 +113,11 @@ func (c *ApiController) GetCert() {
id := c.Input().Get("id")
cert, err := object.GetCert(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedCert(cert))
c.Data["json"] = object.GetMaskedCert(cert)
c.ServeJSON()
}
// UpdateCert

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetChats() {
if limit == "" || page == "" {
maskedChats, err := object.GetMaskedChats(object.GetChats(owner))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedChats)
c.Data["json"] = maskedChats
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetChatCount(owner, field, value)
@@ -77,11 +77,11 @@ func (c *ApiController) GetChat() {
maskedChat, err := object.GetMaskedChat(object.GetChat(id))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedChat)
c.Data["json"] = maskedChat
c.ServeJSON()
}
// UpdateChat

View File

@@ -44,26 +44,14 @@ func (c *ApiController) Enforce() {
}
if permissionId != "" {
permission, err := object.GetPermission(permissionId)
enforceResult, err := object.Enforce(permissionId, &request)
if err != nil {
c.ResponseError(err.Error())
return
}
res := []bool{}
if permission == nil {
res = append(res, false)
} else {
enforceResult, err := object.Enforce(permission, &request)
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
res = append(res, enforceResult)
c.ResponseOk(res)
return
}
@@ -88,16 +76,8 @@ func (c *ApiController) Enforce() {
}
res := []bool{}
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
for _, permissionIds := range listPermissionIdMap {
firstPermission, err := object.GetPermission(permissionIds[0])
if err != nil {
c.ResponseError(err.Error())
return
}
enforceResult, err := object.Enforce(firstPermission, &request, permissionIds...)
for _, permission := range permissions {
enforceResult, err := object.Enforce(permission.GetId(), &request)
if err != nil {
c.ResponseError(err.Error())
return
@@ -105,7 +85,6 @@ func (c *ApiController) Enforce() {
res = append(res, enforceResult)
}
c.ResponseOk(res)
}
@@ -130,32 +109,14 @@ func (c *ApiController) BatchEnforce() {
}
if permissionId != "" {
permission, err := object.GetPermission(permissionId)
enforceResult, err := object.BatchEnforce(permissionId, &requests)
if err != nil {
c.ResponseError(err.Error())
return
}
res := [][]bool{}
if permission == nil {
l := len(requests)
resRequest := make([]bool, l)
for i := 0; i < l; i++ {
resRequest[i] = false
}
res = append(res, resRequest)
} else {
enforceResult, err := object.BatchEnforce(permission, &requests)
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
res = append(res, enforceResult)
c.ResponseOk(res)
return
}
@@ -174,16 +135,8 @@ func (c *ApiController) BatchEnforce() {
}
res := [][]bool{}
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
for _, permissionIds := range listPermissionIdMap {
firstPermission, err := object.GetPermission(permissionIds[0])
if err != nil {
c.ResponseError(err.Error())
return
}
enforceResult, err := object.BatchEnforce(firstPermission, &requests, permissionIds...)
for _, permission := range permissions {
enforceResult, err := object.BatchEnforce(permission.GetId(), &requests)
if err != nil {
c.ResponseError(err.Error())
return
@@ -191,7 +144,6 @@ func (c *ApiController) BatchEnforce() {
res = append(res, enforceResult)
}
c.ResponseOk(res)
}

View File

@@ -82,9 +82,9 @@ func (c *ApiController) GetGroup() {
group, err := object.GetGroup(id)
if err != nil {
c.ResponseError(err.Error())
return
} else {
c.ResponseOk(group)
}
c.ResponseOk(group)
}
// UpdateGroup

View File

@@ -38,11 +38,8 @@ type LdapSyncResp struct {
}
// GetLdapUsers
// @Title GetLdapser
// @Tag Account API
// @Description get ldap users
// Param id string true "id"
// @Success 200 {object} LdapResp The Response object
// @Title GetLdapser
// @router /get-ldap-users [get]
func (c *ApiController) GetLdapUsers() {
id := c.Input().Get("id")
@@ -97,24 +94,18 @@ func (c *ApiController) GetLdapUsers() {
}
// GetLdaps
// @Title GetLdaps
// @Tag Account API
// @Description get ldaps
// @Param owner query string false "owner"
// @Success 200 {array} object.Ldap The Response object
// @Title GetLdaps
// @router /get-ldaps [get]
func (c *ApiController) GetLdaps() {
owner := c.Input().Get("owner")
c.ResponseOk(object.GetMaskedLdaps(object.GetLdaps(owner)))
c.ResponseOk(object.GetLdaps(owner))
}
// GetLdap
// @Title GetLdap
// @Tag Account API
// @Description get ldap
// @Param id query string true "id"
// @Success 200 {object} object.Ldap The Response object
// @Title GetLdap
// @router /get-ldap [get]
func (c *ApiController) GetLdap() {
id := c.Input().Get("id")
@@ -125,20 +116,12 @@ func (c *ApiController) GetLdap() {
}
_, name := util.GetOwnerAndNameFromId(id)
ldap, err := object.GetLdap(name)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(object.GetMaskedLdap(ldap))
c.ResponseOk(object.GetLdap(name))
}
// AddLdap
// @Title AddLdap
// @Tag Account API
// @Description add ldap
// @Param body body object.Ldap true "The details of the ldap"
// @Success 200 {object} controllers.Response The Response object
// @Title AddLdap
// @router /add-ldap [post]
func (c *ApiController) AddLdap() {
var ldap object.Ldap
@@ -177,11 +160,8 @@ func (c *ApiController) AddLdap() {
}
// UpdateLdap
// @Title UpdateLdap
// @Tag Account API
// @Description update ldap
// @Param body body object.Ldap true "The details of the ldap"
// @Success 200 {object} controllers.Response The Response object
// @Title UpdateLdap
// @router /update-ldap [post]
func (c *ApiController) UpdateLdap() {
var ldap object.Ldap
@@ -218,11 +198,8 @@ func (c *ApiController) UpdateLdap() {
}
// DeleteLdap
// @Title DeleteLdap
// @Tag Account API
// @Description delete ldap
// @Param body body object.Ldap true "The details of the ldap"
// @Success 200 {object} controllers.Response The Response object
// @Title DeleteLdap
// @router /delete-ldap [post]
func (c *ApiController) DeleteLdap() {
var ldap object.Ldap
@@ -245,16 +222,12 @@ func (c *ApiController) DeleteLdap() {
}
// SyncLdapUsers
// @Title SyncLdapUsers
// @Tag Account API
// @Description sync ldap users
// @Param id query string true "id"
// @Success 200 {object} LdapSyncResp The Response object
// @Title SyncLdapUsers
// @router /sync-ldap-users [post]
func (c *ApiController) SyncLdapUsers() {
id := c.Input().Get("id")
owner, ldapId := util.GetOwnerAndNameFromId(id)
owner := c.Input().Get("owner")
ldapId := c.Input().Get("ldapId")
var users []object.LdapUser
err := json.Unmarshal(c.Ctx.Input.RequestBody, &users)
if err != nil {

View File

@@ -53,11 +53,11 @@ func (c *ApiController) GetMessages() {
}
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedMessages(messages))
c.Data["json"] = object.GetMaskedMessages(messages)
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetMessageCount(owner, organization, field, value)
@@ -89,19 +89,18 @@ func (c *ApiController) GetMessage() {
id := c.Input().Get("id")
message, err := object.GetMessage(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(message)
c.Data["json"] = object.GetMaskedMessage(message)
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 {
c.ResponseError(err.Error())
return
panic(err)
}
}
@@ -197,8 +196,7 @@ func (c *ApiController) GetMessageAnswer() {
event := fmt.Sprintf("event: end\ndata: %s\n\n", "end")
_, err = c.Ctx.ResponseWriter.Write([]byte(event))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
answer := stringBuilder.String()
@@ -206,8 +204,7 @@ func (c *ApiController) GetMessageAnswer() {
message.Text = answer
_, err = object.UpdateMessage(message.GetId(), message)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
}

View File

@@ -17,6 +17,7 @@ package controllers
import (
"net/http"
"github.com/beego/beego"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -28,12 +29,12 @@ import (
// @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} controllers.Response The Response object
// @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")
mfaType := c.Ctx.Request.Form.Get("mfaType")
authType := c.Ctx.Request.Form.Get("type")
userId := util.GetId(owner, name)
if len(userId) == 0 {
@@ -41,11 +42,10 @@ func (c *ApiController) MfaSetupInitiate() {
return
}
MfaUtil := object.GetMfaUtil(mfaType, nil)
MfaUtil := object.GetMfaUtil(authType, nil)
if MfaUtil == nil {
c.ResponseError("Invalid auth type")
}
user, err := object.GetUser(userId)
if err != nil {
c.ResponseError(err.Error())
@@ -57,7 +57,10 @@ func (c *ApiController) MfaSetupInitiate() {
return
}
mfaProps, err := MfaUtil.Initiate(c.Ctx, user.GetId())
issuer := beego.AppConfig.String("appname")
accountName := user.GetId()
mfaProps, err := MfaUtil.Initiate(c.Ctx, issuer, accountName)
if err != nil {
c.ResponseError(err.Error())
return
@@ -76,20 +79,16 @@ func (c *ApiController) MfaSetupInitiate() {
// @Success 200 {object} Response object
// @router /mfa/setup/verify [post]
func (c *ApiController) MfaSetupVerify() {
mfaType := c.Ctx.Request.Form.Get("mfaType")
authType := c.Ctx.Request.Form.Get("type")
passcode := c.Ctx.Request.Form.Get("passcode")
if mfaType == "" || passcode == "" {
if authType == "" || passcode == "" {
c.ResponseError("missing auth type or passcode")
return
}
mfaUtil := object.GetMfaUtil(mfaType, nil)
if mfaUtil == nil {
c.ResponseError("Invalid multi-factor authentication type")
return
}
MfaUtil := object.GetMfaUtil(authType, nil)
err := mfaUtil.SetupVerify(c.Ctx, passcode)
err := MfaUtil.SetupVerify(c.Ctx, passcode)
if err != nil {
c.ResponseError(err.Error())
} else {
@@ -109,7 +108,7 @@ func (c *ApiController) MfaSetupVerify() {
func (c *ApiController) MfaSetupEnable() {
owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name")
mfaType := c.Ctx.Request.Form.Get("mfaType")
authType := c.Ctx.Request.Form.Get("type")
user, err := object.GetUser(util.GetId(owner, name))
if err != nil {
@@ -122,13 +121,8 @@ func (c *ApiController) MfaSetupEnable() {
return
}
mfaUtil := object.GetMfaUtil(mfaType, nil)
if mfaUtil == nil {
c.ResponseError("Invalid multi-factor authentication type")
return
}
err = mfaUtil.Enable(c.Ctx, user)
twoFactor := object.GetMfaUtil(authType, nil)
err = twoFactor.Enable(c.Ctx, user)
if err != nil {
c.ResponseError(err.Error())
return
@@ -143,9 +137,11 @@ func (c *ApiController) MfaSetupEnable() {
// @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)
@@ -155,18 +151,28 @@ func (c *ApiController) DeleteMfa() {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError("User doesn't exist")
return
}
err = object.DisabledMultiFactorAuth(user)
mfaProps := user.MultiFactorAuths[:0]
i := 0
for _, mfaProp := range mfaProps {
if mfaProp.Id != id {
mfaProps[i] = mfaProp
i++
}
}
user.MultiFactorAuths = mfaProps
_, err = object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(object.GetAllMfaProps(user, true))
c.ResponseOk(user.MultiFactorAuths)
}
// SetPreferredMfa
@@ -179,7 +185,7 @@ func (c *ApiController) DeleteMfa() {
// @Success 200 {object} Response object
// @router /set-preferred-mfa [post]
func (c *ApiController) SetPreferredMfa() {
mfaType := c.Ctx.Request.Form.Get("mfaType")
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)
@@ -189,15 +195,29 @@ func (c *ApiController) SetPreferredMfa() {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError("User doesn't exist")
return
}
err = object.SetPreferredMultiFactorAuth(user, mfaType)
mfaProps := user.MultiFactorAuths
for i, mfaProp := range user.MultiFactorAuths {
if mfaProp.Id == id {
mfaProps[i].IsPreferred = true
} else {
mfaProps[i].IsPreferred = false
}
}
_, err = object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(object.GetAllMfaProps(user, true))
for i, mfaProp := range mfaProps {
mfaProps[i] = object.GetMaskedProps(mfaProp)
}
c.ResponseOk(mfaProps)
}

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetModels() {
if limit == "" || page == "" {
models, err := object.GetModels(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(models)
c.Data["json"] = models
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetModelCount(owner, field, value)
@@ -77,11 +77,11 @@ func (c *ApiController) GetModel() {
model, err := object.GetModel(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(model)
c.Data["json"] = model
c.ServeJSON()
}
// UpdateModel

View File

@@ -37,26 +37,17 @@ func (c *ApiController) GetOrganizations() {
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
organizationName := c.Input().Get("organizationName")
isGlobalAdmin := c.IsGlobalAdmin()
if limit == "" || page == "" {
var maskedOrganizations []*object.Organization
var err error
if isGlobalAdmin {
maskedOrganizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner))
} else {
maskedOrganizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
}
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedOrganizations)
c.Data["json"] = maskedOrganizations
c.ServeJSON()
} else {
isGlobalAdmin := c.IsGlobalAdmin()
if !isGlobalAdmin {
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
if err != nil {
@@ -73,7 +64,7 @@ func (c *ApiController) GetOrganizations() {
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
organizations, err := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, organizationName, paginator.Offset(), limit, field, value, sortField, sortOrder))
organizations, err := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -42,24 +42,22 @@ func (c *ApiController) GetPayments() {
if limit == "" || page == "" {
payments, err := object.GetPayments(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(payments)
c.Data["json"] = payments
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetPaymentCount(owner, organization, field, value)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
payments, err := object.GetPaginationPayments(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(payments, paginator.Nums())
@@ -101,11 +99,11 @@ func (c *ApiController) GetPayment() {
payment, err := object.GetPayment(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(payment)
c.Data["json"] = payment
c.ServeJSON()
}
// UpdatePayment
@@ -192,8 +190,7 @@ func (c *ApiController) NotifyPayment() {
}
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
}

View File

@@ -41,24 +41,22 @@ func (c *ApiController) GetPermissions() {
if limit == "" || page == "" {
permissions, err := object.GetPermissions(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(permissions)
c.Data["json"] = permissions
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetPermissionCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
permissions, err := object.GetPaginationPermissions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(permissions, paginator.Nums())
@@ -84,6 +82,7 @@ func (c *ApiController) GetPermissionsBySubmitter() {
}
c.ResponseOk(permissions, len(permissions))
return
}
// GetPermissionsByRole
@@ -102,6 +101,7 @@ func (c *ApiController) GetPermissionsByRole() {
}
c.ResponseOk(permissions, len(permissions))
return
}
// GetPermission
@@ -116,11 +116,11 @@ func (c *ApiController) GetPermission() {
permission, err := object.GetPermission(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(permission)
c.Data["json"] = permission
c.ServeJSON()
}
// UpdatePermission

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetPlans() {
if limit == "" || page == "" {
plans, err := object.GetPlans(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(plans)
c.Data["json"] = plans
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetPlanCount(owner, field, value)
@@ -79,15 +79,13 @@ func (c *ApiController) GetPlan() {
plan, err := object.GetPlan(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
if includeOption {
options, err := object.GetPermissionsByRole(plan.Role)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
for _, option := range options {

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetPricings() {
if limit == "" || page == "" {
pricings, err := object.GetPricings(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(pricings)
c.Data["json"] = pricings
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetPricingCount(owner, field, value)
@@ -70,18 +70,18 @@ func (c *ApiController) GetPricings() {
// @Tag Pricing API
// @Description get pricing
// @Param id query string true "The id ( owner/name ) of the pricing"
// @Success 200 {object} object.Pricing The Response object
// @Success 200 {object} object.pricing The Response object
// @router /get-pricing [get]
func (c *ApiController) GetPricing() {
id := c.Input().Get("id")
pricing, err := object.GetPricing(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(pricing)
c.Data["json"] = pricing
c.ServeJSON()
}
// UpdatePricing

View File

@@ -42,11 +42,11 @@ func (c *ApiController) GetProducts() {
if limit == "" || page == "" {
products, err := object.GetProducts(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(products)
c.Data["json"] = products
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetProductCount(owner, field, value)
@@ -78,14 +78,12 @@ func (c *ApiController) GetProduct() {
product, err := object.GetProduct(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
err = object.ExtendProductWithProviders(product)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.Data["json"] = product

View File

@@ -46,8 +46,7 @@ func (c *ApiController) GetProviders() {
if limit == "" || page == "" {
providers, err := object.GetProviders(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedProviders(providers, isMaskEnabled))
@@ -93,8 +92,7 @@ func (c *ApiController) GetGlobalProviders() {
if limit == "" || page == "" {
globalProviders, err := object.GetGlobalProviders()
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(object.GetMaskedProviders(globalProviders, isMaskEnabled))

View File

@@ -42,21 +42,17 @@ func (c *ApiController) GetRecords() {
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
organizationName := c.Input().Get("organizationName")
if limit == "" || page == "" {
records, err := object.GetRecords()
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(records)
c.Data["json"] = records
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
if c.IsGlobalAdmin() && organizationName != "" {
organization = organizationName
}
filterRecord := &object.Record{Organization: organization}
count, err := object.GetRecordCount(field, value, filterRecord)
if err != nil {
@@ -88,17 +84,16 @@ func (c *ApiController) GetRecordsByFilter() {
record := &object.Record{}
err := util.JsonToStruct(body, record)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
records, err := object.GetRecordsByField(record)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(records)
c.Data["json"] = records
c.ServeJSON()
}
// AddRecord

View File

@@ -29,19 +29,9 @@ import (
)
// GetResources
// @router /get-resources [get]
// @Tag Resource API
// @Title GetResources
// @Description get resources
// @Param owner query string true "Owner"
// @Param user query string true "User"
// @Param pageSize query integer false "Page Size"
// @Param p query integer false "Page Number"
// @Param field query string false "Field"
// @Param value query string false "Value"
// @Param sortField query string false "Sort Field"
// @Param sortOrder query string false "Sort Order"
// @Success 200 {array} object.Resource The Response object
// @router /get-resources [get]
func (c *ApiController) GetResources() {
owner := c.Input().Get("owner")
user := c.Input().Get("user")
@@ -63,11 +53,11 @@ func (c *ApiController) GetResources() {
if limit == "" || page == "" {
resources, err := object.GetResources(owner, user)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(resources)
c.Data["json"] = resources
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetResourceCount(owner, user, field, value)
@@ -90,29 +80,22 @@ func (c *ApiController) GetResources() {
// GetResource
// @Tag Resource API
// @Title GetResource
// @Description get resource
// @Param id query string true "The id ( owner/name ) of resource"
// @Success 200 {object} object.Resource The Response object
// @router /get-resource [get]
func (c *ApiController) GetResource() {
id := c.Input().Get("id")
resource, err := object.GetResource(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(resource)
c.Data["json"] = resource
c.ServeJSON()
}
// UpdateResource
// @Tag Resource API
// @Title UpdateResource
// @Description get resource
// @Param id query string true "The id ( owner/name ) of resource"
// @Param resource body object.Resource true "The resource object"
// @Success 200 {object} controllers.Response Success or error
// @router /update-resource [post]
func (c *ApiController) UpdateResource() {
id := c.Input().Get("id")
@@ -131,8 +114,6 @@ func (c *ApiController) UpdateResource() {
// AddResource
// @Tag Resource API
// @Title AddResource
// @Param resource body object.Resource true "Resource object"
// @Success 200 {object} controllers.Response Success or error
// @router /add-resource [post]
func (c *ApiController) AddResource() {
var resource object.Resource
@@ -149,8 +130,6 @@ func (c *ApiController) AddResource() {
// DeleteResource
// @Tag Resource API
// @Title DeleteResource
// @Param resource body object.Resource true "Resource object"
// @Success 200 {object} controllers.Response Success or error
// @router /delete-resource [post]
func (c *ApiController) DeleteResource() {
var resource object.Resource
@@ -179,16 +158,6 @@ func (c *ApiController) DeleteResource() {
// UploadResource
// @Tag Resource API
// @Title UploadResource
// @Param owner query string true "Owner"
// @Param user query string true "User"
// @Param application query string true "Application"
// @Param tag query string false "Tag"
// @Param parent query string false "Parent"
// @Param fullFilePath query string true "Full File Path"
// @Param createdTime query string false "Created Time"
// @Param description query string false "Description"
// @Param file formData file true "Resource file"
// @Success 200 {object} object.Resource FileUrl, objectKey
// @router /upload-resource [post]
func (c *ApiController) UploadResource() {
owner := c.Input().Get("owner")
@@ -227,16 +196,16 @@ func (c *ApiController) UploadResource() {
fileType := "unknown"
contentType := header.Header.Get("Content-Type")
fileType, _ = util.GetOwnerAndNameFromIdNoCheck(contentType + "/")
fileType, _ = util.GetOwnerAndNameFromId(contentType)
if fileType != "image" && fileType != "video" {
ext := filepath.Ext(filename)
mimeType := mime.TypeByExtension(ext)
fileType, _ = util.GetOwnerAndNameFromIdNoCheck(mimeType + "/")
fileType, _ = util.GetOwnerAndNameFromId(mimeType)
}
fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 175)
if tag != "avatar" && tag != "termsOfUse" && !strings.HasPrefix(tag, "idCard") {
if tag != "avatar" && tag != "termsOfUse" {
ext := filepath.Ext(filepath.Base(fullFilePath))
index := len(fullFilePath) - len(ext)
for i := 1; ; i++ {
@@ -323,7 +292,7 @@ func (c *ApiController) UploadResource() {
return
}
_, applicationId := util.GetOwnerAndNameFromIdNoCheck(strings.TrimSuffix(fullFilePath, ".html"))
_, applicationId := util.GetOwnerAndNameFromIdNoCheck(strings.TrimRight(fullFilePath, ".html"))
applicationObj, err := object.GetApplication(applicationId)
if err != nil {
c.ResponseError(err.Error())
@@ -336,25 +305,6 @@ func (c *ApiController) UploadResource() {
c.ResponseError(err.Error())
return
}
case "idCardFront", "idCardBack", "idCardWithPerson":
user, err := object.GetUserNoCheck(util.GetId(owner, username))
if err != nil {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError(c.T("resource:User is nil for tag: avatar"))
return
}
user.Properties[tag] = fileUrl
user.Properties["isIdCardVerified"] = "false"
_, err = object.UpdateUser(user.GetId(), user, []string{"properties"}, false)
if err != nil {
c.ResponseError(err.Error())
return
}
}
c.ResponseOk(fileUrl, objectKey)

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetRoles() {
if limit == "" || page == "" {
roles, err := object.GetRoles(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(roles)
c.Data["json"] = roles
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetRoleCount(owner, field, value)
@@ -77,11 +77,11 @@ func (c *ApiController) GetRole() {
role, err := object.GetRole(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(role)
c.Data["json"] = role
c.ServeJSON()
}
// UpdateRole

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetSessions() {
if limit == "" || page == "" {
sessions, err := object.GetSessions(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(sessions)
c.Data["json"] = sessions
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetSessionCount(owner, field, value)
@@ -76,11 +76,11 @@ func (c *ApiController) GetSingleSession() {
session, err := object.GetSingleSession(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(session)
c.Data["json"] = session
c.ServeJSON()
}
// UpdateSession
@@ -155,9 +155,10 @@ func (c *ApiController) IsSessionDuplicated() {
isUserSessionDuplicated, err := object.IsSessionDuplicated(id, sessionId)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(isUserSessionDuplicated)
c.Data["json"] = &Response{Status: "ok", Msg: "", Data: isUserSessionDuplicated}
c.ServeJSON()
}

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetSubscriptions() {
if limit == "" || page == "" {
subscriptions, err := object.GetSubscriptions(owner)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(subscriptions)
c.Data["json"] = subscriptions
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetSubscriptionCount(owner, field, value)
@@ -70,18 +70,18 @@ func (c *ApiController) GetSubscriptions() {
// @Tag Subscription API
// @Description get subscription
// @Param id query string true "The id ( owner/name ) of the subscription"
// @Success 200 {object} object.Subscription The Response object
// @Success 200 {object} object.subscription The Response object
// @router /get-subscription [get]
func (c *ApiController) GetSubscription() {
id := c.Input().Get("id")
subscription, err := object.GetSubscription(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(subscription)
c.Data["json"] = subscription
c.ServeJSON()
}
// UpdateSubscription

View File

@@ -42,11 +42,11 @@ func (c *ApiController) GetSyncers() {
if limit == "" || page == "" {
organizationSyncers, err := object.GetOrganizationSyncers(owner, organization)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(organizationSyncers)
c.Data["json"] = organizationSyncers
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetSyncerCount(owner, organization, field, value)
@@ -78,11 +78,11 @@ func (c *ApiController) GetSyncer() {
syncer, err := object.GetSyncer(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(syncer)
c.Data["json"] = syncer
c.ServeJSON()
}
// UpdateSyncer

View File

@@ -43,11 +43,11 @@ func (c *ApiController) GetTokens() {
if limit == "" || page == "" {
token, err := object.GetTokens(owner, organization)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(token)
c.Data["json"] = token
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetTokenCount(owner, organization, field, value)
@@ -78,11 +78,11 @@ func (c *ApiController) GetToken() {
id := c.Input().Get("id")
token, err := object.GetToken(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(token)
c.Data["json"] = token
c.ServeJSON()
}
// UpdateToken
@@ -193,8 +193,7 @@ func (c *ApiController) GetOAuthToken() {
host := c.Ctx.Request.Host
oAuthtoken, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.Data["json"] = oAuthtoken
@@ -237,8 +236,7 @@ func (c *ApiController) RefreshToken() {
refreshToken2, err := object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.Data["json"] = refreshToken2
@@ -278,8 +276,7 @@ func (c *ApiController) IntrospectToken() {
}
application, err := object.GetApplicationByClientId(clientId)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
if application == nil || application.ClientSecret != clientSecret {
@@ -292,8 +289,7 @@ func (c *ApiController) IntrospectToken() {
}
token, err := object.GetTokenByTokenAndApplication(tokenValue, application.Name)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
if token == nil {
@@ -323,7 +319,7 @@ func (c *ApiController) IntrospectToken() {
Sub: jwtToken.Subject,
Aud: jwtToken.Audience,
Iss: jwtToken.Issuer,
Jti: jwtToken.ID,
Jti: jwtToken.Id,
}
c.ServeJSON()
}

View File

@@ -41,11 +41,11 @@ func (c *ApiController) GetGlobalUsers() {
if limit == "" || page == "" {
maskedUsers, err := object.GetMaskedUsers(object.GetGlobalUsers())
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedUsers)
c.Data["json"] = maskedUsers
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetGlobalUserCount(field, value)
@@ -80,7 +80,7 @@ func (c *ApiController) GetGlobalUsers() {
// @router /get-users [get]
func (c *ApiController) GetUsers() {
owner := c.Input().Get("owner")
groupName := c.Input().Get("groupName")
groupId := c.Input().Get("groupId")
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
@@ -89,8 +89,8 @@ func (c *ApiController) GetUsers() {
sortOrder := c.Input().Get("sortOrder")
if limit == "" || page == "" {
if groupName != "" {
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName))
if groupId != "" {
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupId))
if err != nil {
c.ResponseError(err.Error())
return
@@ -101,21 +101,21 @@ func (c *ApiController) GetUsers() {
maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedUsers)
c.Data["json"] = maskedUsers
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetUserCount(owner, field, value, groupName)
count, err := object.GetUserCount(owner, field, value, groupId)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupName)
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupId)
if err != nil {
c.ResponseError(err.Error())
return
@@ -153,8 +153,7 @@ func (c *ApiController) GetUser() {
if userId != "" && owner != "" {
userFromUserId, err = object.GetUserByUserId(owner, userId)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
id = util.GetId(userFromUserId.Owner, userFromUserId.Name)
@@ -166,8 +165,7 @@ func (c *ApiController) GetUser() {
organization, err := object.GetOrganization(util.GetId("admin", owner))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
if !organization.IsProfilePublic {
@@ -192,28 +190,21 @@ func (c *ApiController) GetUser() {
}
if err != nil {
c.ResponseError(err.Error())
return
}
if user != nil {
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
panic(err)
}
err = object.ExtendUserWithRolesAndPermissions(user)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
isAdminOrSelf := c.IsAdminOrSelf(user)
maskedUser, err := object.GetMaskedUser(user, isAdminOrSelf)
maskedUser, err := object.GetMaskedUser(user)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(maskedUser)
c.Data["json"] = maskedUser
c.ServeJSON()
}
// UpdateUser
@@ -424,7 +415,6 @@ func (c *ApiController) SetPassword() {
requestUserId := c.GetSessionUsername()
if requestUserId == "" && code == "" {
c.ResponseError(c.T("general:Please login first"), "Please login first")
return
} else if code == "" {
hasPermission, err := object.CheckUserPermission(requestUserId, userId, true, c.GetAcceptLanguage())
@@ -434,7 +424,7 @@ func (c *ApiController) SetPassword() {
}
} else {
if code != c.GetSession("verifiedCode") {
c.ResponseError(c.T("general:Missing parameter"))
c.ResponseError("")
return
}
c.SetSession("verifiedCode", "")
@@ -506,8 +496,7 @@ func (c *ApiController) GetSortedUsers() {
maskedUsers, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.Data["json"] = maskedUsers
@@ -567,8 +556,8 @@ func (c *ApiController) AddUserkeys() {
func (c *ApiController) RemoveUserFromGroup() {
owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name")
groupName := c.Ctx.Request.Form.Get("groupName")
groupId := c.Ctx.Request.Form.Get("groupId")
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupName))
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupId))
c.ServeJSON()
}

View File

@@ -19,14 +19,13 @@ import (
"io"
"mime/multipart"
"os"
"path/filepath"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
func saveFile(path string, file *multipart.File) (err error) {
f, err := os.Create(filepath.Clean(path))
f, err := os.Create(path)
if err != nil {
return err
}

View File

@@ -55,9 +55,6 @@ func (c *ApiController) T(error string) string {
// GetAcceptLanguage ...
func (c *ApiController) GetAcceptLanguage() string {
language := c.Ctx.Request.Header.Get("Accept-Language")
if len(language) > 2 {
language = language[0:2]
}
return conf.GetLanguage(language)
}
@@ -97,8 +94,7 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
user, err := object.GetUser(userId)
if err != nil {
c.ResponseError(err.Error())
return nil, false
panic(err)
}
if user == nil {

View File

@@ -93,9 +93,9 @@ func (c *ApiController) SendVerificationCode() {
}
}
// mfaUserSession != "", means method is MfaAuthVerification
if mfaUserSession := c.getMfaUserSession(); mfaUserSession != "" {
user, err = object.GetUser(mfaUserSession)
// mfaSessionData != nil, means method is MfaSetupVerification
if mfaSessionData := c.getMfaSessionData(); mfaSessionData != nil {
user, err = object.GetUser(mfaSessionData.UserId)
if err != nil {
c.ResponseError(err.Error())
return
@@ -129,12 +129,10 @@ func (c *ApiController) SendVerificationCode() {
} else if vform.Method == ResetVerification {
user = c.getCurrentUser()
} else if vform.Method == MfaAuthVerification {
mfaProps := user.GetPreferredMfaProps(false)
mfaProps := user.GetPreferMfa(false)
if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest {
vform.Dest = mfaProps.Secret
}
} else if vform.Method == MfaSetupVerification {
c.SetSession(object.MfaDestSession, vform.Dest)
}
provider, err := application.GetEmailProvider()
@@ -159,19 +157,12 @@ func (c *ApiController) SendVerificationCode() {
}
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
} else if vform.Method == ResetVerification || vform.Method == MfaSetupVerification {
if vform.CountryCode == "" {
if user = c.getCurrentUser(); user != nil {
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
}
}
if vform.Method == MfaSetupVerification {
c.SetSession(object.MfaCountryCodeSession, vform.CountryCode)
c.SetSession(object.MfaDestSession, vform.Dest)
} else if vform.Method == ResetVerification {
if user = c.getCurrentUser(); user != nil {
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
}
} else if vform.Method == MfaAuthVerification {
mfaProps := user.GetPreferredMfaProps(false)
mfaProps := user.GetPreferMfa(false)
if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest {
vform.Dest = mfaProps.Secret
}
@@ -193,6 +184,11 @@ func (c *ApiController) SendVerificationCode() {
}
}
if vform.Method == MfaSetupVerification {
c.SetSession(object.MfaSmsCountryCodeSession, vform.CountryCode)
c.SetSession(object.MfaSmsDestSession, vform.Dest)
}
if sendResp != nil {
c.ResponseError(sendResp.Error())
} else {

View File

@@ -66,7 +66,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
// @Tag User API
// @Description WebAuthn Registration Flow 2nd stage
// @Param body body protocol.CredentialCreationResponse true "authenticator attestation Response"
// @Success 200 {object} controllers.Response "The Response object"
// @Success 200 {object} Response "The Response object"
// @router /webauthn/signup/finish [post]
func (c *ApiController) WebAuthnSignupFinish() {
webauthnObj, err := object.GetWebAuthnObject(c.Ctx.Request.Host)
@@ -150,7 +150,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
// @Tag Login API
// @Description WebAuthn Login Flow 2nd stage
// @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response"
// @Success 200 {object} controllers.Response "The Response object"
// @Success 200 {object} Response "The Response object"
// @router /webauthn/signin/finish [post]
func (c *ApiController) WebAuthnSigninFinish() {
responseType := c.Input().Get("responseType")

View File

@@ -42,11 +42,11 @@ func (c *ApiController) GetWebhooks() {
if limit == "" || page == "" {
webhooks, err := object.GetWebhooks(owner, organization)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(webhooks)
c.Data["json"] = webhooks
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetWebhookCount(owner, organization, field, value)
@@ -79,11 +79,11 @@ func (c *ApiController) GetWebhook() {
webhook, err := object.GetWebhook(id)
if err != nil {
c.ResponseError(err.Error())
return
panic(err)
}
c.ResponseOk(webhook)
c.Data["json"] = webhook
c.ServeJSON()
}
// UpdateWebhook

View File

@@ -17,7 +17,6 @@ package deployment
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/casdoor/casdoor/object"
@@ -46,7 +45,7 @@ func uploadFolder(storageProvider oss.StorageInterface, folder string) {
continue
}
file, err := os.Open(filepath.Clean(path + filename))
file, err := os.Open(path + filename)
if err != nil {
panic(err)
}

View File

@@ -25,12 +25,6 @@ import (
)
func TestDeployStaticFiles(t *testing.T) {
object.InitConfig()
provider, err := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss"))
if err != nil {
panic(err)
}
provider := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss"))
deployStaticFiles(provider)
}

7
go.mod
View File

@@ -39,18 +39,17 @@ require (
github.com/lib/pq v1.10.2
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
github.com/markbates/goth v1.75.2
github.com/mitchellh/mapstructure v1.5.0
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/pquerna/otp v1.4.0
github.com/plutov/paypal/v4 v4.7.0
github.com/prometheus/client_golang v1.11.1
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.9.0
github.com/russellhaering/goxmldsig v1.2.0
github.com/sashabaranov/go-openai v1.12.0
github.com/sashabaranov/go-openai v1.9.1
github.com/satori/go.uuid v1.2.0
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible
@@ -60,7 +59,7 @@ require (
github.com/tealeg/xlsx v1.0.5
github.com/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/xorm-io/builder v0.3.13
github.com/xorm-io/builder v0.3.13 // indirect
github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6
github.com/yusufpapurcu/wmi v1.2.2 // indirect

11
go.sum
View File

@@ -105,8 +105,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
@@ -497,10 +495,10 @@ 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/plutov/paypal/v4 v4.7.0 h1:6TRvYD4ny6yQfHaABeStNf43GFM1wpW5jU/XEDGQmq0=
github.com/plutov/paypal/v4 v4.7.0/go.mod h1:D56boafCRGcF/fEM0w282kj0fCDKIyrwOPX/Te1jCmw=
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/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -546,8 +544,8 @@ github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80
github.com/russellhaering/goxmldsig v1.2.0/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.12.0 h1:aRNHH0gtVfrpIaEolD0sWrLLRnYQNK4cH/bIAHwL8Rk=
github.com/sashabaranov/go-openai v1.12.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
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=
@@ -597,6 +595,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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=

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert",
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
"Unauthorized operation": "Nicht autorisierte Operation",
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s"
},
"cas": {
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación",
"The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
"Unauthorized operation": "Operación no autorizada",
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s"
},
"cas": {
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application",
"The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
"Unauthorized operation": "Opération non autorisée",
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s"
},
"cas": {
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
"Unauthorized operation": "Operasi tidak sah",
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s"
},
"cas": {
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
"Unauthorized operation": "不正操作",
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s"
},
"cas": {
"Service %s and %s do not match": "サービス%sと%sは一致しません"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
"Unauthorized operation": "무단 조작",
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s"
},
"cas": {
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
"Unauthorized operation": "Несанкционированная операция",
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s"
},
"cas": {
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng",
"The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
"Unauthorized operation": "Hoạt động không được ủy quyền",
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s"
},
"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"

View File

@@ -18,8 +18,7 @@
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
"Unauthorized operation": "未授权的操作",
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中"
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s"
},
"cas": {
"Service %s and %s do not match": "服务%s与%s不匹配"

View File

@@ -20,37 +20,32 @@ import (
"fmt"
"io"
"net/http"
_ "net/url"
_ "time"
"github.com/casdoor/casdoor/util"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2"
)
type CustomIdProvider struct {
Client *http.Client
Config *oauth2.Config
UserInfoURL string
TokenURL string
AuthURL string
UserMapping map[string]string
Scopes []string
Client *http.Client
Config *oauth2.Config
UserInfoUrl string
}
func NewCustomIdProvider(idpInfo *ProviderInfo, redirectUrl string) *CustomIdProvider {
func NewCustomIdProvider(clientId string, clientSecret string, redirectUrl string, authUrl string, tokenUrl string, userInfoUrl string) *CustomIdProvider {
idp := &CustomIdProvider{}
idp.UserInfoUrl = userInfoUrl
idp.Config = &oauth2.Config{
ClientID: idpInfo.ClientId,
ClientSecret: idpInfo.ClientSecret,
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
Endpoint: oauth2.Endpoint{
AuthURL: idpInfo.AuthURL,
TokenURL: idpInfo.TokenURL,
AuthURL: authUrl,
TokenURL: tokenUrl,
},
}
idp.UserInfoURL = idpInfo.UserInfoURL
idp.UserMapping = idpInfo.UserMapping
idp.Config = config
return idp
}
@@ -65,20 +60,22 @@ func (idp *CustomIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
type CustomUserInfo struct {
Id string `mapstructure:"id"`
Username string `mapstructure:"username"`
DisplayName string `mapstructure:"displayName"`
Email string `mapstructure:"email"`
AvatarUrl string `mapstructure:"avatarUrl"`
Id string `json:"sub"`
Name string `json:"preferred_username,omitempty"`
DisplayName string `json:"name"`
Email string `json:"email"`
AvatarUrl string `json:"picture"`
Status string `json:"status"`
Msg string `json:"msg"`
}
func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
ctUserinfo := &CustomUserInfo{}
accessToken := token.AccessToken
request, err := http.NewRequest("GET", idp.UserInfoURL, nil)
request, err := http.NewRequest("GET", idp.UserInfoUrl, nil)
if err != nil {
return nil, err
}
// add accessToken to request header
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
resp, err := idp.Client.Do(request)
@@ -92,40 +89,21 @@ func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
return nil, err
}
var dataMap map[string]interface{}
err = json.Unmarshal(data, &dataMap)
err = json.Unmarshal(data, ctUserinfo)
if err != nil {
return nil, err
}
// map user info
for k, v := range idp.UserMapping {
_, ok := dataMap[v]
if !ok {
return nil, fmt.Errorf("cannot find %s in user from castom provider", v)
}
dataMap[k] = dataMap[v]
}
// try to parse id to string
id, err := util.ParseIdToString(dataMap["id"])
if err != nil {
return nil, err
}
dataMap["id"] = id
customUserinfo := &CustomUserInfo{}
err = mapstructure.Decode(dataMap, customUserinfo)
if err != nil {
return nil, err
if ctUserinfo.Status != "" {
return nil, fmt.Errorf("err: %s", ctUserinfo.Msg)
}
userInfo := &UserInfo{
Id: customUserinfo.Id,
Username: customUserinfo.Username,
DisplayName: customUserinfo.DisplayName,
Email: customUserinfo.Email,
AvatarUrl: customUserinfo.AvatarUrl,
Id: ctUserinfo.Id,
Username: ctUserinfo.Name,
DisplayName: ctUserinfo.DisplayName,
Email: ctUserinfo.Email,
AvatarUrl: ctUserinfo.AvatarUrl,
}
return userInfo, nil
}

View File

@@ -1,80 +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 idp
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
"golang.org/x/oauth2"
)
const Web3AuthTokenKey = "web3AuthToken"
type MetaMaskIdProvider struct {
Client *http.Client
}
type Web3AuthToken struct {
Address string `json:"address"`
Nonce string `json:"nonce"`
CreateAt uint64 `json:"createAt"`
TypedData string `json:"typedData"`
Signature string `json:"signature"` // signature for typed data
}
func NewMetaMaskIdProvider() *MetaMaskIdProvider {
idp := &MetaMaskIdProvider{}
return idp
}
func (idp *MetaMaskIdProvider) SetHttpClient(client *http.Client) {
idp.Client = client
}
func (idp *MetaMaskIdProvider) GetToken(code string) (*oauth2.Token, error) {
web3AuthToken := Web3AuthToken{}
if err := json.Unmarshal([]byte(code), &web3AuthToken); err != nil {
return nil, err
}
token := &oauth2.Token{
AccessToken: web3AuthToken.Signature,
TokenType: "Bearer",
Expiry: time.Now().AddDate(0, 1, 0),
}
token = token.WithExtra(map[string]interface{}{
Web3AuthTokenKey: web3AuthToken,
})
return token, nil
}
func (idp *MetaMaskIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
// TODO use "github.com/ethereum/go-ethereum" to check address's eth balance or transaction
web3AuthToken, ok := token.Extra(Web3AuthTokenKey).(Web3AuthToken)
if !ok {
return nil, errors.New("invalid web3AuthToken")
}
userInfo := &UserInfo{
Id: web3AuthToken.Address,
Username: web3AuthToken.Address,
DisplayName: web3AuthToken.Address,
AvatarUrl: fmt.Sprintf("metamask:%v", web3AuthToken.Address),
}
return userInfo, nil
}

View File

@@ -32,91 +32,72 @@ type UserInfo struct {
AvatarUrl string
}
type ProviderInfo struct {
Type string
SubType string
ClientId string
ClientSecret string
AppId string
HostUrl string
RedirectUrl string
TokenURL string
AuthURL string
UserInfoURL string
UserMapping map[string]string
}
type IdProvider interface {
SetHttpClient(client *http.Client)
GetToken(code string) (*oauth2.Token, error)
GetUserInfo(token *oauth2.Token) (*UserInfo, error)
}
func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) IdProvider {
switch idpInfo.Type {
case "GitHub":
return NewGithubIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Google":
return NewGoogleIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "QQ":
return NewQqIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "WeChat":
return NewWeChatIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Facebook":
return NewFacebookIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "DingTalk":
return NewDingTalkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Weibo":
return NewWeiBoIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Gitee":
return NewGiteeIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "LinkedIn":
return NewLinkedInIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "WeCom":
if idpInfo.SubType == "Internal" {
return NewWeComInternalIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else if idpInfo.SubType == "Third-party" {
return NewWeComIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
func GetIdProvider(typ string, subType string, clientId string, clientSecret string, appId string, redirectUrl string, hostUrl string, authUrl string, tokenUrl string, userInfoUrl string) IdProvider {
if typ == "GitHub" {
return NewGithubIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "Google" {
return NewGoogleIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "QQ" {
return NewQqIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "WeChat" {
return NewWeChatIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "Facebook" {
return NewFacebookIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "DingTalk" {
return NewDingTalkIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "Weibo" {
return NewWeiBoIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "Gitee" {
return NewGiteeIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "LinkedIn" {
return NewLinkedInIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "WeCom" {
if subType == "Internal" {
return NewWeComInternalIdProvider(clientId, clientSecret, redirectUrl)
} else if subType == "Third-party" {
return NewWeComIdProvider(clientId, clientSecret, redirectUrl)
} else {
return nil
}
case "Lark":
return NewLarkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "GitLab":
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Adfs":
return NewAdfsIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
case "Baidu":
return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Alipay":
return NewAlipayIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Custom":
return NewCustomIdProvider(idpInfo, redirectUrl)
case "Infoflow":
if idpInfo.SubType == "Internal" {
return NewInfoflowInternalIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, idpInfo.AppId, redirectUrl)
} else if idpInfo.SubType == "Third-party" {
return NewInfoflowIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, idpInfo.AppId, redirectUrl)
} else if typ == "Lark" {
return NewLarkIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "GitLab" {
return NewGitlabIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "Adfs" {
return NewAdfsIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
} else if typ == "Baidu" {
return NewBaiduIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "Alipay" {
return NewAlipayIdProvider(clientId, clientSecret, redirectUrl)
} else if typ == "Custom" {
return NewCustomIdProvider(clientId, clientSecret, redirectUrl, authUrl, tokenUrl, userInfoUrl)
} else if typ == "Infoflow" {
if subType == "Internal" {
return NewInfoflowInternalIdProvider(clientId, clientSecret, appId, redirectUrl)
} else if subType == "Third-party" {
return NewInfoflowIdProvider(clientId, clientSecret, appId, redirectUrl)
} else {
return nil
}
case "Casdoor":
return NewCasdoorIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
case "Okta":
return NewOktaIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
case "Douyin":
return NewDouyinIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "Bilibili":
return NewBilibiliIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "MetaMask":
return NewMetaMaskIdProvider()
default:
if isGothSupport(idpInfo.Type) {
return NewGothIdProvider(idpInfo.Type, idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
}
return nil
} else if typ == "Casdoor" {
return NewCasdoorIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
} else if typ == "Okta" {
return NewOktaIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
} else if typ == "Douyin" {
return NewDouyinIdProvider(clientId, clientSecret, redirectUrl)
} else if isGothSupport(typ) {
return NewGothIdProvider(typ, clientId, clientSecret, redirectUrl, hostUrl)
} else if typ == "Bilibili" {
return NewBilibiliIdProvider(clientId, clientSecret, redirectUrl)
}
return nil
}
var gothList = []string{

View File

@@ -198,22 +198,12 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
request, err := http.NewRequest("GET", accessTokenUrl, nil)
if err != nil {
return "", err
}
client := new(http.Client)
resp, err := client.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var data struct {
ExpireIn int `json:"expires_in"`
AccessToken string `json:"access_token"`
@@ -222,30 +212,20 @@ func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (
if err != nil {
return "", err
}
return data.AccessToken, nil
}
func GetWechatOfficialAccountQRCode(clientId string, clientSecret string) (string, error) {
accessToken, err := GetWechatOfficialAccountAccessToken(clientId, clientSecret)
client := new(http.Client)
weChatEndpoint := "https://api.weixin.qq.com/cgi-bin/qrcode/create"
qrCodeUrl := fmt.Sprintf("%s?access_token=%s", weChatEndpoint, accessToken)
params := `{"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}`
params := "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"test\"}}}"
bodyData := bytes.NewReader([]byte(params))
qrCodeUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s", accessToken)
requeset, err := http.NewRequest("POST", qrCodeUrl, bodyData)
if err != nil {
return "", err
}
resp, err := client.Do(requeset)
if err != nil {
return "", err
}
defer resp.Body.Close()
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err

View File

@@ -15,10 +15,8 @@
package object
import (
"database/sql"
"fmt"
"runtime"
"strings"
"github.com/beego/beego"
"github.com/casdoor/casdoor/conf"
@@ -48,11 +46,6 @@ func InitConfig() {
}
func InitAdapter() {
err := createDatabaseForPostgres(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
if err != nil {
panic(err)
}
adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
@@ -103,32 +96,7 @@ func NewAdapter(driverName string, dataSourceName string, dbName string) *Adapte
return a
}
func createDatabaseForPostgres(driverName string, dataSourceName string, dbName string) error {
if driverName == "postgres" {
db, err := sql.Open(driverName, dataSourceName)
if err != nil {
return err
}
defer db.Close()
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s;", dbName))
if err != nil {
if !strings.Contains(err.Error(), "already exists") {
return err
}
}
return nil
} else {
return nil
}
}
func (a *Adapter) CreateDatabase() error {
if a.driverName == "postgres" {
return nil
}
engine, err := xorm.NewEngine(a.driverName, a.dataSourceName)
if err != nil {
return err
@@ -177,6 +145,11 @@ func (a *Adapter) createTable() {
panic(err)
}
err = a.Engine.Sync2(new(UserGroupRelation))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Role))
if err != nil {
panic(err)
@@ -307,7 +280,7 @@ func GetSession(owner string, offset, limit int, field, value, sortField, sortOr
session = session.And("owner=?", owner)
}
if field != "" && value != "" {
if util.FilterField(field) {
if filterField(field) {
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
@@ -335,7 +308,7 @@ func GetSessionForUser(owner string, offset, limit int, field, value, sortField,
}
}
if field != "" && value != "" {
if util.FilterField(field) {
if filterField(field) {
if offset != -1 {
field = fmt.Sprintf("a.%s", field)
}

View File

@@ -57,7 +57,6 @@ type Application struct {
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
Tags []string `xorm:"mediumtext" json:"tags"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`

View File

@@ -16,6 +16,7 @@ package object
import (
"fmt"
"regexp"
"strings"
"time"
"unicode"
@@ -27,11 +28,21 @@ import (
goldap "github.com/go-ldap/ldap/v3"
)
var (
reWhiteSpace *regexp.Regexp
reFieldWhiteList *regexp.Regexp
)
const (
SigninWrongTimesLimit = 5
LastSignWrongTimeDuration = time.Minute * 15
)
func init() {
reWhiteSpace, _ = regexp.Compile(`\s`)
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
}
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
if organization == nil {
return i18n.Translate(lang, "check:Organization does not exist")
@@ -47,7 +58,7 @@ func CheckUserSignup(application *Application, organization *Organization, form
if util.IsEmailValid(form.Username) {
return i18n.Translate(lang, "check:Username cannot be an email address")
}
if util.ReWhiteSpace.MatchString(form.Username) {
if reWhiteSpace.MatchString(form.Username) {
return i18n.Translate(lang, "check:Username cannot contain white spaces")
}
@@ -283,6 +294,10 @@ func CheckUserPassword(organization string, username string, password string, la
return user, ""
}
func filterField(field string) bool {
return reFieldWhiteList.MatchString(field)
}
func CheckUserPermission(requestUserId, userId string, strict bool, lang string) (bool, error) {
if requestUserId == "" {
return false, fmt.Errorf(i18n.Translate(lang, "general:Please login first"))
@@ -381,9 +396,14 @@ func CheckUsername(username string, lang string) string {
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
}
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
exclude, _ := regexp.Compile("^[\u0021-\u007E]+$")
if !exclude.MatchString(username) {
return ""
}
if !util.ReUserName.MatchString(username) {
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
re, _ := regexp.Compile("^[a-zA-Z0-9]+((?:-[a-zA-Z0-9]+)|(?:_[a-zA-Z0-9]+))*$")
if !re.MatchString(username) {
return i18n.Translate(lang, "check:The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.")
}

View File

@@ -19,23 +19,23 @@ import (
"fmt"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/builder"
"github.com/xorm-io/core"
)
type Group struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk unique index" json:"name"`
Name string `xorm:"varchar(100) notnull pk unique" json:"name"`
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"`
Id string `xorm:"varchar(100) not null index" json:"id"`
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"`
@@ -95,6 +95,24 @@ func getGroup(owner string, name string) (*Group, error) {
}
}
func getGroupById(id string) (*Group, error) {
if id == "" {
return nil, nil
}
group := Group{Id: id}
existed, err := adapter.Engine.Get(&group)
if err != nil {
return nil, err
}
if existed {
return &group, nil
} else {
return nil, nil
}
}
func GetGroup(id string) (*Group, error) {
owner, name := util.GetOwnerAndNameFromId(id)
return getGroup(owner, name)
@@ -107,18 +125,7 @@ func UpdateGroup(id string, group *Group) (bool, error) {
return false, err
}
err = checkGroupName(group.Name)
if err != nil {
return false, err
}
if name != group.Name {
err := GroupChangeTrigger(name, group.Name)
if err != nil {
return false, err
}
}
group.UpdatedTime = util.GetCurrentTime()
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(group)
if err != nil {
return false, err
@@ -128,9 +135,8 @@ func UpdateGroup(id string, group *Group) (bool, error) {
}
func AddGroup(group *Group) (bool, error) {
err := checkGroupName(group.Name)
if err != nil {
return false, err
if group.Id == "" {
group.Id = util.GenerateId()
}
affected, err := adapter.Engine.Insert(group)
@@ -158,37 +164,43 @@ func DeleteGroup(group *Group) (bool, error) {
return false, err
}
if count, err := adapter.Engine.Where("parent_id = ?", group.Name).Count(&Group{}); err != nil {
if count, err := adapter.Engine.Where("parent_id = ?", group.Id).Count(&Group{}); err != nil {
return false, err
} else if count > 0 {
return false, errors.New("group has children group")
}
if count, err := GetGroupUserCount(group.Name, "", ""); err != nil {
if count, err := GetGroupUserCount(group.GetId(), "", ""); err != nil {
return false, err
} else if count > 0 {
return false, errors.New("group has users")
}
affected, err := adapter.Engine.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
session := adapter.Engine.NewSession()
defer session.Close()
if err := session.Begin(); err != nil {
return false, err
}
if _, err := session.Delete(&UserGroupRelation{GroupId: group.Id}); err != nil {
session.Rollback()
return false, err
}
affected, err := session.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
if err != nil {
session.Rollback()
return false, err
}
if err := session.Commit(); err != nil {
return false, err
}
return affected != 0, nil
}
func checkGroupName(name string) error {
exist, err := adapter.Engine.Exist(&Organization{Owner: "admin", Name: name})
if err != nil {
return err
}
if exist {
return errors.New("group name can't be same as the organization name")
}
return nil
}
func (group *Group) GetId() string {
return fmt.Sprintf("%s/%s", group.Owner, group.Name)
}
@@ -203,8 +215,9 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
Key: group.Name,
Type: group.Type,
Owner: group.Owner,
Id: group.Id,
}
children := ConvertToTreeData(groups, group.Name)
children := ConvertToTreeData(groups, group.Id)
if len(children) > 0 {
node.Children = children
}
@@ -213,113 +226,3 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
}
return treeData
}
func RemoveUserFromGroup(owner, name, groupName string) (bool, error) {
user, err := getUser(owner, name)
if err != nil {
return false, err
}
if user == nil {
return false, errors.New("user not exist")
}
user.Groups = util.DeleteVal(user.Groups, groupName)
affected, err := updateUser(user.GetId(), user, []string{"groups"})
if err != nil {
return false, err
}
return affected != 0, err
}
func GetGroupUserCount(groupName string, field, value string) (int64, error) {
if field == "" && value == "" {
return adapter.Engine.Where(builder.Like{"`groups`", groupName}).
Count(&User{})
} else {
return adapter.Engine.Table("user").
Where(builder.Like{"`groups`", groupName}).
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
Count()
}
}
func GetPaginationGroupUsers(groupName string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
users := []*User{}
session := adapter.Engine.Table("user").
Where(builder.Like{"`groups`", groupName + "\""})
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if field != "" && value != "" {
session = session.And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%")
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
if sortOrder == "ascend" {
session = session.Asc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
} else {
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
}
err := session.Find(&users)
if err != nil {
return nil, err
}
return users, nil
}
func GetGroupUsers(groupName string) ([]*User, error) {
users := []*User{}
err := adapter.Engine.Table("user").
Where(builder.Like{"`groups`", groupName + "\""}).
Find(&users)
if err != nil {
return nil, err
}
return users, nil
}
func GroupChangeTrigger(oldName, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
users := []*User{}
err = session.Where(builder.Like{"`groups`", oldName}).Find(&users)
if err != nil {
return err
}
for _, user := range users {
user.Groups = util.ReplaceVal(user.Groups, oldName, newName)
_, err := updateUser(user.GetId(), user, []string{"groups"})
if err != nil {
return err
}
}
groups := []*Group{}
err = session.Where("parent_id = ?", oldName).Find(&groups)
for _, group := range groups {
group.ParentId = newName
_, err := session.ID(core.PK{group.Owner, group.Name}).Cols("parent_id").Update(group)
if err != nil {
return err
}
}
err = session.Commit()
if err != nil {
return err
}
return nil
}

View File

@@ -61,7 +61,7 @@ func getBuiltInAccountItems() []*AccountItem {
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Groups", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Groups", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
@@ -93,7 +93,7 @@ func initBuiltInOrganization() bool {
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
PasswordType: "plain",
PasswordOptions: []string{"AtLeast6"},
CountryCodes: []string{"US", "ES", "FR", "DE", "GB", "CN", "JP", "KR", "VN", "ID", "SG", "IN"},
CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"},
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Tags: []string{},
Languages: []string{"en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "pt"},
@@ -130,7 +130,7 @@ func initBuiltInUser() {
Avatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Email: "admin@example.com",
Phone: "12345678910",
CountryCode: "US",
CountryCode: "CN",
Address: []string{},
Affiliation: "Example Inc.",
Tag: "staff",
@@ -184,7 +184,6 @@ func initBuiltInApplication() {
{Name: "Phone", Visible: true, Required: true, Prompted: false, Rule: "None"},
{Name: "Agreement", Visible: true, Required: true, Prompted: false, Rule: "None"},
},
Tags: []string{},
RedirectUris: []string{},
ExpireInHours: 168,
FormOffset: 2,

View File

@@ -145,9 +145,6 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
if application.RedirectUris == nil {
application.RedirectUris = []string{}
}
if application.Tags == nil {
application.Tags = []string{}
}
}
for _, permission := range data.Permissions {
if permission.Actions == nil {

View File

@@ -103,37 +103,6 @@ func GetLdap(id string) (*Ldap, error) {
}
}
func GetMaskedLdap(ldap *Ldap, errs ...error) (*Ldap, error) {
if len(errs) > 0 && errs[0] != nil {
return nil, errs[0]
}
if ldap == nil {
return nil, nil
}
if ldap.Password != "" {
ldap.Password = "***"
}
return ldap, nil
}
func GetMaskedLdaps(ldaps []*Ldap, errs ...error) ([]*Ldap, error) {
if len(errs) > 0 && errs[0] != nil {
return nil, errs[0]
}
var err error
for _, ldap := range ldaps {
ldap, err = GetMaskedLdap(ldap)
if err != nil {
return nil, err
}
}
return ldaps, nil
}
func UpdateLdap(ldap *Ldap) (bool, error) {
if l, err := GetLdap(ldap.Id); err != nil {
return false, nil

View File

@@ -22,12 +22,14 @@ import (
"github.com/beego/beego/context"
)
const MfaRecoveryCodesSession = "mfa_recovery_codes"
type MfaSessionData struct {
UserId string
}
type MfaProps struct {
Enabled bool `json:"enabled"`
Id string `json:"id"`
IsPreferred bool `json:"isPreferred"`
MfaType string `json:"mfaType" form:"mfaType"`
AuthType string `json:"type" form:"type"`
Secret string `json:"secret,omitempty"`
CountryCode string `json:"countryCode,omitempty"`
URL string `json:"url,omitempty"`
@@ -35,16 +37,15 @@ type MfaProps struct {
}
type MfaInterface interface {
Initiate(ctx *context.Context, userId string) (*MfaProps, error)
SetupVerify(ctx *context.Context, passcode string) error
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
Verify(passcode string) error
}
const (
EmailType = "email"
SmsType = "sms"
TotpType = "app"
SmsType = "sms"
TotpType = "app"
)
const (
@@ -53,30 +54,28 @@ const (
RequiredMfa = "RequiredMfa"
)
func GetMfaUtil(mfaType string, config *MfaProps) MfaInterface {
switch mfaType {
func GetMfaUtil(providerType string, config *MfaProps) MfaInterface {
switch providerType {
case SmsType:
return NewSmsMfaUtil(config)
case EmailType:
return NewEmailMfaUtil(config)
return NewSmsTwoFactor(config)
case TotpType:
return NewTotpMfaUtil(config)
return nil
}
return nil
}
func MfaRecover(user *User, recoveryCode string) error {
func RecoverTfs(user *User, recoveryCode string) error {
hit := false
if len(user.RecoveryCodes) == 0 {
twoFactor := user.GetPreferMfa(false)
if len(twoFactor.RecoveryCodes) == 0 {
return fmt.Errorf("do not have recovery codes")
}
for _, code := range user.RecoveryCodes {
for _, code := range twoFactor.RecoveryCodes {
if code == recoveryCode {
hit = true
user.RecoveryCodes = util.DeleteVal(user.RecoveryCodes, code)
break
}
}
@@ -84,106 +83,30 @@ func MfaRecover(user *User, recoveryCode string) error {
return fmt.Errorf("recovery code not found")
}
_, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, user.IsAdminUser())
affected, err := UpdateUser(user.GetId(), user, []string{"two_factor_auth"}, user.IsAdminUser())
if err != nil {
return err
}
return nil
}
func GetAllMfaProps(user *User, masked bool) []*MfaProps {
mfaProps := []*MfaProps{}
for _, mfaType := range []string{SmsType, EmailType, TotpType} {
mfaProps = append(mfaProps, user.GetMfaProps(mfaType, masked))
}
return mfaProps
}
func (user *User) GetMfaProps(mfaType string, masked bool) *MfaProps {
mfaProps := &MfaProps{}
if mfaType == SmsType {
if !user.MfaPhoneEnabled {
return &MfaProps{
Enabled: false,
MfaType: mfaType,
}
}
mfaProps = &MfaProps{
Enabled: user.MfaPhoneEnabled,
MfaType: mfaType,
CountryCode: user.CountryCode,
}
if masked {
mfaProps.Secret = util.GetMaskedPhone(user.Phone)
} else {
mfaProps.Secret = user.Phone
}
} else if mfaType == EmailType {
if !user.MfaEmailEnabled {
return &MfaProps{
Enabled: false,
MfaType: mfaType,
}
}
mfaProps = &MfaProps{
Enabled: user.MfaEmailEnabled,
MfaType: mfaType,
}
if masked {
mfaProps.Secret = util.GetMaskedEmail(user.Email)
} else {
mfaProps.Secret = user.Email
}
} else if mfaType == TotpType {
if user.TotpSecret == "" {
return &MfaProps{
Enabled: false,
MfaType: mfaType,
}
}
mfaProps = &MfaProps{
Enabled: true,
MfaType: mfaType,
}
if masked {
mfaProps.Secret = ""
} else {
mfaProps.Secret = user.TotpSecret
}
}
if user.PreferredMfaType == mfaType {
mfaProps.IsPreferred = true
}
return mfaProps
}
func DisabledMultiFactorAuth(user *User) error {
user.PreferredMfaType = ""
user.RecoveryCodes = []string{}
user.MfaPhoneEnabled = false
user.MfaEmailEnabled = false
user.TotpSecret = ""
_, err := updateUser(user.GetId(), user, []string{"preferred_mfa_type", "recovery_codes", "mfa_phone_enabled", "mfa_email_enabled", "totp_secret"})
if err != nil {
return err
if !affected {
return fmt.Errorf("")
}
return nil
}
func SetPreferredMultiFactorAuth(user *User, mfaType string) error {
user.PreferredMfaType = mfaType
_, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, user.IsAdminUser())
if err != nil {
return err
func GetMaskedProps(props *MfaProps) *MfaProps {
maskedProps := &MfaProps{
AuthType: props.AuthType,
Id: props.Id,
IsPreferred: props.IsPreferred,
}
return nil
if props.AuthType == SmsType {
if !util.IsEmailValid(props.Secret) {
maskedProps.Secret = util.GetMaskedPhone(props.Secret)
} else {
maskedProps.Secret = util.GetMaskedEmail(props.Secret)
}
}
return maskedProps
}

View File

@@ -18,49 +18,26 @@ import (
"errors"
"fmt"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/util"
"github.com/beego/beego/context"
"github.com/google/uuid"
)
const (
MfaCountryCodeSession = "mfa_country_code"
MfaDestSession = "mfa_dest"
MfaSmsCountryCodeSession = "mfa_country_code"
MfaSmsDestSession = "mfa_dest"
MfaSmsRecoveryCodesSession = "mfa_recovery_codes"
)
type SmsMfa struct {
Config *MfaProps
}
func (mfa *SmsMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
recoveryCode := uuid.NewString()
err := ctx.Input.CruSession.Set(MfaRecoveryCodesSession, []string{recoveryCode})
if err != nil {
return nil, err
}
mfaProps := MfaProps{
MfaType: mfa.Config.MfaType,
RecoveryCodes: []string{recoveryCode},
}
return &mfaProps, nil
}
func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
destSession := ctx.Input.CruSession.Get(MfaDestSession)
if destSession == nil {
return errors.New("dest session is missing")
}
dest := destSession.(string)
dest := ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
countryCode := ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
if !util.IsEmailValid(dest) {
countryCodeSession := ctx.Input.CruSession.Get(MfaCountryCodeSession)
if countryCodeSession == nil {
return errors.New("country code is missing")
}
countryCode := countryCodeSession.(string)
dest, _ = util.GetE164Number(dest, countryCode)
}
@@ -70,50 +47,6 @@ func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
return nil
}
func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
recoveryCodes := ctx.Input.CruSession.Get(MfaRecoveryCodesSession).([]string)
if len(recoveryCodes) == 0 {
return fmt.Errorf("recovery codes is missing")
}
columns := []string{"recovery_codes", "preferred_mfa_type"}
user.RecoveryCodes = append(user.RecoveryCodes, recoveryCodes...)
if user.PreferredMfaType == "" {
user.PreferredMfaType = mfa.Config.MfaType
}
if mfa.Config.MfaType == SmsType {
user.MfaPhoneEnabled = true
columns = append(columns, "mfa_phone_enabled")
if user.Phone == "" {
user.Phone = ctx.Input.CruSession.Get(MfaDestSession).(string)
user.CountryCode = ctx.Input.CruSession.Get(MfaCountryCodeSession).(string)
columns = append(columns, "phone", "country_code")
}
} else if mfa.Config.MfaType == EmailType {
user.MfaEmailEnabled = true
columns = append(columns, "mfa_email_enabled")
if user.Email == "" {
user.Email = ctx.Input.CruSession.Get(MfaDestSession).(string)
columns = append(columns, "email")
}
}
_, err := UpdateUser(user.GetId(), user, columns, false)
if err != nil {
return err
}
ctx.Input.CruSession.Delete(MfaRecoveryCodesSession)
ctx.Input.CruSession.Delete(MfaDestSession)
ctx.Input.CruSession.Delete(MfaCountryCodeSession)
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)
@@ -124,21 +57,65 @@ func (mfa *SmsMfa) Verify(passCode string) error {
return nil
}
func NewSmsMfaUtil(config *MfaProps) *SmsMfa {
if config == nil {
config = &MfaProps{
MfaType: SmsType,
}
func (mfa *SmsMfa) Initiate(ctx *context.Context, name string, secret string) (*MfaProps, error) {
recoveryCode, err := uuid.NewRandom()
if err != nil {
return nil, err
}
return &SmsMfa{
Config: config,
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 NewEmailMfaUtil(config *MfaProps) *SmsMfa {
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, err := UpdateUser(user.GetId(), user, []string{"multi_factor_auths"}, user.IsAdminUser())
if err != nil {
return err
}
if !affected {
return fmt.Errorf("failed to enable two factor authentication")
}
return nil
}
func NewSmsTwoFactor(config *MfaProps) *SmsMfa {
if config == nil {
config = &MfaProps{
MfaType: EmailType,
AuthType: SmsType,
}
}
return &SmsMfa{

View File

@@ -1,140 +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 object
import (
"errors"
"fmt"
"github.com/beego/beego"
"github.com/beego/beego/context"
"github.com/google/uuid"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
)
const MfaTotpSecretSession = "mfa_totp_secret"
type TotpMfa struct {
Config *MfaProps
period uint
secretSize uint
digits otp.Digits
}
func (mfa *TotpMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
issuer := beego.AppConfig.String("appname")
if issuer == "" {
issuer = "casdoor"
}
key, err := totp.Generate(totp.GenerateOpts{
Issuer: issuer,
AccountName: userId,
Period: mfa.period,
SecretSize: mfa.secretSize,
Digits: mfa.digits,
})
if err != nil {
return nil, err
}
err = ctx.Input.CruSession.Set(MfaTotpSecretSession, key.Secret())
if err != nil {
return nil, err
}
recoveryCode := uuid.NewString()
err = ctx.Input.CruSession.Set(MfaRecoveryCodesSession, []string{recoveryCode})
if err != nil {
return nil, err
}
mfaProps := MfaProps{
MfaType: mfa.Config.MfaType,
RecoveryCodes: []string{recoveryCode},
Secret: key.Secret(),
URL: key.URL(),
}
return &mfaProps, nil
}
func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
secret := ctx.Input.CruSession.Get(MfaTotpSecretSession)
if secret == nil {
return errors.New("totp secret is missing")
}
result := totp.Validate(passcode, secret.(string))
if result {
return nil
} else {
return errors.New("totp passcode error")
}
}
func (mfa *TotpMfa) Enable(ctx *context.Context, user *User) error {
recoveryCodes := ctx.Input.CruSession.Get(MfaRecoveryCodesSession).([]string)
if len(recoveryCodes) == 0 {
return fmt.Errorf("recovery codes is missing")
}
secret := ctx.Input.CruSession.Get(MfaTotpSecretSession).(string)
if secret == "" {
return fmt.Errorf("totp secret is missing")
}
columns := []string{"recovery_codes", "preferred_mfa_type", "totp_secret"}
user.RecoveryCodes = append(user.RecoveryCodes, recoveryCodes...)
user.TotpSecret = secret
if user.PreferredMfaType == "" {
user.PreferredMfaType = mfa.Config.MfaType
}
_, err := updateUser(user.GetId(), user, columns)
if err != nil {
return err
}
ctx.Input.CruSession.Delete(MfaRecoveryCodesSession)
ctx.Input.CruSession.Delete(MfaTotpSecretSession)
return nil
}
func (mfa *TotpMfa) Verify(passcode string) error {
result := totp.Validate(passcode, mfa.Config.Secret)
if result {
return nil
} else {
return errors.New("totp passcode error")
}
}
func NewTotpMfaUtil(config *MfaProps) *TotpMfa {
if config == nil {
config = &MfaProps{
MfaType: TotpType,
}
}
return &TotpMfa{
Config: config,
period: 30,
secretSize: 20,
digits: otp.DigitsSix,
}
}

View File

@@ -65,13 +65,10 @@ func getOriginFromHost(host string) (string, string) {
return origin, origin
}
// "door.casdoor.com"
protocol := "https://"
if !strings.Contains(host, ".") {
// "localhost:8000" or "computer-name:80"
if strings.HasPrefix(host, "localhost") {
protocol = "http://"
} else if isIpAddress(host) {
// "192.168.0.10"
protocol = "http://"
}
@@ -123,10 +120,6 @@ func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
// link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
// or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
for _, cert := range certs {
if cert.Type != "x509" {
continue
}
certPemBlock := []byte(cert.Certificate)
certDerBlock, _ := pem.Decode(certPemBlock)
x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes)

View File

@@ -69,7 +69,7 @@ type Organization struct {
IsProfilePublic bool `json:"isProfilePublic"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
AccountItems []*AccountItem `xorm:"varchar(3000)" json:"accountItems"`
}
func GetOrganizationCount(owner, field, value string) (int64, error) {
@@ -104,15 +104,10 @@ func GetOrganizationsByFields(owner string, fields ...string) ([]*Organization,
return organizations, nil
}
func GetPaginationOrganizations(owner string, name string, offset, limit int, field, value, sortField, sortOrder string) ([]*Organization, error) {
func GetPaginationOrganizations(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Organization, error) {
organizations := []*Organization{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
var err error
if name != "" {
err = session.Find(&organizations, &Organization{Name: name})
} else {
err = session.Find(&organizations)
}
err := session.Find(&organizations)
if err != nil {
return nil, err
}
@@ -236,10 +231,6 @@ func DeleteOrganization(organization *Organization) (bool, error) {
}
func GetOrganizationByUser(user *User) (*Organization, error) {
if user == nil {
return nil, nil
}
return getOrganization("admin", user.Owner)
}
@@ -476,21 +467,10 @@ func organizationChangeTrigger(oldName string, newName string) error {
return session.Commit()
}
func IsNeedPromptMfa(org *Organization, user *User) bool {
if org == nil || user == nil {
return false
}
func (org *Organization) HasRequiredMfa() bool {
for _, item := range org.MfaItems {
if item.Rule == "Required" {
if item.Name == EmailType && !user.MfaEmailEnabled {
return true
}
if item.Name == SmsType && !user.MfaPhoneEnabled {
return true
}
if item.Name == TotpType && user.TotpSecret == "" {
return true
}
return true
}
}
return false

View File

@@ -370,24 +370,3 @@ func GetMaskedPermissions(permissions []*Permission) []*Permission {
return permissions
}
// GroupPermissionsByModelAdapter group permissions by model and adapter.
// Every model and adapter will be a key, and the value is a list of permission ids.
// With each list of permission ids have the same key, we just need to init the
// enforcer and do the enforce/batch-enforce once (with list of permission ids
// as the policyFilter when the enforcer load policy).
func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]string {
m := make(map[string][]string)
for _, permission := range permissions {
key := permission.Model + permission.Adapter
permissionIds, ok := m[key]
if !ok {
m[key] = []string{permission.GetId()}
} else {
m[key] = append(permissionIds, permission.GetId())
}
}
return m
}

View File

@@ -26,7 +26,7 @@ import (
xormadapter "github.com/casdoor/xorm-adapter/v3"
)
func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforcer {
func getEnforcer(permission *Permission) *casbin.Enforcer {
tableName := "permission_rule"
if len(permission.Adapter) != 0 {
adapterObj, err := getCasbinAdapter(permission.Owner, permission.Adapter)
@@ -77,13 +77,8 @@ func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforc
enforcer.SetAdapter(adapter)
policyFilterV5 := []string{permission.GetId()}
if len(permissionIDs) != 0 {
policyFilterV5 = permissionIDs
}
policyFilter := xormadapter.Filter{
V5: policyFilterV5,
V5: []string{permission.GetId()},
}
if !HasRoleDefinition(m) {
@@ -104,16 +99,29 @@ func getPolicies(permission *Permission) [][]string {
permissionId := permission.GetId()
domainExist := len(permission.Domains) > 0
usersAndRoles := append(permission.Users, permission.Roles...)
for _, userOrRole := range usersAndRoles {
for _, user := range permission.Users {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{userOrRole, domain, resource, strings.ToLower(action), strings.ToLower(permission.Effect), permissionId})
policies = append(policies, []string{user, domain, resource, strings.ToLower(action), "", permissionId})
}
} else {
policies = append(policies, []string{userOrRole, resource, strings.ToLower(action), strings.ToLower(permission.Effect), "", permissionId})
policies = append(policies, []string{user, resource, strings.ToLower(action), "", "", permissionId})
}
}
}
}
for _, role := range permission.Roles {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{role, domain, resource, strings.ToLower(action), "", permissionId})
}
} else {
policies = append(policies, []string{role, resource, strings.ToLower(action), "", "", permissionId})
}
}
}
@@ -233,13 +241,28 @@ func removePolicies(permission *Permission) {
type CasbinRequest = []interface{}
func Enforce(permission *Permission, request *CasbinRequest, permissionIds ...string) (bool, error) {
enforcer := getEnforcer(permission, permissionIds...)
func Enforce(permissionId string, request *CasbinRequest) (bool, error) {
permission, err := GetPermission(permissionId)
if err != nil {
return false, err
}
enforcer := getEnforcer(permission)
return enforcer.Enforce(*request...)
}
func BatchEnforce(permission *Permission, requests *[]CasbinRequest, permissionIds ...string) ([]bool, error) {
enforcer := getEnforcer(permission, permissionIds...)
func BatchEnforce(permissionId string, requests *[]CasbinRequest) ([]bool, error) {
permission, err := GetPermission(permissionId)
if err != nil {
res := []bool{}
for i := 0; i < len(*requests); i++ {
res = append(res, false)
}
return res, err
}
enforcer := getEnforcer(permission)
return enforcer.BatchEnforce(*requests)
}

View File

@@ -43,7 +43,6 @@ func UploadPermissions(owner string, fileId string) (bool, error) {
newPermissions := []*Permission{}
for index, line := range table {
line := line
if index == 0 || parseLineItem(&line, 0) == "" {
continue
}

View File

@@ -16,11 +16,8 @@ package object
import (
"fmt"
"strings"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
@@ -31,22 +28,21 @@ type Provider struct {
Name string `xorm:"varchar(100) notnull pk unique" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Category string `xorm:"varchar(100)" json:"category"`
Type string `xorm:"varchar(100)" json:"type"`
SubType string `xorm:"varchar(100)" json:"subType"`
Method string `xorm:"varchar(100)" json:"method"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
Cert string `xorm:"varchar(100)" json:"cert"`
CustomAuthUrl string `xorm:"varchar(200)" json:"customAuthUrl"`
CustomTokenUrl string `xorm:"varchar(200)" json:"customTokenUrl"`
CustomUserInfoUrl string `xorm:"varchar(200)" json:"customUserInfoUrl"`
CustomLogo string `xorm:"varchar(200)" json:"customLogo"`
Scopes string `xorm:"varchar(100)" json:"scopes"`
UserMapping map[string]string `xorm:"varchar(500)" json:"userMapping"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Category string `xorm:"varchar(100)" json:"category"`
Type string `xorm:"varchar(100)" json:"type"`
SubType string `xorm:"varchar(100)" json:"subType"`
Method string `xorm:"varchar(100)" json:"method"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
Cert string `xorm:"varchar(100)" json:"cert"`
CustomAuthUrl string `xorm:"varchar(200)" json:"customAuthUrl"`
CustomScope string `xorm:"varchar(200)" json:"customScope"`
CustomTokenUrl string `xorm:"varchar(200)" json:"customTokenUrl"`
CustomUserInfoUrl string `xorm:"varchar(200)" json:"customUserInfoUrl"`
CustomLogo string `xorm:"varchar(200)" json:"customLogo"`
Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"`
@@ -229,7 +225,7 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
session = session.Omit("client_secret2")
}
if provider.Type == "Tencent Cloud COS" {
if provider.Type != "Keycloak" {
provider.Endpoint = util.GetEndPoint(provider.Endpoint)
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
}
@@ -243,7 +239,7 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
}
func AddProvider(provider *Provider) (bool, error) {
if provider.Type == "Tencent Cloud COS" {
if provider.Type != "Keycloak" {
provider.Endpoint = util.GetEndPoint(provider.Endpoint)
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
}
@@ -369,27 +365,3 @@ func providerChangeTrigger(oldName string, newName string) error {
return session.Commit()
}
func FromProviderToIdpInfo(ctx *context.Context, provider *Provider) *idp.ProviderInfo {
providerInfo := &idp.ProviderInfo{
Type: provider.Type,
SubType: provider.SubType,
ClientId: provider.ClientId,
ClientSecret: provider.ClientSecret,
AppId: provider.AppId,
HostUrl: provider.Host,
TokenURL: provider.CustomTokenUrl,
AuthURL: provider.CustomAuthUrl,
UserInfoURL: provider.CustomUserInfoUrl,
UserMapping: provider.UserMapping,
}
if provider.Type == "WeChat" {
if ctx != nil && strings.Contains(ctx.Request.UserAgent(), "MicroMessenger") {
providerInfo.ClientId = provider.ClientId2
providerInfo.ClientSecret = provider.ClientSecret2
}
}
return providerInfo
}

View File

@@ -49,7 +49,7 @@ func (pi *ProviderItem) IsProviderVisible() bool {
if pi.Provider == nil {
return false
}
return pi.Provider.Category == "OAuth" || pi.Provider.Category == "SAML" || pi.Provider.Category == "Web3"
return pi.Provider.Category == "OAuth" || pi.Provider.Category == "SAML"
}
func (pi *ProviderItem) isProviderPrompted() bool {

View File

@@ -161,8 +161,7 @@ func SendWebhooks(record *Record) error {
if matched {
if webhook.IsUserExtended {
user, err := getUser(record.Organization, record.User)
user, err = GetMaskedUser(user, false, err)
user, err := GetMaskedUser(getUser(record.Organization, record.User))
if err != nil {
return err
}

View File

@@ -43,7 +43,6 @@ func UploadRoles(owner string, fileId string) (bool, error) {
newRoles := []*Role{}
for index, line := range table {
line := line
if index == 0 || parseLineItem(&line, 0) == "" {
continue
}

View File

@@ -260,17 +260,10 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
// decompress
var buffer bytes.Buffer
rdr := flate.NewReader(bytes.NewReader(defated))
for {
_, err := io.CopyN(&buffer, rdr, 1024)
if err != nil {
if err == io.EOF {
break
}
return "", "", "", err
}
_, err = io.Copy(&buffer, rdr)
if err != nil {
return "", "", "", err
}
var authnRequest saml.AuthnRequest
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
if err != nil {

View File

@@ -42,11 +42,7 @@ func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
return err
}
if provider.Type == sender.Twilio {
if provider.AppId != "" {
phoneNumbers = append([]string{provider.AppId}, phoneNumbers...)
}
} else if provider.Type == sender.Aliyun {
if provider.Type == sender.Aliyun {
for i, number := range phoneNumbers {
phoneNumbers[i] = strings.TrimPrefix(number, "+86")
}

View File

@@ -25,7 +25,6 @@ import (
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/storage"
"github.com/casdoor/casdoor/util"
"github.com/casdoor/oss"
)
var isCloudIntranet bool
@@ -103,11 +102,11 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
return fileUrl, objectKey
}
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer, lang string) (string, string, error) {
endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if storageProvider == nil {
return nil, fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
return "", "", fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
}
if provider.Domain == "" {
@@ -115,18 +114,9 @@ func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface,
UpdateProvider(provider.GetId(), provider)
}
return storageProvider, nil
}
func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer, lang string) (string, string, error) {
storageProvider, err := getStorageProvider(provider, lang)
if err != nil {
return "", "", err
}
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
_, err = storageProvider.Put(objectKey, fileBuffer)
_, err := storageProvider.Put(objectKey, fileBuffer)
if err != nil {
return "", "", err
}
@@ -164,9 +154,15 @@ func DeleteFile(provider *Provider, objectKey string, lang string) error {
return fmt.Errorf(i18n.Translate(lang, "storage:The objectKey: %s is not allowed"), objectKey)
}
storageProvider, err := getStorageProvider(provider, lang)
if err != nil {
return err
endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if storageProvider == nil {
return fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
}
if provider.Domain == "" {
provider.Domain = storageProvider.GetEndpoint()
UpdateProvider(provider.GetId(), provider)
}
return storageProvider.Delete(objectKey)

View File

@@ -50,7 +50,6 @@ type Syncer struct {
AvatarBaseUrl string `xorm:"varchar(100)" json:"avatarBaseUrl"`
ErrorText string `xorm:"mediumtext" json:"errorText"`
SyncInterval int `json:"syncInterval"`
IsReadOnly bool `json:"isReadOnly"`
IsEnabled bool `json:"isEnabled"`
Adapter *Adapter `xorm:"-" json:"-"`

View File

@@ -63,11 +63,9 @@ func (syncer *Syncer) syncUsers() {
}
} else {
if user.PreHash == oHash {
if !syncer.IsReadOnly {
updatedOUser := syncer.createOriginalUserFromUser(user)
syncer.updateUser(updatedOUser)
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
}
updatedOUser := syncer.createOriginalUserFromUser(user)
syncer.updateUser(updatedOUser)
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
// update preHash
user.PreHash = user.Hash
@@ -93,17 +91,15 @@ func (syncer *Syncer) syncUsers() {
panic(err)
}
if !syncer.IsReadOnly {
for _, user := range users {
id := user.Id
if _, ok := oUserMap[id]; !ok {
newOUser := syncer.createOriginalUserFromUser(user)
_, err = syncer.addUser(newOUser)
if err != nil {
panic(err)
}
fmt.Printf("New oUser: %v\n", newOUser)
for _, user := range users {
id := user.Id
if _, ok := oUserMap[id]; !ok {
newOUser := syncer.createOriginalUserFromUser(user)
_, err = syncer.addUser(newOUser)
if err != nil {
panic(err)
}
fmt.Printf("New oUser: %v\n", newOUser)
}
}
}

View File

@@ -170,7 +170,6 @@ func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]string) []*Or
originalUser := &OriginalUser{
Address: []string{},
Properties: map[string]string{},
Groups: []string{},
}
for _, tableColumn := range syncer.TableColumns {

View File

@@ -794,7 +794,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
ErrorDescription: "the wechat mini program session is invalid",
}, nil
}
user, err := getUserByWechatId(application.Organization, openId, unionId)
user, err := getUserByWechatId(openId, unionId)
if err != nil {
return nil, nil, err
}

View File

@@ -216,9 +216,6 @@ func refineUser(user *User) *User {
if user.Permissions == nil {
user.Permissions = []*Permission{}
}
if user.Groups == nil {
user.Groups = []string{}
}
return user
}
@@ -281,14 +278,6 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
return "", "", "", err
}
if cert == nil {
if application.Cert == "" {
return "", "", "", fmt.Errorf("The cert field of the application \"%s\" should not be empty", application.GetId())
} else {
return "", "", "", fmt.Errorf("The cert \"%s\" does not exist", application.Cert)
}
}
// RSA private key
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
if err != nil {

View File

@@ -76,6 +76,7 @@ type User struct {
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
Hash string `xorm:"varchar(100)" json:"hash"`
PreHash string `xorm:"varchar(100)" json:"preHash"`
Groups []string `xorm:"varchar(1000)" json:"groups"`
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
@@ -156,23 +157,16 @@ type User struct {
Yammer string `xorm:"yammer varchar(100)" json:"yammer"`
Yandex string `xorm:"yandex varchar(100)" json:"yandex"`
Zoom string `xorm:"zoom varchar(100)" json:"zoom"`
MetaMask string `xorm:"metamask varchar(100)" json:"metamask"`
Custom string `xorm:"custom varchar(100)" json:"custom"`
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
PreferredMfaType string `xorm:"varchar(100)" json:"preferredMfaType"`
RecoveryCodes []string `xorm:"varchar(1000)" json:"recoveryCodes"`
TotpSecret string `xorm:"varchar(100)" json:"totpSecret"`
MfaPhoneEnabled bool `json:"mfaPhoneEnabled"`
MfaEmailEnabled bool `json:"mfaEmailEnabled"`
MultiFactorAuths []*MfaProps `xorm:"-" json:"multiFactorAuths,omitempty"`
MultiFactorAuths []*MfaProps `json:"multiFactorAuths"`
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"`
Roles []*Role `json:"roles"`
Permissions []*Permission `json:"permissions"`
Groups []string `xorm:"groups varchar(1000)" json:"groups"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`
@@ -226,11 +220,11 @@ func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOr
return users, nil
}
func GetUserCount(owner, field, value string, groupName string) (int64, error) {
func GetUserCount(owner, field, value string, groupId string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
if groupName != "" {
return GetGroupUserCount(groupName, field, value)
if groupId != "" {
return GetGroupUserCount(groupId, field, value)
}
return session.Count(&User{})
@@ -270,11 +264,11 @@ func GetSortedUsers(owner string, sorter string, limit int) ([]*User, error) {
return users, nil
}
func GetPaginationUsers(owner string, offset, limit int, field, value, sortField, sortOrder string, groupName string) ([]*User, error) {
func GetPaginationUsers(owner string, offset, limit int, field, value, sortField, sortOrder string, groupId string) ([]*User, error) {
users := []*User{}
if groupName != "" {
return GetPaginationGroupUsers(groupName, offset, limit, field, value, sortField, sortOrder)
if groupId != "" {
return GetPaginationGroupUsers(groupId, offset, limit, field, value, sortField, sortOrder)
}
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
@@ -321,12 +315,12 @@ func getUserById(owner string, id string) (*User, error) {
}
}
func getUserByWechatId(owner string, wechatOpenId string, wechatUnionId string) (*User, error) {
func getUserByWechatId(wechatOpenId string, wechatUnionId string) (*User, error) {
if wechatUnionId == "" {
wechatUnionId = wechatOpenId
}
user := &User{}
existed, err := adapter.Engine.Where("owner = ?", owner).Where("wechat = ? OR wechat = ?", wechatOpenId, wechatUnionId).Get(user)
existed, err := adapter.Engine.Where("wechat = ? OR wechat = ?", wechatOpenId, wechatUnionId).Get(user)
if err != nil {
return nil, err
}
@@ -419,7 +413,7 @@ func GetUserNoCheck(id string) (*User, error) {
return getUser(owner, name)
}
func GetMaskedUser(user *User, isAdminOrSelf bool, errs ...error) (*User, error) {
func GetMaskedUser(user *User, errs ...error) (*User, error) {
if len(errs) > 0 && errs[0] != nil {
return nil, errs[0]
}
@@ -432,25 +426,17 @@ func GetMaskedUser(user *User, isAdminOrSelf bool, errs ...error) (*User, error)
user.Password = "***"
}
if !isAdminOrSelf {
if user.AccessSecret != "" {
user.AccessSecret = "***"
}
}
if user.ManagedAccounts != nil {
for _, manageAccount := range user.ManagedAccounts {
manageAccount.Password = "***"
}
}
if user.TotpSecret != "" {
user.TotpSecret = ""
if user.MultiFactorAuths != nil {
for i, props := range user.MultiFactorAuths {
user.MultiFactorAuths[i] = GetMaskedProps(props)
}
}
if user.RecoveryCodes != nil {
user.RecoveryCodes = nil
}
return user, nil
}
@@ -461,7 +447,7 @@ func GetMaskedUsers(users []*User, errs ...error) ([]*User, error) {
var err error
for _, user := range users {
user, err = GetMaskedUser(user, false)
user, err = GetMaskedUser(user)
if err != nil {
return nil, err
}
@@ -497,13 +483,17 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
if name != user.Name {
err := userChangeTrigger(name, user.Name)
if err != nil {
return false, err
return false, nil
}
}
if user.Password == "***" {
user.Password = oldUser.Password
}
err = user.UpdateUserHash()
if err != nil {
panic(err)
}
if user.Avatar != oldUser.Avatar && user.Avatar != "" && user.PermanentAvatar != "*" {
user.PermanentAvatar, err = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
@@ -531,7 +521,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
columns = append(columns, "name", "email", "phone", "country_code")
}
affected, err := updateUser(id, user, columns)
affected, err := updateUser(oldUser, user, columns)
if err != nil {
return false, err
}
@@ -539,17 +529,32 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
return affected != 0, nil
}
func updateUser(id string, user *User, columns []string) (int64, error) {
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
err := user.UpdateUserHash()
func updateUser(oldUser, user *User, columns []string) (int64, error) {
session := adapter.Engine.NewSession()
defer session.Close()
session.Begin()
if util.ContainsString(columns, "groups") {
affected, err := updateUserGroupRelation(session, user)
if err != nil {
session.Rollback()
return affected, err
}
}
affected, err := session.ID(core.PK{oldUser.Owner, oldUser.Name}).Cols(columns...).Update(user)
if err != nil {
session.Rollback()
return affected, err
}
err = session.Commit()
if err != nil {
session.Rollback()
return 0, err
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user)
if err != nil {
return 0, err
}
return affected, nil
}
@@ -720,6 +725,11 @@ func DeleteUser(user *User) (bool, error) {
return false, err
}
affected, err = deleteRelationByUser(user.Id)
if err != nil {
return false, err
}
return affected != 0, nil
}
@@ -735,7 +745,7 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
resp.Name = user.Name
resp.DisplayName = user.DisplayName
resp.Avatar = user.Avatar
resp.Groups = user.Groups
resp.Groups = []string{user.Owner}
}
if strings.Contains(scope, "email") {
resp.Email = user.Email
@@ -771,10 +781,6 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
return err
}
if user.Groups == nil {
user.Groups = []string{}
}
return
}
@@ -837,17 +843,35 @@ func userChangeTrigger(oldName string, newName string) error {
}
func (user *User) IsMfaEnabled() bool {
if user == nil {
return false
}
return user.PreferredMfaType != ""
return len(user.MultiFactorAuths) > 0
}
func (user *User) GetPreferredMfaProps(masked bool) *MfaProps {
if user == nil || user.PreferredMfaType == "" {
func (user *User) GetPreferMfa(masked bool) *MfaProps {
if len(user.MultiFactorAuths) == 0 {
return nil
}
return user.GetMfaProps(user.PreferredMfaType, masked)
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]
}
}
func AddUserkeys(user *User, isAdmin bool) (bool, error) {

View File

@@ -34,7 +34,7 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
resp, err := client.Do(req)
if err != nil {
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
if strings.Contains(err.Error(), "EOF") || strings.Contains(err.Error(), "no such host") {
if strings.Contains(err.Error(), "EOF") {
return nil, "", nil
} else {
return nil, "", err

View File

@@ -18,9 +18,9 @@ import (
"bytes"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/casdoor/casdoor/util"
"golang.org/x/net/html"
)
@@ -124,7 +124,6 @@ func chooseFaviconLinkBySizes(links []Link) *Link {
var chosenLink *Link
for _, link := range links {
link := link
if chosenLink == nil || compareSizes(link.Sizes, chosenLink.Sizes) > 0 {
chosenLink = &link
}
@@ -207,7 +206,10 @@ func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, str
}
if !strings.HasPrefix(faviconUrl, "http") {
faviconUrl = util.UrlJoin(htmlUrl, faviconUrl)
faviconUrl, err = url.JoinPath(htmlUrl, faviconUrl)
if err != nil {
return nil, "", err
}
}
}

157
object/user_group.go Normal file
View File

@@ -0,0 +1,157 @@
package object
import (
"errors"
"fmt"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
"github.com/xorm-io/xorm"
)
type UserGroupRelation struct {
UserId string `xorm:"varchar(100) notnull pk" json:"userId"`
GroupId string `xorm:"varchar(100) notnull pk" json:"groupId"`
CreatedTime string `xorm:"created" json:"createdTime"`
UpdatedTime string `xorm:"updated" json:"updatedTime"`
}
func updateUserGroupRelation(session *xorm.Session, user *User) (int64, error) {
physicalGroupCount, err := session.Where("type = ?", "Physical").In("id", user.Groups).Count(Group{})
if err != nil {
return 0, err
}
if physicalGroupCount > 1 {
return 0, errors.New("user can only be in one physical group")
}
groups := []*Group{}
err = session.In("id", user.Groups).Find(&groups)
if err != nil {
return 0, err
}
if len(groups) != len(user.Groups) {
return 0, errors.New("group not found")
}
_, err = session.Delete(&UserGroupRelation{UserId: user.Id})
if err != nil {
return 0, err
}
relations := []*UserGroupRelation{}
for _, group := range groups {
relations = append(relations, &UserGroupRelation{UserId: user.Id, GroupId: group.Id})
}
if len(relations) == 0 {
return 1, nil
}
_, err = session.Insert(relations)
if err != nil {
return 0, err
}
return 1, nil
}
func RemoveUserFromGroup(owner, name, groupId string) (bool, error) {
user, err := getUser(owner, name)
if err != nil {
return false, err
}
groups := []string{}
for _, group := range user.Groups {
if group != groupId {
groups = append(groups, group)
}
}
user.Groups = groups
_, err = UpdateUser(util.GetId(owner, name), user, []string{"groups"}, false)
if err != nil {
return false, err
}
return true, nil
}
func deleteUserGroupRelation(session *xorm.Session, userId, groupId string) (int64, error) {
affected, err := session.ID(core.PK{userId, groupId}).Delete(&UserGroupRelation{})
return affected, err
}
func deleteRelationByUser(id string) (int64, error) {
affected, err := adapter.Engine.Delete(&UserGroupRelation{UserId: id})
return affected, err
}
func GetGroupUserCount(id string, field, value string) (int64, error) {
group, err := GetGroup(id)
if group == nil || err != nil {
return 0, err
}
if field == "" && value == "" {
return adapter.Engine.Count(UserGroupRelation{GroupId: group.Id})
} else {
return adapter.Engine.Table("user").
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
Where("r.group_id = ?", group.Id).
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
Count()
}
}
func GetPaginationGroupUsers(id string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
group, err := GetGroup(id)
if group == nil || err != nil {
return nil, err
}
users := []*User{}
session := adapter.Engine.Table("user").
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
Where("r.group_id = ?", group.Id)
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if field != "" && value != "" {
session = session.And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%")
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
if sortOrder == "ascend" {
session = session.Asc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
} else {
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
}
err = session.Find(&users)
if err != nil {
return nil, err
}
return users, nil
}
func GetGroupUsers(id string) ([]*User, error) {
group, err := GetGroup(id)
if group == nil || err != nil {
return []*User{}, err
}
users := []*User{}
err = adapter.Engine.Table("user_group_relation").Join("INNER", []string{"user", "u"}, "user_group_relation.user_id = u.id").
Where("user_group_relation.group_id = ?", group.Id).
Find(&users)
if err != nil {
return nil, err
}
return users, nil
}

View File

@@ -83,7 +83,6 @@ func UploadUsers(owner string, fileId string) (bool, error) {
newUsers := []*User{}
for index, line := range table {
line := line
if index == 0 || parseLineItem(&line, 0) == "" {
continue
}

View File

@@ -288,18 +288,14 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
itemsChanged = append(itemsChanged, item)
}
if oldUser.PreferredMfaType != newUser.PreferredMfaType {
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.Groups == nil {
oldUser.Groups = []string{}
}
oldUserGroupsJson, _ := json.Marshal(oldUser.Groups)
if newUser.Groups == nil {
newUser.Groups = []string{}
}
newUserGroupsJson, _ := json.Marshal(newUser.Groups)
if string(oldUserGroupsJson) != string(newUserGroupsJson) {
item := GetAccountItemByName("Groups", organization)

View File

@@ -33,13 +33,6 @@ func CorsFilter(ctx *context.Context) {
origin := ctx.Input.Header(headerOrigin)
originConf := conf.GetConfigString("origin")
if ctx.Request.Method == "POST" && ctx.Request.RequestURI == "/api/login/oauth/access_token" {
ctx.Output.Header(headerAllowOrigin, origin)
ctx.Output.Header(headerAllowMethods, "POST, GET, OPTIONS, DELETE")
ctx.Output.Header(headerAllowHeaders, "Content-Type, Authorization")
return
}
if origin != "" && originConf != "" && origin != originConf {
ok, err := object.IsOriginAllowed(origin)
if err != nil {

View File

@@ -19,7 +19,6 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"strings"
"time"
@@ -55,7 +54,7 @@ func StaticFilter(ctx *context.Context) {
path += urlPath
}
path2 := strings.TrimPrefix(path, "web/build/images/")
path2 := strings.TrimLeft(path, "web/build/images/")
if util.FileExist(path2) {
makeGzipResponse(ctx.ResponseWriter, ctx.Request, path2)
return
@@ -73,7 +72,7 @@ func StaticFilter(ctx *context.Context) {
}
func serveFileWithReplace(w http.ResponseWriter, r *http.Request, name string, old string, new string) {
f, err := os.Open(filepath.Clean(name))
f, err := os.Open(name)
if err != nil {
panic(err)
}

View File

@@ -70,7 +70,7 @@ func (fileSystem FileSystem) Put(path string, reader io.Reader) (*oss.Object, er
return nil, err
}
dst, err := os.Create(filepath.Clean(fullPath))
dst, err := os.Create(fullPath)
if err == nil {
if seeker, ok := reader.(io.ReadSeeker); ok {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -72,9 +72,6 @@ func UrlJoin(base string, path string) string {
func GetUrlPath(urlString string) string {
u, _ := url.Parse(urlString)
if u == nil {
return ""
}
return u.Path
}

View File

@@ -26,32 +26,11 @@ func DeleteVal(values []string, val string) []string {
return newValues
}
func ReplaceVal(values []string, oldVal string, newVal string) []string {
newValues := []string{}
for _, v := range values {
if v == oldVal {
newValues = append(newValues, newVal)
} else {
newValues = append(newValues, v)
}
}
return newValues
}
func ContainsString(values []string, val string) bool {
sort.Strings(values)
return sort.SearchStrings(values, val) != len(values)
}
func InSlice(slice []string, elem string) bool {
for _, val := range slice {
if val == elem {
return true
}
}
return false
}
func ReturnAnyNotEmpty(strs ...string) string {
for _, str := range strs {
if str != "" {

View File

@@ -22,7 +22,6 @@ import (
"fmt"
"math/rand"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@@ -202,7 +201,7 @@ func GetMinLenStr(strs ...string) string {
}
func ReadStringFromPath(path string) string {
data, err := os.ReadFile(filepath.Clean(path))
data, err := os.ReadFile(path)
if err != nil {
panic(err)
}
@@ -289,18 +288,3 @@ func HasString(strs []string, str string) bool {
}
return false
}
func ParseIdToString(input interface{}) (string, error) {
switch v := input.(type) {
case string:
return v, nil
case int:
return strconv.Itoa(v), nil
case int64:
return strconv.FormatInt(v, 10), nil
case float64:
return strconv.FormatFloat(v, 'f', -1, 64), nil
default:
return "", fmt.Errorf("unsupported id type: %T", input)
}
}

View File

@@ -246,23 +246,3 @@ func TestSnakeString(t *testing.T) {
})
}
}
func TestParseId(t *testing.T) {
scenarios := []struct {
description string
input interface{}
expected interface{}
}{
{"Should be return 123456", "123456", "123456"},
{"Should be return 123456", 123456, "123456"},
{"Should be return 123456", int64(123456), "123456"},
{"Should be return 123456", float64(123456), "123456"},
}
for _, scenery := range scenarios {
t.Run(scenery.description, func(t *testing.T) {
actual, err := ParseIdToString(scenery.input)
assert.Nil(t, err, "The returned value not is expected")
assert.Equal(t, scenery.expected, actual, "The returned value not is expected")
})
}
}

View File

@@ -18,7 +18,6 @@ import (
"bufio"
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strconv"
@@ -156,7 +155,7 @@ func GetVersionInfoFromFile() (*VersionInfo, error) {
_, filename, _, _ := runtime.Caller(0)
rootPath := path.Dir(path.Dir(filename))
file, err := os.Open(filepath.Clean(path.Join(rootPath, "version_info.txt")))
file, err := os.Open(path.Join(rootPath, "version_info.txt"))
if err != nil {
return res, err
}

View File

@@ -22,18 +22,10 @@ import (
"github.com/nyaruka/phonenumbers"
)
var (
rePhone *regexp.Regexp
ReWhiteSpace *regexp.Regexp
ReFieldWhiteList *regexp.Regexp
ReUserName *regexp.Regexp
)
var rePhone *regexp.Regexp
func init() {
rePhone, _ = regexp.Compile(`(\d{3})\d*(\d{4})`)
ReWhiteSpace, _ = regexp.Compile(`\s`)
ReFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
ReUserName, _ = regexp.Compile("^[a-zA-Z0-9]+((?:-[a-zA-Z0-9]+)|(?:_[a-zA-Z0-9]+))*$")
}
func IsEmailValid(email string) bool {
@@ -78,7 +70,3 @@ func GetCountryCode(prefix string, phone string) (string, error) {
return countryCode, nil
}
func FilterField(field string) bool {
return ReFieldWhiteList.MatchString(field)
}

View File

@@ -50,32 +50,4 @@ module.exports = {
},
},
],
webpack: {
// use polyfill Buffer with Webpack 5
// https://viglucci.io/articles/how-to-polyfill-buffer-with-webpack-5
// https://craco.js.org/docs/configuration/webpack/
configure: (webpackConfig, { env, paths }) => {
webpackConfig.resolve.fallback = {
// "process": require.resolve('process/browser'),
// "util": require.resolve("util/"),
// "url": require.resolve("url/"),
// "zlib": require.resolve("browserify-zlib"),
// "stream": require.resolve("stream-browserify"),
// "http": require.resolve("stream-http"),
// "https": require.resolve("https-browserify"),
// "assert": require.resolve("assert/"),
"buffer": require.resolve('buffer/'),
"process": false,
"util": false,
"url": false,
"zlib": false,
"stream": false,
"http": false,
"https": false,
"assert": false,
"buffer": false,
};
return webpackConfig;
},
}
};

View File

@@ -9,13 +9,11 @@
"@crowdin/cli": "^3.7.10",
"@ctrl/tinycolor": "^3.5.0",
"@emotion/react": "^11.10.5",
"@metamask/eth-sig-util": "^6.0.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"antd": "5.2.3",
"antd-token-previewer": "^1.1.0-22",
"buffer": "^6.0.3",
"codemirror": "^5.61.1",
"copy-to-clipboard": "^3.3.1",
"core-js": "^3.25.0",
@@ -26,6 +24,8 @@
"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",
"react-codemirror2": "^7.2.1",
@@ -36,7 +36,6 @@
"react-helmet": "^6.1.0",
"react-highlight-words": "^0.18.0",
"react-i18next": "^11.8.7",
"react-metamask-avatar": "^1.2.1",
"react-router-dom": "^5.3.3",
"react-scripts": "5.0.1",
"react-social-login-buttons": "^3.4.0"

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