influxdb/session/service.go

207 lines
6.0 KiB
Go

package session
import (
"context"
"time"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/rand"
"github.com/influxdata/influxdb/v2/snowflake"
)
// Service implements the influxdb.SessionService interface and
// handles communication between session and the necessary user and urm services
type Service struct {
store *Storage
userService influxdb.UserService
urmService influxdb.UserResourceMappingService
authService influxdb.AuthorizationService
sessionLength time.Duration
idGen platform.IDGenerator
tokenGen influxdb.TokenGenerator
disableAuthorizationsForMaxPermissions func(context.Context) bool
}
// ServiceOption is a functional option for configuring a *Service
type ServiceOption func(*Service)
// WithSessionLength configures the length of the session with the provided
// duration when the resulting option is called on a *Service.
func WithSessionLength(length time.Duration) ServiceOption {
return func(s *Service) {
s.sessionLength = length
}
}
// WithIDGenerator overrides the default ID generator with the one
// provided to this function when called on a *Service
func WithIDGenerator(gen platform.IDGenerator) ServiceOption {
return func(s *Service) {
s.idGen = gen
}
}
// WithTokenGenerator overrides the default token generator with the one
// provided to this function when called on a *Service
func WithTokenGenerator(gen influxdb.TokenGenerator) ServiceOption {
return func(s *Service) {
s.tokenGen = gen
}
}
// NewService creates a new session service
func NewService(store *Storage, userService influxdb.UserService, urmService influxdb.UserResourceMappingService, authSvc influxdb.AuthorizationService, opts ...ServiceOption) *Service {
service := &Service{
store: store,
userService: userService,
urmService: urmService,
authService: authSvc,
sessionLength: time.Hour,
idGen: snowflake.NewIDGenerator(),
tokenGen: rand.NewTokenGenerator(64),
disableAuthorizationsForMaxPermissions: func(context.Context) bool {
return false
},
}
for _, opt := range opts {
opt(service)
}
return service
}
// WithMaxPermissionFunc sets the useAuthorizationsForMaxPermissions function
// which can trigger whether or not max permissions uses the users authorizations
// to derive maximum permissions.
func (s *Service) WithMaxPermissionFunc(fn func(context.Context) bool) {
s.disableAuthorizationsForMaxPermissions = fn
}
// FindSession finds a session based on the session key
func (s *Service) FindSession(ctx context.Context, key string) (*influxdb.Session, error) {
session, err := s.store.FindSessionByKey(ctx, key)
if err != nil {
return nil, err
}
// TODO: We want to be able to store permissions in the session
// but the contract provided by urm's doesn't give us enough information to quickly repopulate our
// session permissions on updates so we are required to pull the permissions every time we find the session.
permissions, err := s.getPermissionSet(ctx, session.UserID)
if err != nil {
return nil, err
}
session.Permissions = permissions
return session, nil
}
// ExpireSession removes a session from the system
func (s *Service) ExpireSession(ctx context.Context, key string) error {
session, err := s.store.FindSessionByKey(ctx, key)
if err != nil {
return err
}
return s.store.DeleteSession(ctx, session.ID)
}
// CreateSession
func (s *Service) CreateSession(ctx context.Context, user string) (*influxdb.Session, error) {
u, err := s.userService.FindUser(ctx, influxdb.UserFilter{
Name: &user,
})
if err != nil {
return nil, err
}
token, err := s.tokenGen.Token()
if err != nil {
return nil, err
}
// for now we are not storing the permissions because we need to pull them every time we find
// so we might as well keep the session stored small
now := time.Now()
session := &influxdb.Session{
ID: s.idGen.ID(),
Key: token,
CreatedAt: now,
ExpiresAt: now.Add(s.sessionLength),
UserID: u.ID,
}
return session, s.store.CreateSession(ctx, session)
}
// RenewSession update the sessions expiration time
func (s *Service) RenewSession(ctx context.Context, session *influxdb.Session, newExpiration time.Time) error {
if session == nil {
return &errors.Error{
Msg: "session is nil",
}
}
return s.store.RefreshSession(ctx, session.ID, newExpiration)
}
func (s *Service) getPermissionSet(ctx context.Context, uid platform.ID) ([]influxdb.Permission, error) {
mappings, _, err := s.urmService.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{UserID: uid}, influxdb.FindOptions{Limit: 100})
if err != nil {
return nil, err
}
permissions, err := permissionFromMapping(mappings)
if err != nil {
return nil, err
}
if len(mappings) == 100 {
// if we got 100 mappings we probably need to pull more pages
// account for paginated results
for i := len(mappings); len(mappings) > 0; i += len(mappings) {
mappings, _, err = s.urmService.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{UserID: uid}, influxdb.FindOptions{Offset: i, Limit: 100})
if err != nil {
return nil, err
}
pms, err := permissionFromMapping(mappings)
if err != nil {
return nil, err
}
permissions = append(permissions, pms...)
}
}
if !s.disableAuthorizationsForMaxPermissions(ctx) {
as, _, err := s.authService.FindAuthorizations(ctx, influxdb.AuthorizationFilter{UserID: &uid})
if err != nil {
return nil, err
}
for _, a := range as {
permissions = append(permissions, a.Permissions...)
}
}
permissions = append(permissions, influxdb.MePermissions(uid)...)
return permissions, nil
}
func permissionFromMapping(mappings []*influxdb.UserResourceMapping) ([]influxdb.Permission, error) {
ps := make([]influxdb.Permission, 0, len(mappings))
for _, m := range mappings {
p, err := m.ToPermissions()
if err != nil {
return nil, &errors.Error{
Err: err,
}
}
ps = append(ps, p...)
}
return ps, nil
}