mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-10 15:27:48 +08:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
941c56e69e | ||
![]() |
a28b871a46 | ||
![]() |
387f5d58f7 | ||
![]() |
7d846b2060 |
@@ -159,6 +159,11 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(r.FilterString(), "(objectClass=*)") && (string(r.BaseObject()) == "" || strings.EqualFold(string(r.BaseObject()), "cn=Subschema")) {
|
||||||
|
handleRootSearch(w, &r, &res, m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var isGroupSearch bool = false
|
var isGroupSearch bool = false
|
||||||
filter := r.Filter()
|
filter := r.Filter()
|
||||||
if eq, ok := filter.(message.FilterEqualityMatch); ok && strings.EqualFold(string(eq.AttributeDesc()), "objectClass") && strings.EqualFold(string(eq.AssertionValue()), "posixGroup") {
|
if eq, ok := filter.(message.FilterEqualityMatch); ok && strings.EqualFold(string(eq.AttributeDesc()), "objectClass") && strings.EqualFold(string(eq.AssertionValue()), "posixGroup") {
|
||||||
@@ -229,6 +234,46 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
w.Write(res)
|
w.Write(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleRootSearch(w ldap.ResponseWriter, r *message.SearchRequest, res *message.SearchResultDone, m *ldap.Message) {
|
||||||
|
if len(r.Attributes()) == 0 {
|
||||||
|
w.Write(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
firstAttr := string(r.Attributes()[0])
|
||||||
|
|
||||||
|
if string(r.BaseObject()) == "" {
|
||||||
|
// Handle special root DSE requests
|
||||||
|
if strings.EqualFold(firstAttr, "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 strings.EqualFold(firstAttr, "subschemaSubentry") {
|
||||||
|
e := ldap.NewSearchResultEntry(string(r.BaseObject()))
|
||||||
|
e.AddAttribute("subschemaSubentry", message.AttributeValue("cn=Subschema"))
|
||||||
|
w.Write(e)
|
||||||
|
}
|
||||||
|
} else if strings.EqualFold(firstAttr, "objectclasses") && strings.EqualFold(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)
|
||||||
|
}
|
||||||
|
|
||||||
func hash(s string) uint32 {
|
func hash(s string) uint32 {
|
||||||
h := fnv.New32a()
|
h := fnv.New32a()
|
||||||
h.Write([]byte(s))
|
h.Write([]byte(s))
|
||||||
|
23
ldap/util.go
23
ldap/util.go
@@ -358,6 +358,29 @@ func GetFilteredGroups(m *ldap.Message, baseDN string, filterStr string) ([]*obj
|
|||||||
return groups, ldap.LDAPResultSuccess
|
return groups, ldap.LDAPResultSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get user password with hash type prefix
|
// get user password with hash type prefix
|
||||||
// TODO not handle salt yet
|
// TODO not handle salt yet
|
||||||
// @return {md5}5f4dcc3b5aa765d61d8327deb882cf99
|
// @return {md5}5f4dcc3b5aa765d61d8327deb882cf99
|
||||||
|
@@ -20,6 +20,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
@@ -341,10 +342,31 @@ func getClaimsCustom(claims Claims, tokenField []string) jwt.MapClaims {
|
|||||||
res["provider"] = claims.Provider
|
res["provider"] = claims.Provider
|
||||||
|
|
||||||
for _, field := range tokenField {
|
for _, field := range tokenField {
|
||||||
userField := userValue.FieldByName(field)
|
if strings.HasPrefix(field, "Properties") {
|
||||||
if userField.IsValid() {
|
/*
|
||||||
newfield := util.SnakeToCamel(util.CamelToSnakeCase(field))
|
Use selected properties fields as custom claims.
|
||||||
res[newfield] = userField.Interface()
|
Converts `Properties.my_field` to custom claim with name `my_field`.
|
||||||
|
*/
|
||||||
|
parts := strings.Split(field, ".")
|
||||||
|
if len(parts) != 2 || parts[0] != "Properties" { // Either too many segments, or not properly scoped to `Properties`, so skip.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
base, fieldName := parts[0], parts[1]
|
||||||
|
mField := userValue.FieldByName(base)
|
||||||
|
if !mField.IsValid() { // Can't find `Properties` field, so skip.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
finalField := mField.MapIndex(reflect.ValueOf(fieldName))
|
||||||
|
if finalField.IsValid() { // // Provided field within `Properties` exists, add claim.
|
||||||
|
res[fieldName] = finalField.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // Use selected user field as claims.
|
||||||
|
userField := userValue.FieldByName(field)
|
||||||
|
if userField.IsValid() {
|
||||||
|
newfield := util.SnakeToCamel(util.CamelToSnakeCase(field))
|
||||||
|
res[newfield] = userField.Interface()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +403,14 @@ func generateJwtToken(application *Application, user *User, provider string, non
|
|||||||
refreshExpireTime = expireTime
|
refreshExpireTime = expireTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.GetConfigBool("useGroupPathInToken") {
|
||||||
|
groupPath, err := user.GetUserFullGroupPath()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Groups = groupPath
|
||||||
|
}
|
||||||
user = refineUser(user)
|
user = refineUser(user)
|
||||||
|
|
||||||
_, originBackend := getOriginFromHost(host)
|
_, originBackend := getOriginFromHost(host)
|
||||||
|
@@ -1331,6 +1331,56 @@ func (user *User) CheckUserFace(faceIdImage []string, provider *Provider) (bool,
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) GetUserFullGroupPath() ([]string, error) {
|
||||||
|
if len(user.Groups) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgGroups []*Group
|
||||||
|
orgGroups, err := GetGroups(user.Owner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
groupMap := make(map[string]Group)
|
||||||
|
for _, group := range orgGroups {
|
||||||
|
groupMap[group.Name] = *group
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupFullPath []string
|
||||||
|
|
||||||
|
for _, groupId := range user.Groups {
|
||||||
|
_, groupName := util.GetOwnerAndNameFromIdNoCheck(groupId)
|
||||||
|
group, ok := groupMap[groupName]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
groupPath := groupName
|
||||||
|
|
||||||
|
curGroup, ok := groupMap[group.ParentId]
|
||||||
|
if !ok {
|
||||||
|
return []string{}, fmt.Errorf("group:Group %s not exist", group.ParentId)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
groupPath = util.GetId(curGroup.Name, groupPath)
|
||||||
|
if curGroup.IsTopGroup {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
curGroup, ok = groupMap[curGroup.ParentId]
|
||||||
|
if !ok {
|
||||||
|
return []string{}, fmt.Errorf("group:Group %s not exist", curGroup.ParentId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupPath = util.GetId(curGroup.Owner, groupPath)
|
||||||
|
groupFullPath = append(groupFullPath, groupPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupFullPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateIdForNewUser(application *Application) (string, error) {
|
func GenerateIdForNewUser(application *Application) (string, error) {
|
||||||
if application == nil || application.GetSignupItemRule("ID") != "Incremental" {
|
if application == nil || application.GetSignupItemRule("ID") != "Incremental" {
|
||||||
return util.GenerateId(), nil
|
return util.GenerateId(), nil
|
||||||
|
@@ -88,7 +88,10 @@ class ProviderTable extends React.Component {
|
|||||||
}
|
}
|
||||||
}} >
|
}} >
|
||||||
{
|
{
|
||||||
Setting.getDeduplicatedArray(this.props.providers, table, "name").map((provider, index) => <Option key={index} value={provider.name}>{provider.name}</Option>)
|
Setting.getDeduplicatedArray(this.props.providers, table, "name").filter(provider => provider.category !== "Captcha" || !table.some(tableItem => {
|
||||||
|
const existingProvider = Setting.getArrayItem(this.props.providers, "name", tableItem.name);
|
||||||
|
return existingProvider && existingProvider.category === "Captcha";
|
||||||
|
})).map((provider, index) => <Option key={index} value={provider.name}>{provider.name}</Option>)
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user