Skip to content

Commit

Permalink
User provisioning and variouse CR fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
IljaN committed Dec 11, 2020
1 parent dcc6a3b commit 9f25ab9
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 54 deletions.
1 change: 1 addition & 0 deletions proxy/pkg/command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic
middleware.Logger(l),
middleware.UserProvider(userProvider),
middleware.TokenManagerConfig(cfg.TokenManager),
middleware.AutoprovisionAccounts(cfg.AutoprovisionAccounts),
),
middleware.CreateHome(
middleware.Logger(l),
Expand Down
35 changes: 20 additions & 15 deletions proxy/pkg/middleware/account_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ func AccountResolver(optionSetters ...Option) func(next http.Handler) http.Handl
logger.Fatal().Err(err).Msgf("Could not initialize token-manager")
}

return &accountResolverCS3{
next: next,
logger: logger,
tokenManager: tokenManager,
userProvider: options.UserProvider,
return &accountResolver{
next: next,
logger: logger,
tokenManager: tokenManager,
userProvider: options.UserProvider,
autoProvisionAccounts: options.AutoprovisionAccounts,
}
}
}

type accountResolverCS3 struct {
next http.Handler
logger log.Logger
tokenManager tokenPkg.Manager
userProvider backend.UserBackend
type accountResolver struct {
next http.Handler
logger log.Logger
tokenManager tokenPkg.Manager
userProvider backend.UserBackend
autoProvisionAccounts bool
}

func (m accountResolverCS3) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
claims := oidc.FromContext(req.Context())
u, ok := revauser.ContextGetUser(req.Context())

Expand All @@ -69,7 +71,13 @@ func (m accountResolverCS3) ServeHTTP(w http.ResponseWriter, req *http.Request)
var err error
u, err = m.userProvider.GetUserByClaims(req.Context(), claim, value, true)

if err == nil && u == nil {
if m.autoProvisionAccounts && err == backend.ErrAccountNotFound {
m.logger.Debug().Interface("claims", claims).Interface("user", u).Msgf("User by claim not found... autoprovisioning.")
u, err = m.userProvider.CreateUserFromClaims(req.Context(), claims)
}

if err == backend.ErrAccountNotFound || err == backend.ErrAccountDisabled {
m.logger.Debug().Interface("claims", claims).Interface("user", u).Msgf("Unautorized")
w.WriteHeader(http.StatusUnauthorized)
return
}
Expand All @@ -81,9 +89,6 @@ func (m accountResolverCS3) ServeHTTP(w http.ResponseWriter, req *http.Request)
}

m.logger.Debug().Interface("claims", claims).Interface("user", u).Msgf("associated claims with uuid")

// TODO: Check if user is disabled?

}

token, err := m.tokenManager.MintToken(req.Context(), u)
Expand Down
90 changes: 52 additions & 38 deletions proxy/pkg/user/backend/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
accounts "github.com/owncloud/ocis/accounts/pkg/proto/v0"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocis-pkg/oidc"
settings "github.com/owncloud/ocis/settings/pkg/proto/v0"
"net/http"
"strconv"
Expand Down Expand Up @@ -47,44 +48,25 @@ func (a accountsServiceBackend) GetUserByClaims(ctx context.Context, claim, valu
}

account, status = a.getAccount(ctx, query)
if status == http.StatusNotFound {
return nil, ErrAccountNotFound
}

if status != 0 || account == nil {
return nil, fmt.Errorf("could not get account, got status: %d", status)
}

if !account.AccountEnabled {
return nil, fmt.Errorf("could not get account: disabled!")
return nil, ErrAccountDisabled
}

user := &cs3.User{
Id: &cs3.UserId{
OpaqueId: account.Id,
Idp: a.OIDCIss,
},
Username: account.OnPremisesSamAccountName,
DisplayName: account.DisplayName,
Mail: account.Mail,
MailVerified: account.ExternalUserState == "" || account.ExternalUserState == "Accepted",
Groups: expandGroups(account),
Opaque: &types.Opaque{
Map: map[string]*types.OpaqueEntry{},
},
}

user.Opaque.Map["uid"] = &types.OpaqueEntry{
Decoder: "plain",
Value: []byte(strconv.FormatInt(account.UidNumber, 10)),
}
user.Opaque.Map["gid"] = &types.OpaqueEntry{
Decoder: "plain",
Value: []byte(strconv.FormatInt(account.GidNumber, 10)),
}
user := a.accountToUser(account)

if !withRoles {
return user, nil
}

if err := lazyLoadRoles(ctx, user, a.settingsRoleService); err != nil {
if err := injectRoles(ctx, user, a.settingsRoleService); err != nil {
a.logger.Warn().Err(err).Msgf("Could not load roles... continuing without")
}

Expand All @@ -105,6 +87,48 @@ func (a *accountsServiceBackend) Authenticate(ctx context.Context, username stri
return nil, fmt.Errorf("could not authenticate with username, password for user %s. Status: %d", username, status)
}

user := a.accountToUser(account)

if err := injectRoles(ctx, user, a.settingsRoleService); err != nil {
a.logger.Warn().Err(err).Msgf("Could not load roles... continuing without")
}

return user, nil
}

