Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 67 additions & 10 deletions cmd/iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,10 +912,17 @@ func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) {
return nil, errServerNotInitialized
}

if sys.usersSysType != MinIOUsersSysType {
switch sys.usersSysType {
case MinIOUsersSysType:
return sys.listMinIOUsers()
case LDAPUsersSysType:
return sys.listLDAPUsers()
default:
return nil, errIAMActionNotAllowed
}
}

func (sys *IAMSys) listMinIOUsers() (map[string]madmin.UserInfo, error) {
<-sys.configLoaded

sys.Lock()
Expand All @@ -940,6 +947,32 @@ func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) {
return users, nil
}

func (sys *IAMSys) listLDAPUsers() (map[string]madmin.UserInfo, error) {
<-sys.configLoaded

policyMap := make(map[string]MappedPolicy)
if err := sys.store.loadMappedPolicies(context.Background(), stsUser, false, policyMap); err != nil {
return nil, err
}

users := make(map[string]madmin.UserInfo)
for k, v := range policyMap {
users[k] = madmin.UserInfo{
PolicyName: v.Policies,
Status: madmin.AccountEnabled,
}
}

// remove temp users created via STS
for k, v := range sys.iamUsersMap {
if v.IsTemp() || v.IsServiceAccount() {
delete(users, k)
}
}

return users, nil
}

// IsTempUser - returns if given key is a temporary user.
func (sys *IAMSys) IsTempUser(name string) (bool, string, error) {
if !sys.Initialized() {
Expand Down Expand Up @@ -1008,6 +1041,7 @@ func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
}
return madmin.UserInfo{
PolicyName: mappedPolicy.Policies,
Status: madmin.AccountEnabled,
MemberOf: memberships.ToSlice(),
}, nil
}
Expand Down Expand Up @@ -1808,23 +1842,46 @@ func (sys *IAMSys) ListGroups() (r []string, err error) {
return r, errServerNotInitialized
}

if sys.usersSysType != MinIOUsersSysType {
return nil, errIAMActionNotAllowed
switch sys.usersSysType {
case MinIOUsersSysType:
return sys.listMinIOGroups()
case LDAPUsersSysType:
return sys.listLDAPGroups()
default:
return r, errIAMActionNotAllowed
}
}

func (sys *IAMSys) listMinIOGroups() ([]string, error) {
<-sys.configLoaded

sys.Lock()
defer sys.Unlock()

r = make([]string, 0, len(sys.iamGroupsMap))
r := make([]string, 0, len(sys.iamGroupsMap))
for k := range sys.iamGroupsMap {
r = append(r, k)
}

return r, nil
}

func (sys *IAMSys) listLDAPGroups() ([]string, error) {
<-sys.configLoaded

policyMap := make(map[string]MappedPolicy)
if err := sys.store.loadMappedPolicies(context.Background(), stsUser, true, policyMap); err != nil {
return nil, err
}

r := make([]string, 0, len(policyMap))
for k := range policyMap {
r = append(r, k)
}

return r, nil
}

