Add assume organization session support
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -273,6 +273,31 @@ func (e ErrInvitationNotPending) Error() string {
|
|||||||
return fmt.Sprintf("invitation %q is not pending", e.InvitationID)
|
return fmt.Sprintf("invitation %q is not pending", e.InvitationID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrPasswordRequired struct {
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPasswordRequiredError(reason string) *ErrPasswordRequired {
|
||||||
|
return &ErrPasswordRequired{Reason: reason}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrPasswordRequired) Error() string {
|
||||||
|
return fmt.Sprintf("password authentication required: %s", e.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrSAMLAuthenticationRequired struct {
|
||||||
|
Reason string
|
||||||
|
RedirectURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSAMLAuthenticationRequiredError(reason string, redirectURL string) *ErrSAMLAuthenticationRequired {
|
||||||
|
return &ErrSAMLAuthenticationRequired{Reason: reason, RedirectURL: redirectURL}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrSAMLAuthenticationRequired) Error() string {
|
||||||
|
return fmt.Sprintf("SAML authentication required: %s", e.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
// TenantAccessError is used by API recovery middleware to translate authorization/tenant failures
|
// TenantAccessError is used by API recovery middleware to translate authorization/tenant failures
|
||||||
// into a consistent client-facing error response.
|
// into a consistent client-facing error response.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -318,3 +318,101 @@ func (s SessionService) GetActiveSessionForMembership(ctx context.Context, rootS
|
|||||||
|
|
||||||
return childSession, nil
|
return childSession, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SessionService) AssumeOrganizationSession(
|
||||||
|
ctx context.Context,
|
||||||
|
sessionID gid.GID,
|
||||||
|
organizationID gid.GID,
|
||||||
|
) (*coredata.Session, *coredata.Membership, error) {
|
||||||
|
var (
|
||||||
|
now = time.Now()
|
||||||
|
rootSession = &coredata.Session{}
|
||||||
|
user = &coredata.User{}
|
||||||
|
membership = &coredata.Membership{}
|
||||||
|
childSession = &coredata.Session{}
|
||||||
|
scope = coredata.NewScopeFromObjectID(organizationID)
|
||||||
|
)
|
||||||
|
|
||||||
|
err := s.pg.WithTx(
|
||||||
|
ctx,
|
||||||
|
func(tx pg.Conn) error {
|
||||||
|
err := rootSession.LoadByID(ctx, tx, sessionID)
|
||||||
|
if err != nil {
|
||||||
|
if err == coredata.ErrResourceNotFound {
|
||||||
|
return NewSessionNotFoundError(sessionID)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot load session: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rootSession.IsRootSession() {
|
||||||
|
return fmt.Errorf("session %q is not a root session", sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rootSession.ExpireReason != nil || now.After(rootSession.ExpiredAt) {
|
||||||
|
return NewSessionExpiredError(sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = user.LoadByID(ctx, tx, rootSession.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot load user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = membership.LoadByUserInOrganization(ctx, tx, rootSession.UserID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
if err == coredata.ErrResourceNotFound {
|
||||||
|
return NewMembershipNotFoundError(organizationID)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot load membership: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
samlConfig := &coredata.SAMLConfiguration{}
|
||||||
|
err = samlConfig.LoadByOrganizationIDAndEmailDomain(
|
||||||
|
ctx,
|
||||||
|
tx,
|
||||||
|
scope,
|
||||||
|
organizationID,
|
||||||
|
user.EmailAddress.Domain(),
|
||||||
|
)
|
||||||
|
if err != nil && err != coredata.ErrResourceNotFound {
|
||||||
|
return fmt.Errorf("cannot load SAML configuration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && samlConfig.EnforcementPolicy == coredata.SAMLEnforcementPolicyRequired {
|
||||||
|
redirectURL, err := s.SAMLService.InitiateLogin(ctx, samlConfig.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot initiate SAML login: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewSAMLAuthenticationRequiredError("policy_requirement", redirectURL.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if rootSession.AuthMethod != coredata.AuthMethodPassword {
|
||||||
|
return NewPasswordRequiredError("password_authentication_required")
|
||||||
|
}
|
||||||
|
|
||||||
|
tenantID := scope.GetTenantID()
|
||||||
|
childSession = &coredata.Session{
|
||||||
|
ID: gid.New(tenantID, coredata.SessionEntityType),
|
||||||
|
UserID: rootSession.UserID,
|
||||||
|
TenantID: &tenantID,
|
||||||
|
ParentSessionID: &rootSession.ID,
|
||||||
|
ExpiredAt: rootSession.ExpiredAt,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = childSession.Insert(ctx, tx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot insert child session: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return childSession, membership, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user