Compare commits

...

7 Commits

Author SHA1 Message Date
524cf4dda5 feat: fix update application failed for permissions with the same name (#2579)
* fixed: update application failed where have two same permission in different organization

* Update application.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2024-01-05 20:45:55 +08:00
077a1cb8b7 fix: support owner parameter in enforce API (#2578) 2024-01-05 15:12:59 +08:00
00efdf1d03 Fix EmailVerified in UserInfo() 2024-01-05 09:37:42 +08:00
aa543f1abb feat: more RFC like LDAP server behaviour (#2574)
* feat: more RFC like LDAP server behaviour

* Extend FieldRelationMap to support case insensitive mapping, add more fields definition

* feat: Add group syncing for LDAP server
2024-01-05 09:24:12 +08:00
1d1d3049bd feat: fix dropped getAffiliationMap error in object (#2576) 2024-01-05 09:03:39 +08:00
4f497d44a5 Enable at least password login in extendApplicationWithSigninMethods() 2024-01-03 22:19:43 +08:00
369de36987 feat: add users with correct application (#2570) 2024-01-02 23:49:04 +08:00
9 changed files with 466 additions and 84 deletions

View File

@ -30,6 +30,7 @@ import (
// @Param permissionId query string false "permission id"
// @Param modelId query string false "model id"
// @Param resourceId query string false "resource id"
// @Param owner query string false "owner"
// @Success 200 {object} controllers.Response The Response object
// @router /enforce [post]
func (c *ApiController) Enforce() {
@ -37,6 +38,7 @@ func (c *ApiController) Enforce() {
modelId := c.Input().Get("modelId")
resourceId := c.Input().Get("resourceId")
enforcerId := c.Input().Get("enforcerId")
owner := c.Input().Get("owner")
if len(c.Ctx.Input.RequestBody) == 0 {
c.ResponseError("The request body should not be empty")
@ -117,6 +119,8 @@ func (c *ApiController) Enforce() {
c.ResponseError(err.Error())
return
}
} else if owner != "" {
permissions, err = object.GetPermissions(owner)
} else {
c.ResponseError(c.T("general:Missing parameter"))
return
@ -152,12 +156,14 @@ func (c *ApiController) Enforce() {
// @Param body body []string true "array of casbin requests"
// @Param permissionId query string false "permission id"
// @Param modelId query string false "model id"
// @Param owner query string false "owner"
// @Success 200 {object} controllers.Response The Response object
// @router /batch-enforce [post]
func (c *ApiController) BatchEnforce() {
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
enforcerId := c.Input().Get("enforcerId")
owner := c.Input().Get("owner")
var requests [][]string
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
@ -227,6 +233,8 @@ func (c *ApiController) BatchEnforce() {
c.ResponseError(err.Error())
return
}
} else if owner != "" {
permissions, err = object.GetPermissions(owner)
} else {
c.ResponseError(c.T("general:Missing parameter"))
return

View File

@ -18,6 +18,7 @@ import (
"fmt"
"hash/fnv"
"log"
"strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
@ -49,7 +50,17 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
res := ldap.NewBindResponse(ldap.LDAPResultSuccess)
if r.AuthenticationChoice() == "simple" {
bindUsername, bindOrg, err := getNameAndOrgFromDN(string(r.Name()))
bindDN := string(r.Name())
bindPassword := string(r.AuthenticationSimple())
if bindDN == "" && bindPassword == "" {
res.SetResultCode(ldap.LDAPResultInappropriateAuthentication)
res.SetDiagnosticMessage("Anonymous bind disallowed")
w.Write(res)
return
}
bindUsername, bindOrg, err := getNameAndOrgFromDN(bindDN)
if err != nil {
log.Printf("getNameAndOrgFromDN() error: %s", err.Error())
res.SetResultCode(ldap.LDAPResultInvalidDNSyntax)
@ -58,7 +69,6 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
return
}
bindPassword := string(r.AuthenticationSimple())
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
if err != nil {
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
@ -93,7 +103,46 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
}
r := m.GetSearchRequest()
if r.FilterString() == "(objectClass=*)" {
// case insensitive match
if strings.EqualFold(r.FilterString(), "(objectClass=*)") {
if len(r.Attributes()) == 0 {
w.Write(res)
return
}
first_attr := string(r.Attributes()[0])
if string(r.BaseObject()) == "" {
// handle special search requests
if first_attr == "namingContexts" {
orgs, code := GetFilteredOrganizations(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
e := ldap.NewSearchResultEntry(string(r.BaseObject()))
dnlist := make([]message.AttributeValue, len(orgs))
for i, org := range orgs {
dnlist[i] = message.AttributeValue(fmt.Sprintf("ou=%s", org.Name))
}
e.AddAttribute("namingContexts", dnlist...)
w.Write(e)
} else if first_attr == "subschemaSubentry" {
e := ldap.NewSearchResultEntry(string(r.BaseObject()))
e.AddAttribute("subschemaSubentry", message.AttributeValue("cn=Subschema"))
w.Write(e)
}
} else if strings.EqualFold(first_attr, "objectclasses") && string(r.BaseObject()) == "cn=Subschema" {
e := ldap.NewSearchResultEntry(string(r.BaseObject()))
e.AddAttribute("objectClasses", []message.AttributeValue{
"( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction of an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ gecos $ description ) )",
"( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction of a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPassword $ memberUid $ description ) )",
}...)
w.Write(e)
}
w.Write(res)
return
}
@ -106,38 +155,72 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
default:
}
users, code := GetFilteredUsers(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
for _, user := range users {
dn := fmt.Sprintf("uid=%s,cn=%s,%s", user.Id, user.Name, string(r.BaseObject()))
e := ldap.NewSearchResultEntry(dn)
uidNumberStr := fmt.Sprintf("%v", hash(user.Name))
e.AddAttribute("uidNumber", message.AttributeValue(uidNumberStr))
e.AddAttribute("gidNumber", message.AttributeValue(uidNumberStr))
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
e.AddAttribute("cn", message.AttributeValue(user.Name))
e.AddAttribute("uid", message.AttributeValue(user.Id))
attrs := r.Attributes()
for _, attr := range attrs {
if string(attr) == "*" {
attrs = AdditionalLdapAttributes
break
}
}
for _, attr := range attrs {
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
if string(attr) == "cn" {
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
}
objectClass := searchFilterForEquality(r.Filter(), "objectClass", "posixAccount", "posixGroup")
switch objectClass {
case "posixAccount":
users, code := GetFilteredUsers(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
w.Write(e)
// log.Printf("Handling posixAccount filter=%s", r.FilterString())
for _, user := range users {
dn := fmt.Sprintf("uid=%s,cn=users,%s", user.Name, string(r.BaseObject()))
e := ldap.NewSearchResultEntry(dn)
attrs := r.Attributes()
for _, attr := range attrs {
if string(attr) == "*" {
attrs = AdditionalLdapUserAttributes
break
}
}
for _, attr := range attrs {
if strings.HasSuffix(string(attr), ";binary") {
// unsupported: userCertificate;binary
continue
}
field, ok := ldapUserAttributesMapping.CaseInsensitiveGet(string(attr))
if ok {
e.AddAttribute(message.AttributeDescription(attr), field.GetAttributeValues(user)...)
}
}
w.Write(e)
}
case "posixGroup":
// log.Printf("Handling posixGroup filter=%s", r.FilterString())
groups, code := GetFilteredGroups(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
for _, group := range groups {
dn := fmt.Sprintf("cn=%s,cn=groups,%s", group.Name, string(r.BaseObject()))
e := ldap.NewSearchResultEntry(dn)
attrs := r.Attributes()
for _, attr := range attrs {
if string(attr) == "*" {
attrs = AdditionalLdapGroupAttributes
break
}
}
for _, attr := range attrs {
field, ok := ldapGroupAttributesMapping.CaseInsensitiveGet(string(attr))
if ok {
e.AddAttribute(message.AttributeDescription(attr), field.GetAttributeValues(group)...)
}
}
w.Write(e)
}
case "":
log.Printf("Unmatched search request. filter=%s", r.FilterString())
}
w.Write(res)
}

View File

@ -18,6 +18,7 @@ import (
"fmt"
"log"
"strings"
"time"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
@ -28,65 +29,259 @@ import (
"github.com/xorm-io/builder"
)
type AttributeMapper func(user *object.User) message.AttributeValue
type V = message.AttributeValue
type FieldRelation struct {
type UserAttributeMapper func(user *object.User) []V
type UserFieldRelation struct {
userField string
ldapField string
notSearchable bool
hideOnStarOp bool
fieldMapper AttributeMapper
fieldMapper UserAttributeMapper
constantValue []V
}
func (rel FieldRelation) GetField() (string, error) {
func (rel UserFieldRelation) GetField() (string, error) {
if rel.notSearchable {
return "", fmt.Errorf("attribute %s not supported", rel.userField)
}
return rel.userField, nil
}
func (rel FieldRelation) GetAttributeValue(user *object.User) message.AttributeValue {
func (rel UserFieldRelation) GetAttributeValues(user *object.User) []V {
if rel.constantValue != nil && rel.fieldMapper == nil {
return rel.constantValue
}
return rel.fieldMapper(user)
}
var ldapAttributesMapping = map[string]FieldRelation{
"cn": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Name)
}},
"uid": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Name)
}},
"displayname": {userField: "displayName", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.DisplayName)
}},
"email": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Email)
}},
"mail": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Email)
}},
"mobile": {userField: "phone", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Phone)
}},
"title": {userField: "tag", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Tag)
}},
"userPassword": {
userField: "userPassword",
notSearchable: true,
fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(getUserPasswordWithType(user))
},
},
type UserFieldRelationMap map[string]UserFieldRelation
func (m UserFieldRelationMap) CaseInsensitiveGet(key string) (UserFieldRelation, bool) {
lowerKey := strings.ToLower(key)
ret, ok := m[lowerKey]
return ret, ok
}
var AdditionalLdapAttributes []message.LDAPString
type GroupAttributeMapper func(group *object.Group) []V
type GroupFieldRelation struct {
groupField string
ldapField string
notSearchable bool
hideOnStarOp bool
fieldMapper GroupAttributeMapper
constantValue []V
}
func (rel GroupFieldRelation) GetField() (string, error) {
if rel.notSearchable {
return "", fmt.Errorf("attribute %s not supported", rel.groupField)
}
return rel.groupField, nil
}
func (rel GroupFieldRelation) GetAttributeValues(group *object.Group) []V {
if rel.constantValue != nil && rel.fieldMapper == nil {
return rel.constantValue
}
return rel.fieldMapper(group)
}
type GroupFieldRelationMap map[string]GroupFieldRelation
func (m GroupFieldRelationMap) CaseInsensitiveGet(key string) (GroupFieldRelation, bool) {
lowerKey := strings.ToLower(key)
ret, ok := m[lowerKey]
return ret, ok
}
var ldapUserAttributesMapping = UserFieldRelationMap{
"cn": {ldapField: "cn", userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) []V {
return []V{V(user.Name)}
}},
"uid": {ldapField: "uid", userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) []V {
return []V{V(user.Name)}
}},
"displayname": {ldapField: "displayName", userField: "displayName", fieldMapper: func(user *object.User) []V {
return []V{V(user.DisplayName)}
}},
"email": {ldapField: "email", userField: "email", fieldMapper: func(user *object.User) []V {
return []V{V(user.Email)}
}},
"mail": {ldapField: "mail", userField: "email", fieldMapper: func(user *object.User) []V {
return []V{V(user.Email)}
}},
"mobile": {ldapField: "mobile", userField: "phone", fieldMapper: func(user *object.User) []V {
return []V{V(user.Phone)}
}},
"telephonenumber": {ldapField: "telephoneNumber", userField: "phone", fieldMapper: func(user *object.User) []V {
return []V{V(user.Phone)}
}},
"postaladdress": {ldapField: "postalAddress", userField: "address", fieldMapper: func(user *object.User) []V {
return []V{V(strings.Join(user.Address, " "))}
}},
"title": {ldapField: "title", userField: "title", fieldMapper: func(user *object.User) []V {
return []V{V(user.Title)}
}},
"gecos": {ldapField: "gecos", userField: "displayName", fieldMapper: func(user *object.User) []V {
return []V{V(user.DisplayName)}
}},
"description": {ldapField: "description", userField: "displayName", fieldMapper: func(user *object.User) []V {
return []V{V(user.DisplayName)}
}},
"logindisabled": {ldapField: "loginDisabled", userField: "isForbidden", fieldMapper: func(user *object.User) []V {
if user.IsForbidden {
return []V{V("1")}
} else {
return []V{V("0")}
}
}},
"userpassword": {
ldapField: "userPassword",
userField: "userPassword",
notSearchable: true,
fieldMapper: func(user *object.User) []V {
return []V{V(getUserPasswordWithType(user))}
},
},
"uidnumber": {ldapField: "uidNumber", notSearchable: true, fieldMapper: func(user *object.User) []V {
return []V{V(fmt.Sprintf("%v", hash(user.Name)))}
}},
"gidnumber": {ldapField: "gidNumber", notSearchable: true, fieldMapper: func(user *object.User) []V {
if len(user.Groups) == 0 {
return []V{V("")}
}
group, err := object.GetGroup(user.Groups[0])
if err != nil {
log.Printf("gidnumber object.GetGroup error: %s", err)
return []V{V("")}
}
return []V{V(fmt.Sprintf("%v", hash(group.Name)))}
}},
"homedirectory": {ldapField: "homeDirectory", notSearchable: true, fieldMapper: func(user *object.User) []V {
return []V{V("/home/" + user.Name)}
}},
"loginshell": {ldapField: "loginShell", notSearchable: true, fieldMapper: func(user *object.User) []V {
if user.IsForbidden || user.IsDeleted {
return []V{V("/sbin/nologin")}
} else {
return []V{V("/bin/bash")}
}
}},
"shadowlastchange": {ldapField: "shadowLastChange", notSearchable: true, fieldMapper: func(user *object.User) []V {
// "this attribute specifies number of days between January 1, 1970, and the date that the password was last modified"
updatedTime, err := time.Parse(time.RFC3339, user.UpdatedTime)
if err != nil {
log.Printf("shadowlastchange time.Parse error: %s", err)
updatedTime = time.Now()
}
return []V{V(fmt.Sprint(updatedTime.Unix() / 86400))}
}},
"pwdchangedtime": {ldapField: "pwdChangedTime", notSearchable: true, fieldMapper: func(user *object.User) []V {
updatedTime, err := time.Parse(time.RFC3339, user.UpdatedTime)
if err != nil {
log.Printf("pwdchangedtime time.Parse error: %s", err)
updatedTime = time.Now()
}
return []V{V(updatedTime.UTC().Format("20060102030405Z"))}
}},
"shadowmin": {ldapField: "shadowMin", notSearchable: true, constantValue: []V{V("0")}},
"shadowmax": {ldapField: "shadowMax", notSearchable: true, constantValue: []V{V("99999")}},
"shadowwarning": {ldapField: "shadowWarning", notSearchable: true, constantValue: []V{V("7")}},
"shadowexpire": {ldapField: "shadowExpire", notSearchable: true, fieldMapper: func(user *object.User) []V {
if user.IsForbidden {
return []V{V("1")}
} else {
return []V{V("-1")}
}
}},
"shadowinactive": {ldapField: "shadowInactive", notSearchable: true, constantValue: []V{V("0")}},
"shadowflag": {ldapField: "shadowFlag", notSearchable: true, constantValue: []V{V("0")}},
"memberof": {ldapField: "memberOf", notSearchable: true, fieldMapper: func(user *object.User) []V {
var groupdn []V
for _, groupId := range user.Groups {
group, err := object.GetGroup(groupId)
if err != nil {
log.Printf("memberOf object.GetGroup error: %s", err)
continue
}
groupdn = append(groupdn, V(fmt.Sprintf("cn=%s,cn=groups,ou=%s", group.Name, group.Owner)))
}
return groupdn
}},
"objectclass": {ldapField: "objectClass", notSearchable: true, constantValue: []V{
V("top"),
V("posixAccount"),
V("shadowAccount"),
V("person"),
V("organizationalPerson"),
V("inetOrgPerson"),
V("apple-user"),
V("sambaSamAccount"),
V("sambaIdmapEntry"),
V("extensibleObject"),
}},
}
var ldapGroupAttributesMapping = GroupFieldRelationMap{
"cn": {ldapField: "cn", hideOnStarOp: true, fieldMapper: func(group *object.Group) []V {
return []V{V(group.Name)}
}},
"gidnumber": {ldapField: "gidNumber", hideOnStarOp: true, fieldMapper: func(group *object.Group) []V {
return []V{V(fmt.Sprintf("%v", hash(group.Name)))}
}},
"member": {ldapField: "member", fieldMapper: func(group *object.Group) []V {
users, err := object.GetGroupUsers(group.GetId())
if err != nil {
log.Printf("member object.GetGroupUsers error: %s", err)
return []V{V("")}
}
var members []V
for _, user := range users {
members = append(members, V(fmt.Sprintf("uid=%s,cn=users,ou=%s", user.Name, user.Owner)))
}
return members
}},
"memberuid": {ldapField: "memberUid", fieldMapper: func(group *object.Group) []V {
users, err := object.GetGroupUsers(group.GetId())
if err != nil {
log.Printf("member object.GetGroupUsers error: %s", err)
return []V{V("")}
}
var members []message.AttributeValue
for _, user := range users {
members = append(members, message.AttributeValue(user.Name))
}
return members
}},
"description": {ldapField: "description", hideOnStarOp: true, fieldMapper: func(group *object.Group) []V {
return []V{V(group.DisplayName)}
}},
"objectclass": {ldapField: "objectClass", hideOnStarOp: true, constantValue: []V{
V("top"),
V("posixGroup"),
}},
}
var (
AdditionalLdapUserAttributes []message.LDAPString
AdditionalLdapGroupAttributes []message.LDAPString
)
func init() {
for k, v := range ldapAttributesMapping {
for _, v := range ldapUserAttributesMapping {
if v.hideOnStarOp {
continue
}
AdditionalLdapAttributes = append(AdditionalLdapAttributes, message.LDAPString(k))
AdditionalLdapUserAttributes = append(AdditionalLdapUserAttributes, message.LDAPString(v.ldapField))
}
for _, v := range ldapGroupAttributesMapping {
if v.hideOnStarOp {
continue
}
AdditionalLdapGroupAttributes = append(AdditionalLdapGroupAttributes, message.LDAPString(v.ldapField))
}
}
@ -307,6 +502,52 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
}
}
func GetFilteredOrganizations(m *ldap.Message) ([]*object.Organization, int) {
if m.Client.IsGlobalAdmin {
organizations, err := object.GetOrganizations("")
if err != nil {
panic(err)
}
return organizations, ldap.LDAPResultSuccess
} else if m.Client.IsOrgAdmin {
requestUserId := util.GetId(m.Client.OrgName, m.Client.UserName)
user, err := object.GetUser(requestUserId)
if err != nil {
panic(err)
}
organization, err := object.GetOrganizationByUser(user)
if err != nil {
panic(err)
}
return []*object.Organization{organization}, ldap.LDAPResultSuccess
} else {
return nil, ldap.LDAPResultInsufficientAccessRights
}
}
func GetFilteredGroups(m *ldap.Message) ([]*object.Group, int) {
if m.Client.IsGlobalAdmin {
groups, err := object.GetGroups("")
if err != nil {
panic(err)
}
return groups, ldap.LDAPResultSuccess
} else if m.Client.IsOrgAdmin {
requestUserId := util.GetId(m.Client.OrgName, m.Client.UserName)
user, err := object.GetUser(requestUserId)
if err != nil {
panic(err)
}
groups, err := object.GetGroups(user.Owner)
if err != nil {
panic(err)
}
return groups, ldap.LDAPResultSuccess
} else {
return nil, ldap.LDAPResultInsufficientAccessRights
}
}
// get user password with hash type prefix
// TODO not handle salt yet
// @return {md5}5f4dcc3b5aa765d61d8327deb882cf99
@ -330,18 +571,49 @@ func getUserPasswordWithType(user *object.User) string {
return fmt.Sprintf("{%s}%s", prefix, user.Password)
}
func getAttribute(attributeName string, user *object.User) message.AttributeValue {
v, ok := ldapAttributesMapping[attributeName]
if !ok {
return ""
}
return v.GetAttributeValue(user)
}
func getUserFieldFromAttribute(attributeName string) (string, error) {
v, ok := ldapAttributesMapping[attributeName]
v, ok := ldapUserAttributesMapping.CaseInsensitiveGet(attributeName)
if !ok {
return "", fmt.Errorf("attribute %s not supported", attributeName)
}
return v.GetField()
}
func searchFilterForEquality(filter message.Filter, desc string, values ...string) string {
switch f := filter.(type) {
case message.FilterAnd:
for _, child := range f {
if val := searchFilterForEquality(child, desc, values...); val != "" {
return val
}
}
case message.FilterOr:
for _, child := range f {
if val := searchFilterForEquality(child, desc, values...); val != "" {
return val
}
}
case message.FilterNot:
return searchFilterForEquality(f.Filter, desc, values...)
case message.FilterSubstrings:
// Handle FilterSubstrings case if needed
case message.FilterEqualityMatch:
if strings.EqualFold(string(f.AttributeDesc()), desc) {
for _, value := range values {
if val := string(f.AssertionValue()); val == value {
return val
}
}
}
case message.FilterGreaterOrEqual:
// Handle FilterGreaterOrEqual case if needed
case message.FilterLessOrEqual:
// Handle FilterLessOrEqual case if needed
case message.FilterPresent:
// Handle FilterPresent case if needed
case message.FilterApproxMatch:
// Handle FilterApproxMatch case if needed
}
return ""
}

View File

@ -214,6 +214,11 @@ func extendApplicationWithSigninMethods(application *Application) (err error) {
}
}
if len(application.SigninMethods) == 0 {
signinMethod := &SigninMethod{Name: "Password", DisplayName: "Password", Rule: "None"}
application.SigninMethods = append(application.SigninMethods, signinMethod)
}
return
}
@ -659,7 +664,7 @@ func applicationChangeTrigger(oldName string, newName string) error {
}
}
permissions[i].Resources = permissionResoureces
_, err = session.Where("name=?", permissions[i].Name).Update(permissions[i])
_, err = session.Where("owner=?", permissions[i].Owner).Where("name=?", permissions[i].Name).Update(permissions[i])
if err != nil {
return err
}