func (a accountsServiceBackend) CreateUserFromClaims(ctx context.Context, claims *oidc.StandardClaims) (*cs3.User, error) {
// TODO check if fields are missing.
req := &accounts.CreateAccountRequest{
Account: &accounts.Account{
DisplayName: claims.DisplayName,
PreferredName: claims.PreferredUsername,
OnPremisesSamAccountName: claims.PreferredUsername,
Mail: claims.Email,
CreationType: "LocalAccount",
AccountEnabled: true,
},
}
created, err := a.accountsClient.CreateAccount(context.Background(), req)
if err != nil {
return nil, err
}

user := a.accountToUser(created)

if err := injectRoles(ctx, user, a.settingsRoleService); err != nil {
a.logger.Warn().Err(err).Msgf("Could not load roles... continuing without")
}

return user, nil
}

func (a accountsServiceBackend) GetUserGroups(ctx context.Context, userID string) {
panic("implement me")
}

// accountToUser converts an owncloud account struct to a reva user struct. In the proxy
// we work with the reva struct as a token can be minted from it.
func (a *accountsServiceBackend) accountToUser(account *accounts.Account) *cs3.User {
user := &cs3.User{
Id: &cs3.UserId{
OpaqueId: account.Id,
Expand All @@ -128,16 +152,7 @@ func (a *accountsServiceBackend) Authenticate(ctx context.Context, username stri
Decoder: "plain",
Value: []byte(strconv.FormatInt(account.GidNumber, 10)),
}

if err := lazyLoadRoles(ctx, user, a.settingsRoleService); err != nil {
a.logger.Warn().Err(err).Msgf("Could not load roles... continuing without")
}

return user, nil
}

func (a accountsServiceBackend) GetUserGroups(ctx context.Context, userID string) {
panic("implement me")
return user
}

func (a *accountsServiceBackend) getAccount(ctx context.Context, query string) (account *accounts.Account, status int) {
Expand Down Expand Up @@ -177,16 +192,15 @@ func expandGroups(account *accounts.Account) []string {
return groups
}

// lazyLoadRoles adds roles from the roles-service to the user-struct by mutating an existing struct
func lazyLoadRoles(ctx context.Context, u *cs3.User, ss settings.RoleService) error {
// injectRoles adds roles from the roles-service to the user-struct by mutating an existing struct
func injectRoles(ctx context.Context, u *cs3.User, ss settings.RoleService) error {
roleIDs, err := loadRolesIDs(ctx, u.Id.OpaqueId, ss)
if err != nil {
return err
}

if len(roleIDs) == 0 {
return nil

}

enc, err := encodeRoleIDs(roleIDs)
Expand Down
12 changes: 12 additions & 0 deletions proxy/pkg/user/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,29 @@ package backend
import (
"context"
"encoding/json"
"errors"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/owncloud/ocis/ocis-pkg/oidc"
settings "github.com/owncloud/ocis/settings/pkg/proto/v0"
"google.golang.org/grpc"
)

var (
// ErrAccountNotFound account not found
ErrAccountNotFound = errors.New("user not found")
// ErrAccountDisabled account disabled
ErrAccountDisabled = errors.New("account disabled")
// ErrNotSupported operation not supported by user-backend
ErrNotSupported = errors.New("operation not supported")
)

// UserBackend allows the proxy to retrieve users from different user-backends (accounts-service, CS3)
type UserBackend interface {
GetUserByClaims(ctx context.Context, claim, value string, withRoles bool) (*cs3.User, error)
Authenticate(ctx context.Context, username string, password string) (*cs3.User, error)
CreateUserFromClaims(ctx context.Context, claims *oidc.StandardClaims) (*cs3.User, error)
GetUserGroups(ctx context.Context, userID string)
}

Expand Down
10 changes: 9 additions & 1 deletion proxy/pkg/user/backend/cs3.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocis-pkg/oidc"
settings "github.com/owncloud/ocis/settings/pkg/proto/v0"
)

Expand Down Expand Up @@ -38,6 +39,9 @@ func (c *cs3backend) GetUserByClaims(ctx context.Context, claim, value string, w
case err != nil:
return nil, fmt.Errorf("could not get user by claim %v with value %v", claim, value)
case res.Status.Code != rpcv1beta1.Code_CODE_OK:
if res.Status.Code == rpcv1beta1.Code_CODE_NOT_FOUND {
return nil, ErrAccountNotFound
}
return nil, fmt.Errorf("could not get user by claim %v with value %v : %w ", claim, value, err)
}

Expand Down Expand Up @@ -79,14 +83,18 @@ func (c *cs3backend) Authenticate(ctx context.Context, username string, password

switch {
case err != nil:
return nil, fmt.Errorf("could not authenticate with username and password user: %s", username)
return nil, fmt.Errorf("could not authenticate with username and password user: %s, %w", username, err)
case res.Status.Code != rpcv1beta1.Code_CODE_OK:
return nil, fmt.Errorf("could not authenticate with username and password user: %s, got code: %d", username, res.Status.Code)
}

return res.User, nil
}

func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims *oidc.StandardClaims) (*cs3.User, error) {
return nil, fmt.Errorf("CS3 Backend does not support creating users from claims")
}

func (c cs3backend) GetUserGroups(ctx context.Context, userID string) {
panic("implement me")
}

0 comments on commit 9f25ab9

Please sign in to comment.