Refactor auth request within ListAccounts

The auth request is a special case of ListAccounts with query. It must
only have one account as a result and doesn't need to go through the
full process of query matching. Refactored the function to take some
shortcuts for auth requests.
This commit is contained in:
Benedikt Kulmann
2020-10-17 13:08:17 +02:00
parent a84eede915
commit a6c2e808ae

View File

@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"github.com/owncloud/ocis/ocis-pkg/log"
"path"
"regexp"
"strconv"
@@ -56,17 +57,6 @@ func (s Service) expandMemberOf(a *proto.Account) {
a.MemberOf = expanded
}
func (s Service) passwordIsValid(hash string, pwd string) (ok bool) {
defer func() {
if r := recover(); r != nil {
s.log.Error().Err(fmt.Errorf("%s", r)).Str("hash", hash).Msg("password lib panicked")
}
}()
c := crypt.NewFromHash(hash)
return c.Verify(hash, []byte(pwd)) == nil
}
func (s Service) hasAccountManagementPermissions(ctx context.Context) bool {
// get roles from context
roleIDs, ok := roles.ReadRoleIDsFromContext(ctx)
@@ -119,25 +109,34 @@ func (s Service) ListAccounts(ctx context.Context, in *proto.ListAccountsRequest
accLock.Lock()
defer accLock.Unlock()
var password string
var searchResults []string
teardownServiceUser := s.serviceUserToIndex()
defer teardownServiceUser()
// check if this looks like an auth request
match := authQuery.FindStringSubmatch(in.Query)
if len(match) == 3 {
in.Query = fmt.Sprintf("on_premises_sam_account_name eq '%s'", match[1]) // todo fetch email? make query configurable
password = match[2]
if password == "" {
return merrors.Unauthorized(s.id, "password must not be empty")
match, authRequest := getAuthQueryMatch(in.Query)
if authRequest {
password := match[2]
if len(password) == 0 {
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
}
searchResults, err = s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", match[1])
if err != nil {
return err
ids, err := s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", match[1])
if err != nil || len(ids) != 1 {
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
}
a := &proto.Account{}
err = s.repo.LoadAccount(ctx, ids[0], a)
if err != nil || a.PasswordProfile == nil || len(a.PasswordProfile.Password) == 0 {
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
}
if !isPasswordValid(s.log, a.PasswordProfile.Password, password) {
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
}
a.PasswordProfile.Password = ""
out.Accounts = []*proto.Account{a}
return nil
}
var onPremQuery = regexp.MustCompile(`^on_premises_sam_account_name eq '(.*)'$`) // TODO how is ' escaped in the password?
@@ -199,23 +198,9 @@ func (s Service) ListAccounts(ctx context.Context, in *proto.ListAccountsRequest
s.log.Error().Err(err).Str("account", hit).Msg("could not load account, skipping")
continue
}
var currentHash string
if a.PasswordProfile != nil {
currentHash = a.PasswordProfile.Password
}
s.debugLogAccount(a).Msg("found account")
if password != "" {
if a.PasswordProfile == nil {
s.debugLogAccount(a).Msg("no password profile")
return merrors.Unauthorized(s.id, "invalid password")
}
if !s.passwordIsValid(currentHash, password) {
return merrors.Unauthorized(s.id, "invalid password")
}
}
// TODO add groups if requested
// if in.FieldMask ...
s.expandMemberOf(a)
@@ -762,3 +747,19 @@ func (s Service) accountExists(ctx context.Context, username, mail, id string) (
}
return false, nil
}
func getAuthQueryMatch(query string) (match []string, authRequest bool) {
match = authQuery.FindStringSubmatch(query)
return match, len(match) == 3
}
func isPasswordValid(logger log.Logger, hash string, pwd string) (ok bool) {
defer func() {
if r := recover(); r != nil {
logger.Error().Err(fmt.Errorf("%s", r)).Str("hash", hash).Msg("password lib panicked")
}
}()
c := crypt.NewFromHash(hash)
return c.Verify(hash, []byte(pwd)) == nil
}