View File

@ -54,6 +54,15 @@ func (syncer *Syncer) syncUsers() error {
var affiliationMap map[int]string
if syncer.AffiliationTable != "" {
_, affiliationMap, err = syncer.getAffiliationMap()
if err != nil {
line := fmt.Sprintf("[%s] %s\n", util.GetCurrentTime(), err.Error())
_, err2 := updateSyncerErrorText(syncer, line)
if err2 != nil {
panic(err2)
}
return err
}
}
key := syncer.getKey()

View File

@ -867,7 +867,8 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
}
if strings.Contains(scope, "email") {
resp.Email = user.Email
resp.EmailVerified = user.EmailVerified
// resp.EmailVerified = user.EmailVerified
resp.EmailVerified = true
}
if strings.Contains(scope, "address") {
resp.Address = user.Location

View File

@ -392,7 +392,7 @@ class App extends Component {
</div>
</Tooltip>
<OpenTour />
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() &&
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() && (this.state.uri.indexOf("/trees") === -1) &&
<OrganizationSelect
initValue={Setting.getOrganization()}
withAll={true}

View File

@ -25,6 +25,7 @@ class BaseListPage extends React.Component {
super(props);
this.state = {
classes: props,
organizationName: this.props.match?.params.organizationName || Setting.getRequestOrganization(this.props.account),
data: [],
pagination: {
current: 1,
@ -39,6 +40,10 @@ class BaseListPage extends React.Component {
}
handleOrganizationChange = () => {
this.setState({
organizationName: this.props.match?.params.organizationName || Setting.getRequestOrganization(this.props.account),
});
const {pagination} = this.state;
this.fetch({pagination});
};

View File

@ -30,7 +30,6 @@ class UserListPage extends BaseListPage {
super(props);
this.state = {
...this.state,
organizationName: this.props.organizationName ?? this.props.match?.params.organizationName ?? this.props.account.owner,
organization: null,
};
}
@ -62,7 +61,7 @@ class UserListPage extends BaseListPage {
newUser() {
const randomName = Setting.getRandomName();
const owner = Setting.isDefaultOrganizationSelected(this.props.account) ? this.state.organizationName : Setting.getRequestOrganization(this.props.account);
const owner = (Setting.isDefaultOrganizationSelected(this.props.account) || this.props.groupName) ? this.state.organizationName : Setting.getRequestOrganization(this.props.account);
return {
owner: owner,
name: `user_${randomName}`,
@ -85,7 +84,7 @@ class UserListPage extends BaseListPage {
score: this.state.organization.initScore,
isDeleted: false,
properties: {},
signupApplication: "app-built-in",
signupApplication: this.state.organization.defaultApplication,
};
}