// PolicyDBSet - sets a policy for a user or group in the PolicyDB.
func (sys *IAMSys) PolicyDBSet(name, policy string, isGroup bool) error {
if !sys.Initialized() {
Expand Down Expand Up @@ -2132,13 +2189,13 @@ func (sys *IAMSys) IsAllowedLDAPSTS(args iampolicy.Args, parentUser string) bool
}

// Check policy for this LDAP user.
ldapPolicies, err := sys.PolicyDBGet(parentUser, false, args.Groups...)
if err != nil {
return false
}

ldapPolicies, _ := sys.policyDBGet(args.AccountName, false)
if len(ldapPolicies) == 0 {
return false
parentPolicies, err := sys.PolicyDBGet(parentUser, false, args.Groups...)
if err != nil {
return false
}
ldapPolicies = parentPolicies
}

var availablePolicies []iampolicy.Policy
Expand Down
86 changes: 69 additions & 17 deletions cmd/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package cmd
import (
"errors"
"net/http"
"strings"
"time"

jwtgo "github.com/dgrijalva/jwt-go"
Expand Down Expand Up @@ -51,34 +52,86 @@ var (
)

func authenticateJWTUsers(accessKey, secretKey string, expiry time.Duration) (string, error) {
passedCredential, err := auth.CreateCredentials(accessKey, secretKey)
expiresAt := UTCNow().Add(expiry)

cred, secret, err := authenticateUsersForJWT(accessKey, secretKey, expiresAt)
if err != nil {
return "", err
}
expiresAt := UTCNow().Add(expiry)
return authenticateJWTUsersWithCredentials(passedCredential, expiresAt)

claims := xjwt.NewMapClaims()
claims.SetExpiry(expiresAt)
claims.SetAccessKey(cred.AccessKey)
if cred.ParentUser != "" {
claims.SetLDAPUser(cred.ParentUser)
}

jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
return jwt.SignedString([]byte(secret))
}

func authenticateUsersForJWT(accessKey, secretKey string, expiredAt time.Time) (auth.Credentials, string, error) {
if globalIAMSys.usersSysType == LDAPUsersSysType {
return authenticateLDAPUsersForJWT(accessKey, secretKey, expiredAt)
}
return authenticateMinIOUsersForJWT(accessKey, secretKey)
}

func authenticateLDAPUsersForJWT(username, password string, expiredAt time.Time) (auth.Credentials, string, error) {
ldapUserDN, ldapGroups, err := globalLDAPConfig.Bind(username, password)
if err != nil {
return auth.Credentials{}, "", errAuthentication
}

// Check if this user or their groups have a policy applied.
ldapPolicies, _ := globalIAMSys.PolicyDBGet(ldapUserDN, false, ldapGroups...)
if len(ldapPolicies) == 0 {
return auth.Credentials{}, "", errInvalidAccessKeyID
}

m := map[string]interface{}{
expClaim: expiredAt.Unix(),
ldapUser: ldapUserDN,
}

secret := globalActiveCred.SecretKey
cred, err := auth.GetNewCredentialsWithMetadata(m, secret)
if err != nil {
return auth.Credentials{}, "", errAuthentication
}

cred.ParentUser = ldapUserDN
cred.Groups = ldapGroups

// Set the newly generated credentials, ensure that policies are fixed during the session.
if err = globalIAMSys.SetTempUser(cred.AccessKey, cred, strings.Join(ldapPolicies, ",")); err != nil {
return auth.Credentials{}, "", errAuthentication
}

// Notify all other MinIO peers to reload temp users
for _, nerr := range globalNotificationSys.LoadUser(cred.AccessKey, true) {
if nerr.Err != nil {
return auth.Credentials{}, "", errAuthentication
}
}

return cred, secret, nil
}

func authenticateJWTUsersWithCredentials(credentials auth.Credentials, expiresAt time.Time) (string, error) {
func authenticateMinIOUsersForJWT(accessKey, secretKey string) (auth.Credentials, string, error) {
serverCred := globalActiveCred
if serverCred.AccessKey != credentials.AccessKey {
if serverCred.AccessKey != accessKey {
var ok bool
serverCred, ok = globalIAMSys.GetUser(credentials.AccessKey)
serverCred, ok = globalIAMSys.GetUser(accessKey)
if !ok {
return "", errInvalidAccessKeyID
return auth.Credentials{}, "", errInvalidAccessKeyID
}
}

if !serverCred.Equal(credentials) {
return "", errAuthentication
if serverCred.AccessKey != serverCred.AccessKey && serverCred.SecretKey != secretKey {
return auth.Credentials{}, "", errAuthentication
}

claims := xjwt.NewMapClaims()
claims.SetExpiry(expiresAt)
claims.SetAccessKey(credentials.AccessKey)

jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
return jwt.SignedString([]byte(serverCred.SecretKey))
return serverCred, serverCred.SecretKey, nil
}

func authenticateNode(accessKey, secretKey, audience string) (string, error) {
Expand Down Expand Up @@ -119,7 +172,6 @@ func webTokenCallback(claims *xjwt.MapClaims) ([]byte, error) {
return nil, errInvalidAccessKeyID
}
return []byte(cred.SecretKey), nil

}

func isAuthTokenValid(token string) bool {
Expand Down
8 changes: 8 additions & 0 deletions cmd/jwt/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type StandardClaims struct {
// MapClaims - implements custom unmarshaller
type MapClaims struct {
AccessKey string `json:"accessKey,omitempty"`
LDAPUser string `json:"ldapUser,omitempty"`
jwtgo.MapClaims
}

Expand Down Expand Up @@ -162,6 +163,12 @@ func (c *MapClaims) SetAccessKey(accessKey string) {
c.MapClaims["accessKey"] = accessKey
}

// SetLDAPUser sets parent user dn as custom
// "ldapUser" field.
func (c *MapClaims) SetLDAPUser(ldapUser string) {
c.MapClaims["ldapUser"] = ldapUser
}

// Valid - implements https://godoc.org/github.com/dgrijalva/jwt-go#Claims compatible
// claims interface, additionally validates "accessKey" fields.
func (c *MapClaims) Valid() error {
Expand Down Expand Up @@ -317,6 +324,7 @@ func ParseWithClaims(tokenStr string, claims *MapClaims, fn func(*MapClaims) ([]
jwtgo.ValidationErrorClaimsInvalid)
}
}
claims.LDAPUser, _ = claims.Lookup("ldapUser")

// Lookup key from claims, claims may not be valid and may return
// invalid key which is okay as the signature verification will fail.
Expand Down