ICU-745/Refactor internal/session to new domain errors (#877)
* Refactor internal/sessions to use domain errors
This commit is contained in:
1 parent
81785e139f
commit
178d4efa64
24 files changed
+404
-329
No files matched your search
@@ -31,6 +31,9 @@ const (
|
||||
TicketNotFound Code = 107 // TicketNotFound represents that the ticket was not found
|
||||
Io Code = 108 // Io represents that an io error occurred in an underlying call (i.e binary.Write)
|
||||
InvalidTimeStamp Code = 109 // InvalidTimeStamp represents an invalid time stamp for an operation
|
||||
SessionNotFound Code = 110 // SessionNotFound represents that the session was not found
|
||||
InvalidSessionState Code = 111 // InvalidSessionState represents that the session was in an invalid state
|
||||
TokenMismatch Code = 112 // TokenMismatch represents that there was a token mismatch
|
||||
|
||||
// PasswordTooShort results from attempting to set a password which is to short.
|
||||
PasswordTooShort Code = 200
|
||||
@@ -51,6 +54,8 @@ const (
|
||||
Decrypt Code = 301 // Decrypt represents an error occurred during the underlying decryption process
|
||||
Encode Code = 302 // Encode represents an error occurred during the underlying encoding/marshaling process
|
||||
Decode Code = 303 // Decode represents an error occurred during the underlying decoding/unmarshaling process
|
||||
GenKey Code = 304 // GenKey represents an error occurred during the underlying key generation process
|
||||
GenCert Code = 305 // GenCert represents an error occurred during the underlying certificate generation process
|
||||
|
||||
// DB errors are reserved Codes from 1000-1999
|
||||
CheckConstraint Code = 1000 // CheckConstraint represents a check constraint error
|
||||
|
||||
@@ -77,6 +77,21 @@ func TestCode_Both_String_Info(t *testing.T) {
|
||||
c: InvalidTimeStamp,
|
||||
want: InvalidTimeStamp,
|
||||
},
|
||||
{
|
||||
name: "SessionNotFound",
|
||||
c: SessionNotFound,
|
||||
want: SessionNotFound,
|
||||
},
|
||||
{
|
||||
name: "InvalidSessionState",
|
||||
c: InvalidSessionState,
|
||||
want: InvalidSessionState,
|
||||
},
|
||||
{
|
||||
name: "TokenMismatch",
|
||||
c: TokenMismatch,
|
||||
want: TokenMismatch,
|
||||
},
|
||||
{
|
||||
name: "PasswordTooShort",
|
||||
c: PasswordTooShort,
|
||||
@@ -117,6 +132,16 @@ func TestCode_Both_String_Info(t *testing.T) {
|
||||
c: Decode,
|
||||
want: Decode,
|
||||
},
|
||||
{
|
||||
name: "GenKey",
|
||||
c: GenKey,
|
||||
want: GenKey,
|
||||
},
|
||||
{
|
||||
name: "GenCert",
|
||||
c: GenCert,
|
||||
want: GenCert,
|
||||
},
|
||||
{
|
||||
name: "CheckConstraint",
|
||||
c: CheckConstraint,
|
||||
|
||||
@@ -56,6 +56,18 @@ var errorCodeInfo = map[Code]Info{
|
||||
Message: "invalid time stamp",
|
||||
Kind: Integrity,
|
||||
},
|
||||
SessionNotFound: {
|
||||
Message: "session not found",
|
||||
Kind: Integrity,
|
||||
},
|
||||
InvalidSessionState: {
|
||||
Message: "session state was not valid for the requested operation",
|
||||
Kind: Integrity,
|
||||
},
|
||||
TokenMismatch: {
|
||||
Message: "token mismatch",
|
||||
Kind: Integrity,
|
||||
},
|
||||
PasswordTooShort: {
|
||||
Message: "too short",
|
||||
Kind: Password,
|
||||
@@ -88,6 +100,14 @@ var errorCodeInfo = map[Code]Info{
|
||||
Message: "error occurred during decode",
|
||||
Kind: Encoding,
|
||||
},
|
||||
GenKey: {
|
||||
Message: "error occurred during key generation",
|
||||
Kind: Encryption,
|
||||
},
|
||||
GenCert: {
|
||||
Message: "error occurred during certification generation",
|
||||
Kind: Encryption,
|
||||
},
|
||||
CheckConstraint: {
|
||||
Message: "constraint check failed",
|
||||
Kind: Integrity,
|
||||
|
||||
@@ -131,7 +131,7 @@ func (r *Repository) UpdateUser(ctx context.Context, user *User, version uint32,
|
||||
}
|
||||
if rowsUpdated > 1 {
|
||||
// return err, which will result in a rollback of the update
|
||||
return errors.New(errors.MultipleRecords, op, "error more than 1 resource would have been updated")
|
||||
return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been updated")
|
||||
}
|
||||
// we need a new repo, that's using the same reader/writer as this TxHandler
|
||||
txRepo := &Repository{
|
||||
|
||||
@@ -2,7 +2,6 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
@@ -55,7 +54,8 @@ var (
|
||||
|
||||
// New creates a new in memory session. No options
|
||||
// are currently supported.
|
||||
func NewConnection(sessionID, clientTcpAddress string, clientTcpPort uint32, endpointTcpAddr string, endpointTcpPort uint32, opt ...Option) (*Connection, error) {
|
||||
func NewConnection(sessionID, clientTcpAddress string, clientTcpPort uint32, endpointTcpAddr string, endpointTcpPort uint32, _ ...Option) (*Connection, error) {
|
||||
const op = "session.NewConnection"
|
||||
c := Connection{
|
||||
SessionId: sessionID,
|
||||
ClientTcpAddress: clientTcpAddress,
|
||||
@@ -63,8 +63,8 @@ func NewConnection(sessionID, clientTcpAddress string, clientTcpPort uint32, end
|
||||
EndpointTcpAddress: endpointTcpAddr,
|
||||
EndpointTcpPort: endpointTcpPort,
|
||||
}
|
||||
if err := c.validateNewConnection("new connection:"); err != nil {
|
||||
return nil, err
|
||||
if err := c.validateNewConnection(); err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
@@ -109,29 +109,30 @@ func (c *Connection) Clone() interface{} {
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the connection
|
||||
// before it's written.
|
||||
func (c *Connection) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
func (c *Connection) VetForWrite(_ context.Context, _ db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
const op = "session.(Connection).VetForWrite"
|
||||
opts := db.GetOpts(opt...)
|
||||
if c.PublicId == "" {
|
||||
return fmt.Errorf("connection vet for write: missing public id: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing public id")
|
||||
}
|
||||
switch opType {
|
||||
case db.CreateOp:
|
||||
if err := c.validateNewConnection("connection vet for write:"); err != nil {
|
||||
return err
|
||||
if err := c.validateNewConnection(); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
case db.UpdateOp:
|
||||
switch {
|
||||
case contains(opts.WithFieldMaskPaths, "PublicId"):
|
||||
return fmt.Errorf("connection vet for write: public id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "public id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "SessionId"):
|
||||
return fmt.Errorf("connection vet for write: session id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "session id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "CreateTime"):
|
||||
return fmt.Errorf("connection vet for write: create time is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "create time is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "UpdateTime"):
|
||||
return fmt.Errorf("connection vet for write: update time is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "update time is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "ClosedReason"):
|
||||
if _, err := convertToClosedReason(c.ClosedReason); err != nil {
|
||||
return fmt.Errorf("connection vet for write: %w", errors.ErrInvalidParameter)
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,21 +155,22 @@ func (c *Connection) SetTableName(n string) {
|
||||
}
|
||||
|
||||
// validateNewConnection checks everything but the connection's PublicId
|
||||
func (c *Connection) validateNewConnection(errorPrefix string) error {
|
||||
func (c *Connection) validateNewConnection() error {
|
||||
const op = "session.(Connection).validateNewConnection"
|
||||
if c.SessionId == "" {
|
||||
return fmt.Errorf("%s missing session id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
if c.ClientTcpAddress == "" {
|
||||
return fmt.Errorf("%s missing client address: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing client address")
|
||||
}
|
||||
if c.ClientTcpPort == 0 {
|
||||
return fmt.Errorf("%s missing client port: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing client port")
|
||||
}
|
||||
if c.EndpointTcpAddress == "" {
|
||||
return fmt.Errorf("%s missing endpoint address: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing endpoint address")
|
||||
}
|
||||
if c.EndpointTcpPort == 0 {
|
||||
return fmt.Errorf("%s missing endpoint port: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing endpoint port")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -24,6 +24,7 @@ func (r ClosedReason) String() string {
|
||||
}
|
||||
|
||||
func convertToClosedReason(s string) (ClosedReason, error) {
|
||||
const op = "session.convertToClosedReason"
|
||||
switch s {
|
||||
case UnknownReason.String():
|
||||
return UnknownReason, nil
|
||||
@@ -38,6 +39,6 @@ func convertToClosedReason(s string) (ClosedReason, error) {
|
||||
case ConnectionSystemError.String():
|
||||
return ConnectionSystemError, nil
|
||||
default:
|
||||
return "", fmt.Errorf("closed reason: %s is not a valid reason: %w", s, errors.ErrInvalidParameter)
|
||||
return "", errors.New(errors.InvalidParameter, op, fmt.Sprintf("%s is not a valid reason", s))
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
)
|
||||
|
||||
@@ -16,11 +14,12 @@ type CloseWith struct {
|
||||
}
|
||||
|
||||
func (c CloseWith) validate() error {
|
||||
const op = "session.(CloseWith).validate"
|
||||
if c.ConnectionId == "" {
|
||||
return fmt.Errorf("missing connection id: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing connection id")
|
||||
}
|
||||
if c.ClosedReason.String() == "" {
|
||||
return fmt.Errorf("missing closed reason: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing closed reason")
|
||||
}
|
||||
// 0 is valid for BytesUp and BytesDown
|
||||
return nil
|
||||
|
||||
@@ -2,7 +2,6 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
@@ -66,13 +65,14 @@ var (
|
||||
|
||||
// NewConnectionState creates a new in memory connection state. No options
|
||||
// are currently supported.
|
||||
func NewConnectionState(connectionId string, state ConnectionStatus, opt ...Option) (*ConnectionState, error) {
|
||||
func NewConnectionState(connectionId string, state ConnectionStatus, _ ...Option) (*ConnectionState, error) {
|
||||
const op = "session.NewConnectionState"
|
||||
s := ConnectionState{
|
||||
ConnectionId: connectionId,
|
||||
Status: state,
|
||||
}
|
||||
if err := s.validate("new connection state:"); err != nil {
|
||||
return nil, err
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
@@ -118,9 +118,10 @@ func (s *ConnectionState) Clone() interface{} {
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the state
|
||||
// before it's written.
|
||||
func (s *ConnectionState) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if err := s.validate("connection state vet for write:"); err != nil {
|
||||
return err
|
||||
func (s *ConnectionState) VetForWrite(_ context.Context, _ db.Reader, _ db.OpType, _ ...db.Option) error {
|
||||
const op = "session.(ConnectionState).VetForWrite"
|
||||
if err := s.validate(); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -141,21 +142,22 @@ func (s *ConnectionState) SetTableName(n string) {
|
||||
}
|
||||
|
||||
// validate checks the session state
|
||||
func (s *ConnectionState) validate(errorPrefix string) error {
|
||||
func (s *ConnectionState) validate() error {
|
||||
const op = "session.(ConnectionState).validate"
|
||||
if s.Status == "" {
|
||||
return fmt.Errorf("%s missing status: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing status")
|
||||
}
|
||||
if s.ConnectionId == "" {
|
||||
return fmt.Errorf("%s missing connection id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing connection id")
|
||||
}
|
||||
if s.StartTime != nil {
|
||||
return fmt.Errorf("%s start time is not settable: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "start time is not settable")
|
||||
}
|
||||
if s.EndTime != nil {
|
||||
return fmt.Errorf("%s end time is not settable: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "end time is not settable")
|
||||
}
|
||||
if s.PreviousEndTime != nil {
|
||||
return fmt.Errorf("%s previous end time is not settable: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "previous end time is not settable")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func TestConnectionState_Create(t *testing.T) {
|
||||
args args
|
||||
want *ConnectionState
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
wantIsErr errors.Code
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
@@ -50,7 +50,7 @@ func TestConnectionState_Create(t *testing.T) {
|
||||
status: StatusClosed,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-status",
|
||||
@@ -58,7 +58,7 @@ func TestConnectionState_Create(t *testing.T) {
|
||||
connectionId: connection.PublicId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -67,7 +67,7 @@ func TestConnectionState_Create(t *testing.T) {
|
||||
got, err := NewConnectionState(tt.args.connectionId, tt.args.status)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(err, tt.wantIsErr))
|
||||
assert.True(errors.Match(errors.T(tt.wantIsErr), err))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestConnection_Create(t *testing.T) {
|
||||
args args
|
||||
want *Connection
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
wantIsErr errors.Code
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
@@ -61,7 +61,7 @@ func TestConnection_Create(t *testing.T) {
|
||||
endpointTcpPort: 2222,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-client-address",
|
||||
@@ -72,7 +72,7 @@ func TestConnection_Create(t *testing.T) {
|
||||
endpointTcpPort: 2222,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-client-port",
|
||||
@@ -83,7 +83,7 @@ func TestConnection_Create(t *testing.T) {
|
||||
endpointTcpPort: 2222,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-endpoint-address",
|
||||
@@ -94,7 +94,7 @@ func TestConnection_Create(t *testing.T) {
|
||||
endpointTcpPort: 2222,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-endpoint-port",
|
||||
@@ -105,7 +105,7 @@ func TestConnection_Create(t *testing.T) {
|
||||
endpointTcpAddress: "127.0.0.1",
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -120,7 +120,7 @@ func TestConnection_Create(t *testing.T) {
|
||||
)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(err, tt.wantIsErr))
|
||||
assert.True(errors.Match(errors.T(tt.wantIsErr), err))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
@@ -8,12 +8,4 @@ var (
|
||||
// ErrInvalidStateForOperation indicates the session's state does not allow the
|
||||
// operation.
|
||||
ErrInvalidStateForOperation = errors.New("state is invalid for operation")
|
||||
|
||||
// ErrSessionNotPending indicates that a session cannot be activated
|
||||
// because it's not in a pending state.
|
||||
ErrSessionNotPending = errors.New("session is not in a pending state")
|
||||
|
||||
// ErrOpenConnection indicates that a session can not be terminated because
|
||||
// it has open connections.
|
||||
ErrOpenConnection = errors.New("session has open connections")
|
||||
)
|
||||
@@ -1,9 +1,8 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,33 +20,37 @@ const (
|
||||
)
|
||||
|
||||
func newId() (string, error) {
|
||||
const op = "session.newId"
|
||||
id, err := db.NewPublicId(SessionPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new session id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newStateId() (string, error) {
|
||||
const op = "session.newStateId"
|
||||
id, err := db.NewPublicId(StatePrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new session state id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newConnectionId() (string, error) {
|
||||
const op = "session.newConnectionId"
|
||||
id, err := db.NewPublicId(ConnectionPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new session connection id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newConnectionStateId() (string, error) {
|
||||
const op = "session.newConnectionStateId"
|
||||
id, err := db.NewPublicId(ConnectionStatePrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new session connection state id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -2,11 +2,10 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
)
|
||||
|
||||
@@ -28,14 +27,15 @@ type Repository struct {
|
||||
// NewRepository creates a new session Repository. Supports the options: WithLimit
|
||||
// which sets a default limit on results returned by repo operations.
|
||||
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
|
||||
const op = "session.NewRepository"
|
||||
if r == nil {
|
||||
return nil, errors.New("error creating db repository with nil reader")
|
||||
return nil, errors.New(errors.InvalidParameter, op, "nil reader")
|
||||
}
|
||||
if w == nil {
|
||||
return nil, errors.New("error creating db repository with nil writer")
|
||||
return nil, errors.New(errors.InvalidParameter, op, "nil writer")
|
||||
}
|
||||
if kms == nil {
|
||||
return nil, errors.New("error creating db repository with nil kms")
|
||||
return nil, errors.New(errors.InvalidParameter, op, "nil kms")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
if opts.withLimit == 0 {
|
||||
@@ -53,6 +53,7 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repo
|
||||
// list will return a listing of resources and honor the WithLimit option or the
|
||||
// repo defaultLimit. Supports WithOrder option.
|
||||
func (r *Repository) list(ctx context.Context, resources interface{}, where string, args []interface{}, opts options) error {
|
||||
const op = "session.(Repository).list"
|
||||
limit := r.defaultLimit
|
||||
var dbOpts []db.Option
|
||||
if opts.withLimit != 0 {
|
||||
@@ -63,10 +64,14 @@ func (r *Repository) list(ctx context.Context, resources interface{}, where stri
|
||||
if opts.withOrder != "" {
|
||||
dbOpts = append(dbOpts, db.WithOrder(opts.withOrder))
|
||||
}
|
||||
return r.reader.SearchWhere(ctx, resources, where, args, dbOpts...)
|
||||
if err := r.reader.SearchWhere(ctx, resources, where, args, dbOpts...); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) convertToSessions(ctx context.Context, sessionsWithState []*sessionView, opt ...Option) ([]*Session, error) {
|
||||
const op = "session.(Repository).convertToSessions"
|
||||
opts := getOpts(opt...)
|
||||
|
||||
if len(sessionsWithState) == 0 {
|
||||
@@ -114,10 +119,10 @@ func (r *Repository) convertToSessions(ctx context.Context, sessionsWithState []
|
||||
if len(workingSession.CtTofuToken) > 0 {
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, workingSession.ScopeId, kms.KeyPurposeDatabase, kms.WithKeyId(workingSession.KeyId))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("convert session: unable to get database wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := workingSession.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, fmt.Errorf("convert session: cannot decrypt session value: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("cannot decrypt session value"))
|
||||
}
|
||||
} else {
|
||||
workingSession.CtTofuToken = nil
|
||||
|
||||
@@ -2,7 +2,6 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
@@ -12,9 +11,10 @@ import (
|
||||
// LookupConnection will look up a connection in the repository and return the connection
|
||||
// with its states. If the connection is not found, it will return nil, nil, nil.
|
||||
// No options are currently supported.
|
||||
func (r *Repository) LookupConnection(ctx context.Context, connectionId string, opt ...Option) (*Connection, []*ConnectionState, error) {
|
||||
func (r *Repository) LookupConnection(ctx context.Context, connectionId string, _ ...Option) (*Connection, []*ConnectionState, error) {
|
||||
const op = "session.(Repository).LookupConnection"
|
||||
if connectionId == "" {
|
||||
return nil, nil, fmt.Errorf("lookup connection: missing connectionId id: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing connectionId id")
|
||||
}
|
||||
connection := AllocConnection()
|
||||
connection.PublicId = connectionId
|
||||
@@ -25,11 +25,11 @@ func (r *Repository) LookupConnection(ctx context.Context, connectionId string,
|
||||
db.ExpBackoff{},
|
||||
func(read db.Reader, w db.Writer) error {
|
||||
if err := read.LookupById(ctx, &connection); err != nil {
|
||||
return fmt.Errorf("lookup connection: failed %w for %s", err, connectionId)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", connectionId)))
|
||||
}
|
||||
var err error
|
||||
if states, err = fetchConnectionStates(ctx, read, connectionId, db.WithOrder("start_time desc")); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -38,31 +38,33 @@ func (r *Repository) LookupConnection(ctx context.Context, connectionId string,
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return nil, nil, fmt.Errorf("lookup connection: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &connection, states, nil
|
||||
}
|
||||
|
||||
// ListConnections will sessions. Supports the WithLimit and WithOrder options.
|
||||
func (r *Repository) ListConnections(ctx context.Context, sessionId string, opt ...Option) ([]*Connection, error) {
|
||||
const op = "session.(Repository).ListConnections"
|
||||
opts := getOpts(opt...)
|
||||
var connections []*Connection
|
||||
err := r.list(ctx, &connections, "session_id = ?", []interface{}{sessionId}, opts) // pass options, so WithLimit and WithOrder are supported
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list connections: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return connections, nil
|
||||
}
|
||||
|
||||
// DeleteConnection will delete a connection from the repository.
|
||||
func (r *Repository) DeleteConnection(ctx context.Context, publicId string, opt ...Option) (int, error) {
|
||||
func (r *Repository) DeleteConnection(ctx context.Context, publicId string, _ ...Option) (int, error) {
|
||||
const op = "session.(Repository).DeleteConnection"
|
||||
if publicId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete connection: missing public id %w", errors.ErrInvalidParameter)
|
||||
return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id")
|
||||
}
|
||||
connection := AllocConnection()
|
||||
connection.PublicId = publicId
|
||||
if err := r.reader.LookupByPublicId(ctx, &connection); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete connection: failed %w for %s", err, publicId)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
|
||||
}
|
||||
|
||||
var rowsDeleted int
|
||||
@@ -77,23 +79,27 @@ func (r *Repository) DeleteConnection(ctx context.Context, publicId string, opt
|
||||
ctx,
|
||||
deleteConnection,
|
||||
)
|
||||
if err == nil && rowsDeleted > 1 {
|
||||
// return err, which will result in a rollback of the delete
|
||||
return stderrors.New("error more than 1 connection would have been deleted")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return err
|
||||
if rowsDeleted > 1 {
|
||||
// return err, which will result in a rollback of the delete
|
||||
return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been deleted")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete connection: failed %w for %s", err, publicId)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
|
||||
func fetchConnectionStates(ctx context.Context, r db.Reader, connectionId string, opt ...db.Option) ([]*ConnectionState, error) {
|
||||
const op = "session.fetchConnectionStates"
|
||||
var states []*ConnectionState
|
||||
if err := r.SearchWhere(ctx, &states, "connection_id = ?", []interface{}{connectionId}, opt...); err != nil {
|
||||
return nil, fmt.Errorf("fetch connection states: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if len(states) == 0 {
|
||||
return nil, nil
|
||||
|
||||
@@ -169,7 +169,7 @@ func TestRepository_DeleteConnection(t *testing.T) {
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantErrMsg: "delete connection: missing public id invalid parameter",
|
||||
wantErrMsg: "session.(Repository).DeleteConnection: missing public id: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
@@ -184,7 +184,7 @@ func TestRepository_DeleteConnection(t *testing.T) {
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantErrMsg: "delete connection: failed db.LookupById: record not found",
|
||||
wantErrMsg: "db.LookupById: record not found",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/subtle"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -21,58 +20,59 @@ import (
|
||||
// its State of "Pending". The following fields must be empty when creating a
|
||||
// session: ServerId, ServerType, and PublicId. No options are
|
||||
// currently supported.
|
||||
func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping.Wrapper, newSession *Session, opt ...Option) (*Session, ed25519.PrivateKey, error) {
|
||||
func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping.Wrapper, newSession *Session, _ ...Option) (*Session, ed25519.PrivateKey, error) {
|
||||
const op = "session.(Repository).CreateSession"
|
||||
if newSession == nil {
|
||||
return nil, nil, fmt.Errorf("create session: missing session: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing session")
|
||||
}
|
||||
if newSession.PublicId != "" {
|
||||
return nil, nil, fmt.Errorf("create session: public id is not empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "public id is not empty")
|
||||
}
|
||||
if len(newSession.Certificate) != 0 {
|
||||
return nil, nil, fmt.Errorf("create session: certificate is not empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "certificate is not empty")
|
||||
}
|
||||
if newSession.TargetId == "" {
|
||||
return nil, nil, fmt.Errorf("create session: target id is empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing target id")
|
||||
}
|
||||
if newSession.HostId == "" {
|
||||
return nil, nil, fmt.Errorf("create session: user id is empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing host id")
|
||||
}
|
||||
if newSession.UserId == "" {
|
||||
return nil, nil, fmt.Errorf("create session: user id is empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing user id")
|
||||
}
|
||||
if newSession.HostSetId == "" {
|
||||
return nil, nil, fmt.Errorf("create session: host set id is empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing host set id")
|
||||
}
|
||||
if newSession.AuthTokenId == "" {
|
||||
return nil, nil, fmt.Errorf("create session: auth token id is empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing auth token id")
|
||||
}
|
||||
if newSession.ScopeId == "" {
|
||||
return nil, nil, fmt.Errorf("create session: scope id is empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
if newSession.ServerId != "" {
|
||||
return nil, nil, fmt.Errorf("create session: server id must be empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "server id is not empty")
|
||||
}
|
||||
if newSession.ServerType != "" {
|
||||
return nil, nil, fmt.Errorf("create session: server type must be empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "server type is not empty")
|
||||
}
|
||||
if newSession.CtTofuToken != nil {
|
||||
return nil, nil, fmt.Errorf("create session: ct must be empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "ct is not empty")
|
||||
}
|
||||
if newSession.TofuToken != nil {
|
||||
return nil, nil, fmt.Errorf("create session: tofu token must be empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "tofu token is not empty")
|
||||
}
|
||||
if newSession.ExpirationTime == nil || newSession.ExpirationTime.Timestamp.AsTime().IsZero() {
|
||||
return nil, nil, fmt.Errorf("create session: expiration is empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing expiration time")
|
||||
}
|
||||
|
||||
id, err := newId()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create session: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
privKey, certBytes, err := newCert(sessionWrapper, newSession.UserId, id, newSession.ExpirationTime.Timestamp.AsTime())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create session: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
newSession.Certificate = certBytes
|
||||
newSession.PublicId = id
|
||||
@@ -85,39 +85,40 @@ func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping.
|
||||
func(read db.Reader, w db.Writer) error {
|
||||
returnedSession = newSession.Clone().(*Session)
|
||||
if err = w.Create(ctx, returnedSession); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
var foundStates []*State
|
||||
// trigger will create new "Pending" state
|
||||
if foundStates, err = fetchStates(ctx, read, returnedSession.PublicId); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
if len(foundStates) != 1 {
|
||||
return fmt.Errorf("%d states found for new session %s", len(foundStates), returnedSession.PublicId)
|
||||
return errors.New(errors.MultipleRecords, op, fmt.Sprintf("%d states found for new session %s", len(foundStates), returnedSession.PublicId))
|
||||
}
|
||||
if len(foundStates) == 0 {
|
||||
return fmt.Errorf("no states found for new session %s", returnedSession.PublicId)
|
||||
return errors.New(errors.SessionNotFound, op, fmt.Sprintf("no states found for new session %s", returnedSession.PublicId))
|
||||
}
|
||||
returnedSession.States = foundStates
|
||||
if returnedSession.States[0].Status != StatusPending {
|
||||
return fmt.Errorf("new session %s state is not valid: %s", returnedSession.PublicId, returnedSession.States[0].Status)
|
||||
return errors.New(errors.InvalidSessionState, op, fmt.Sprintf("new session %s state is not valid: %s", returnedSession.PublicId, returnedSession.States[0].Status))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create session: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
return returnedSession, privKey, err
|
||||
return returnedSession, privKey, nil
|
||||
}
|
||||
|
||||
// LookupSession will look up a session in the repository and return the session
|
||||
// with its states. Returned States are ordered by start time descending. If the
|
||||
// session is not found, it will return nil, nil, nil. No options are currently
|
||||
// supported.
|
||||
func (r *Repository) LookupSession(ctx context.Context, sessionId string, opt ...Option) (*Session, *ConnectionAuthzSummary, error) {
|
||||
func (r *Repository) LookupSession(ctx context.Context, sessionId string, _ ...Option) (*Session, *ConnectionAuthzSummary, error) {
|
||||
const op = "session.(Repository).LookupSession"
|
||||
if sessionId == "" {
|
||||
return nil, nil, fmt.Errorf("lookup session: missing sessionId id: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
session := AllocSession()
|
||||
session.PublicId = sessionId
|
||||
@@ -127,11 +128,11 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, opt ..
|
||||
db.ExpBackoff{},
|
||||
func(read db.Reader, w db.Writer) error {
|
||||
if err := read.LookupById(ctx, &session); err != nil {
|
||||
return fmt.Errorf("lookup session: failed %w for %s", err, sessionId)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", sessionId)))
|
||||
}
|
||||
states, err := fetchStates(ctx, read, sessionId, db.WithOrder("start_time desc"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
session.States = states
|
||||
return nil
|
||||
@@ -141,15 +142,15 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, opt ..
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return nil, nil, fmt.Errorf("lookup session: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
if len(session.CtTofuToken) > 0 {
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, session.ScopeId, kms.KeyPurposeDatabase, kms.WithKeyId(session.KeyId))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("lookup session: unable to get database wrapper: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := session.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, nil, fmt.Errorf("lookup session: cannot decrypt session value: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("cannot decrypt session value"))
|
||||
}
|
||||
} else {
|
||||
session.CtTofuToken = nil
|
||||
@@ -157,7 +158,7 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, opt ..
|
||||
|
||||
authzSummary, err := r.sessionAuthzSummary(ctx, sessionId)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("lookup session: failed to get authz summary: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("failed to get authz summary"))
|
||||
}
|
||||
|
||||
return &session, authzSummary, nil
|
||||
@@ -165,6 +166,7 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, opt ..
|
||||
|
||||
// ListSessions will sessions. Supports the WithLimit, WithScopeId and WithSessionIds options.
|
||||
func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Session, error) {
|
||||
const op = "session.(Repository).ListSessions"
|
||||
opts := getOpts(opt...)
|
||||
var where []string
|
||||
var args []interface{}
|
||||
@@ -220,7 +222,7 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio
|
||||
|
||||
rows, err := r.reader.Query(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("changes: query failed: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
@@ -228,26 +230,27 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio
|
||||
for rows.Next() {
|
||||
var s sessionView
|
||||
if err := r.reader.ScanRows(rows, &s); err != nil {
|
||||
return nil, fmt.Errorf("changes: scan row failed: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("scan row failed"))
|
||||
}
|
||||
sessionsWithState = append(sessionsWithState, &s)
|
||||
}
|
||||
sessions, err := r.convertToSessions(ctx, sessionsWithState, withListingConvert(true))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list sessions: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// DeleteSession will delete a session from the repository.
|
||||
func (r *Repository) DeleteSession(ctx context.Context, publicId string, opt ...Option) (int, error) {
|
||||
func (r *Repository) DeleteSession(ctx context.Context, publicId string, _ ...Option) (int, error) {
|
||||
const op = "session.(Repository).DeleteSession"
|
||||
if publicId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete session: missing public id %w", errors.ErrInvalidParameter)
|
||||
return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id")
|
||||
}
|
||||
session := AllocSession()
|
||||
session.PublicId = publicId
|
||||
if err := r.reader.LookupByPublicId(ctx, &session); err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete session: failed %w for %s", err, publicId)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
|
||||
}
|
||||
|
||||
var rowsDeleted int
|
||||
@@ -262,15 +265,18 @@ func (r *Repository) DeleteSession(ctx context.Context, publicId string, opt ...
|
||||
ctx,
|
||||
deleteSession,
|
||||
)
|
||||
if err == nil && rowsDeleted > 1 {
|
||||
// return err, which will result in a rollback of the delete
|
||||
return stderrors.New("error more than 1 session would have been deleted")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return err
|
||||
if rowsDeleted > 1 {
|
||||
// return err, which will result in a rollback of the delete
|
||||
return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been deleted")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete session: failed %w for %s", err, publicId)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
|
||||
}
|
||||
return rowsDeleted, nil
|
||||
}
|
||||
@@ -281,15 +287,16 @@ func (r *Repository) DeleteSession(ctx context.Context, publicId string, opt ...
|
||||
// "canceling signal" during their next status heartbeat. CancelSession is
|
||||
// idempotent.
|
||||
func (r *Repository) CancelSession(ctx context.Context, sessionId string, sessionVersion uint32) (*Session, error) {
|
||||
const op = "session.(Repository).CancelSession"
|
||||
if sessionId == "" {
|
||||
return nil, fmt.Errorf("cancel session: missing session id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
if sessionVersion == 0 {
|
||||
return nil, fmt.Errorf("cancel session: missing session version: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing session version")
|
||||
}
|
||||
s, ss, err := r.updateState(ctx, sessionId, sessionVersion, StatusCanceling)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cancel session: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
s.States = ss
|
||||
return s, nil
|
||||
@@ -299,11 +306,12 @@ func (r *Repository) CancelSession(ctx context.Context, sessionId string, sessio
|
||||
// "terminated" Sessions cannot be terminated which still have connections that
|
||||
// are not closed.
|
||||
func (r *Repository) TerminateSession(ctx context.Context, sessionId string, sessionVersion uint32, reason TerminationReason) (*Session, error) {
|
||||
const op = "session.(Repository).TerminateSession"
|
||||
if sessionId == "" {
|
||||
return nil, fmt.Errorf("terminate session: missing session id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
if sessionVersion == 0 {
|
||||
return nil, fmt.Errorf("terminate session: version cannot be zero: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing session version")
|
||||
}
|
||||
|
||||
updatedSession := AllocSession()
|
||||
@@ -316,28 +324,28 @@ func (r *Repository) TerminateSession(ctx context.Context, sessionId string, ses
|
||||
func(reader db.Reader, w db.Writer) error {
|
||||
rowsAffected, err := w.Exec(ctx, terminateSessionCte, []interface{}{sessionId, sessionVersion})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to terminate session %s: %w", sessionId, err)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to terminate session %s", sessionId)))
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("unable to terminate session %s", sessionId)
|
||||
return errors.New(errors.InvalidSessionState, op, fmt.Sprintf("unable to terminate session %s", sessionId))
|
||||
}
|
||||
rowsUpdated, err := w.Update(ctx, &updatedSession, []string{"TerminationReason"}, nil, db.WithVersion(&sessionVersion))
|
||||
if err != nil {
|
||||
return fmt.Errorf("update session: failed %w for %s", err, sessionId)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", sessionId)))
|
||||
}
|
||||
if rowsUpdated != 1 {
|
||||
return fmt.Errorf("update to session %s would have updated %d session", updatedSession.PublicId, rowsUpdated)
|
||||
return errors.New(errors.MultipleRecords, op, fmt.Sprintf("update to session %s would have updated %d session", updatedSession.PublicId, rowsUpdated))
|
||||
}
|
||||
states, err := fetchStates(ctx, reader, sessionId, db.WithOrder("start_time desc"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
updatedSession.States = states
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("terminate session: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &updatedSession, nil
|
||||
}
|
||||
@@ -349,6 +357,7 @@ func (r *Repository) TerminateSession(ctx context.Context, sessionId string, ses
|
||||
// This function should called on a periodic basis a Controllers via it's
|
||||
// "ticker" pattern.
|
||||
func (r *Repository) TerminateCompletedSessions(ctx context.Context) (int, error) {
|
||||
const op = "session.(Repository).TerminateCompletedSessions"
|
||||
var rowsAffected int
|
||||
_, err := r.writer.DoTx(
|
||||
ctx,
|
||||
@@ -358,13 +367,13 @@ func (r *Repository) TerminateCompletedSessions(ctx context.Context) (int, error
|
||||
var err error
|
||||
rowsAffected, err = w.Exec(ctx, termSessionsUpdate, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("terminate completed sessions: %w", err)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op)
|
||||
}
|
||||
return rowsAffected, nil
|
||||
}
|
||||
@@ -377,6 +386,7 @@ func (r *Repository) TerminateCompletedSessions(ctx context.Context) (int, error
|
||||
// and returns it, along with it's states. If the authorization fails, it
|
||||
// an error of ErrInvalidStateForOperation.
|
||||
func (r *Repository) AuthorizeConnection(ctx context.Context, sessionId string) (*Connection, []*ConnectionState, *ConnectionAuthzSummary, error) {
|
||||
const op = "session.(Repository).AuthorizeConnection"
|
||||
if sessionId == "" {
|
||||
return nil, nil, nil, status.Errorf(codes.FailedPrecondition, "authorize connection: missing session id: %v", errors.ErrInvalidParameter)
|
||||
}
|
||||
@@ -405,17 +415,17 @@ func (r *Repository) AuthorizeConnection(ctx context.Context, sessionId string)
|
||||
}
|
||||
connectionStates, err = fetchConnectionStates(ctx, reader, connectionId, db.WithOrder("start_time desc"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
authzSummary, err := r.sessionAuthzSummary(ctx, connection.SessionId)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("authorize connection: %w", err)
|
||||
return nil, nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &connection, connectionStates, authzSummary, nil
|
||||
}
|
||||
@@ -427,20 +437,21 @@ type ConnectionAuthzSummary struct {
|
||||
}
|
||||
|
||||
func (r *Repository) sessionAuthzSummary(ctx context.Context, sessionId string) (*ConnectionAuthzSummary, error) {
|
||||
const op = "session.(Repository).sessionAuthzSummary"
|
||||
rows, err := r.reader.Query(ctx, remainingConnectionsCte, []interface{}{sessionId})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("session summary: query failed: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var info *ConnectionAuthzSummary
|
||||
for rows.Next() {
|
||||
if info != nil {
|
||||
return nil, fmt.Errorf("session summary: query returned more than one row")
|
||||
return nil, errors.New(errors.MultipleRecords, op, "query returned more than one row")
|
||||
}
|
||||
info = &ConnectionAuthzSummary{}
|
||||
if err := r.reader.ScanRows(rows, info); err != nil {
|
||||
return nil, fmt.Errorf("session summary: scan row failed: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("scan row failed"))
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
@@ -448,9 +459,10 @@ func (r *Repository) sessionAuthzSummary(ctx context.Context, sessionId string)
|
||||
|
||||
// ConnectConnection updates a connection in the repo with a state of "connected".
|
||||
func (r *Repository) ConnectConnection(ctx context.Context, c ConnectWith) (*Connection, []*ConnectionState, error) {
|
||||
const op = "session.(Repository).ConnectConnection"
|
||||
// ConnectWith.validate will check all the fields...
|
||||
if err := c.validate(); err != nil {
|
||||
return nil, nil, fmt.Errorf("connect session: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
var connection Connection
|
||||
var connectionStates []*ConnectionState
|
||||
@@ -473,28 +485,28 @@ func (r *Repository) ConnectConnection(ctx context.Context, c ConnectWith) (*Con
|
||||
}
|
||||
rowsUpdated, err := w.Update(ctx, &connection, fieldMask, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
if err == nil && rowsUpdated > 1 {
|
||||
// return err, which will result in a rollback of the update
|
||||
return stderrors.New("error more than 1 connection would have been updated ")
|
||||
return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been updated")
|
||||
}
|
||||
newState, err := NewConnectionState(connection.PublicId, StatusConnected)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
if err := w.Create(ctx, newState); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
connectionStates, err = fetchConnectionStates(ctx, reader, c.ConnectionId, db.WithOrder("start_time desc"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("connect session: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &connection, connectionStates, nil
|
||||
}
|
||||
@@ -509,13 +521,14 @@ type CloseConnectionResp struct {
|
||||
// CloseConnections set's a connection's state to "closed" in the repo. It's
|
||||
// called by a worker after it's closed a connection between the client and the
|
||||
// endpoint
|
||||
func (r *Repository) CloseConnections(ctx context.Context, closeWith []CloseWith, opt ...Option) ([]CloseConnectionResp, error) {
|
||||
func (r *Repository) CloseConnections(ctx context.Context, closeWith []CloseWith, _ ...Option) ([]CloseConnectionResp, error) {
|
||||
const op = "session.(Repository).CloseConnections"
|
||||
if len(closeWith) == 0 {
|
||||
return nil, fmt.Errorf("close connections: missing connections to close: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing connections")
|
||||
}
|
||||
for _, cw := range closeWith {
|
||||
if err := cw.validate(); err != nil {
|
||||
return nil, fmt.Errorf("close connections: %s was invalid: %w", cw.ConnectionId, err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("%s was invalid", cw.ConnectionId)))
|
||||
}
|
||||
}
|
||||
var resp []CloseConnectionResp
|
||||
@@ -539,14 +552,14 @@ func (r *Repository) CloseConnections(ctx context.Context, closeWith []CloseWith
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update connection %s: %w", cw.ConnectionId, err)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to update connection %s", cw.ConnectionId)))
|
||||
}
|
||||
if rowsUpdated != 1 {
|
||||
return fmt.Errorf("%d would have been updated for connection %s", rowsUpdated, cw.ConnectionId)
|
||||
return errors.New(errors.MultipleRecords, op, fmt.Sprintf("%d would have been updated for connection %s", rowsUpdated, cw.ConnectionId))
|
||||
}
|
||||
states, err := fetchConnectionStates(ctx, reader, cw.ConnectionId, db.WithOrder("start_time desc"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
resp = append(resp, CloseConnectionResp{
|
||||
Connection: &updateConnection,
|
||||
@@ -558,7 +571,7 @@ func (r *Repository) CloseConnections(ctx context.Context, closeWith []CloseWith
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("close connections: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -566,23 +579,24 @@ func (r *Repository) CloseConnections(ctx context.Context, closeWith []CloseWith
|
||||
// ActivateSession will activate the session and is called by a worker after
|
||||
// authenticating the session. The session must be in a "pending" state to be
|
||||
// activated. States are ordered by start time descending. Returns an
|
||||
// ErrSessionNotPending error if a connection cannot be made because the session
|
||||
// InvalidSessionState error code if a connection cannot be made because the session
|
||||
// was canceled or terminated.
|
||||
func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sessionVersion uint32, serverId, serverType string, tofuToken []byte) (*Session, []*State, error) {
|
||||
const op = "session.(Repository).ActivateSession"
|
||||
if sessionId == "" {
|
||||
return nil, nil, fmt.Errorf("activate session: missing session id: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
if sessionVersion == 0 {
|
||||
return nil, nil, fmt.Errorf("activate session: version cannot be zero: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if serverId == "" {
|
||||
return nil, nil, fmt.Errorf("activate session: missing server id: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing server id")
|
||||
}
|
||||
if serverType == "" {
|
||||
return nil, nil, fmt.Errorf("activate session: missing server type: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing server type")
|
||||
}
|
||||
if len(tofuToken) == 0 {
|
||||
return nil, nil, fmt.Errorf("activate session: missing tofu token: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing tofu token")
|
||||
}
|
||||
|
||||
updatedSession := AllocSession()
|
||||
@@ -595,48 +609,48 @@ func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sess
|
||||
func(reader db.Reader, w db.Writer) error {
|
||||
rowsAffected, err := w.Exec(ctx, activateStateCte, []interface{}{sessionId, sessionVersion})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to activate session %s: %w", sessionId, err)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to activate session %s", sessionId)))
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("unable to activate session %s: %w", sessionId, ErrSessionNotPending)
|
||||
return errors.New(errors.InvalidSessionState, op, "session is not in a pending state")
|
||||
}
|
||||
foundSession := AllocSession()
|
||||
foundSession.PublicId = sessionId
|
||||
if err := reader.LookupById(ctx, &foundSession); err != nil {
|
||||
return fmt.Errorf("lookup session: failed for %s: %w", sessionId, err)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", sessionId)))
|
||||
}
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, foundSession.ScopeId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get database wrapper: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if len(foundSession.TofuToken) > 0 && subtle.ConstantTimeCompare(foundSession.TofuToken, tofuToken) != 1 {
|
||||
return fmt.Errorf("tofu token mismatch")
|
||||
return errors.New(errors.TokenMismatch, op, "tofu token mismatch")
|
||||
}
|
||||
|
||||
updatedSession.TofuToken = tofuToken
|
||||
updatedSession.ServerId = serverId
|
||||
updatedSession.ServerType = serverType
|
||||
if err := updatedSession.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
rowsUpdated, err := w.Update(ctx, &updatedSession, []string{"CtTofuToken"}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
if err == nil && rowsUpdated > 1 {
|
||||
// return err, which will result in a rollback of the update
|
||||
return stderrors.New("error more than 1 session would have been updated ")
|
||||
return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been updated")
|
||||
}
|
||||
|
||||
returnedStates, err = fetchStates(ctx, reader, sessionId, db.WithOrder("start_time desc"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("activate session: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &updatedSession, returnedStates, nil
|
||||
}
|
||||
@@ -644,18 +658,19 @@ func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sess
|
||||
// updateState will update the session's state using the session id and its
|
||||
// version. updateState is idempotent. States are ordered by start time
|
||||
// descending. No options are currently supported.
|
||||
func (r *Repository) updateState(ctx context.Context, sessionId string, sessionVersion uint32, s Status, opt ...Option) (*Session, []*State, error) {
|
||||
func (r *Repository) updateState(ctx context.Context, sessionId string, sessionVersion uint32, s Status, _ ...Option) (*Session, []*State, error) {
|
||||
const op = "session.(Repository).updateState"
|
||||
if sessionId == "" {
|
||||
return nil, nil, fmt.Errorf("update session state: missing session id %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
if sessionVersion == 0 {
|
||||
return nil, nil, fmt.Errorf("update session state: version cannot be zero: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if s == "" {
|
||||
return nil, nil, fmt.Errorf("update session state: missing session status: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing session status")
|
||||
}
|
||||
if s == StatusActive {
|
||||
return nil, nil, fmt.Errorf("update session: you must call ActivateSession to update a session's state to active: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "you must call ActivateSession to update a session's state to active")
|
||||
}
|
||||
|
||||
var rowsAffected int
|
||||
@@ -672,18 +687,18 @@ func (r *Repository) updateState(ctx context.Context, sessionId string, sessionV
|
||||
updatedSession.Version = uint32(sessionVersion) + 1
|
||||
rowsUpdated, err := w.Update(ctx, &updatedSession, []string{"Version"}, nil, db.WithVersion(&sessionVersion))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
if rowsUpdated != 1 {
|
||||
return fmt.Errorf("updated session and %d rows updated", rowsUpdated)
|
||||
return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated session and %d rows updated", rowsUpdated))
|
||||
}
|
||||
if len(updatedSession.CtTofuToken) > 0 {
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, updatedSession.ScopeId, kms.KeyPurposeDatabase, kms.WithKeyId(updatedSession.KeyId))
|
||||
if err != nil {
|
||||
return fmt.Errorf("lookup session: unable to get database wrapper: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := updatedSession.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return fmt.Errorf("lookup session: cannot decrypt session value: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithMsg("cannot decrypt session value"))
|
||||
}
|
||||
} else {
|
||||
updatedSession.CtTofuToken = nil
|
||||
@@ -691,31 +706,32 @@ func (r *Repository) updateState(ctx context.Context, sessionId string, sessionV
|
||||
|
||||
rowsAffected, err = w.Exec(ctx, updateSessionState, []interface{}{sessionId, s.String()})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update session %s state to %s: %w", sessionId, s.String(), err)
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to update session %s state to %s", sessionId, s.String())))
|
||||
}
|
||||
if rowsAffected != 0 && rowsAffected != 1 {
|
||||
return fmt.Errorf("updated session %s to state %s and %d rows inserted (should be 0 or 1)", sessionId, s.String(), rowsAffected)
|
||||
return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated session %s to state %s and %d rows inserted (should be 0 or 1)", sessionId, s.String(), rowsAffected))
|
||||
}
|
||||
returnedStates, err = fetchStates(ctx, reader, sessionId, db.WithOrder("start_time desc"))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
if len(returnedStates) < 1 && returnedStates[0].Status != s {
|
||||
return fmt.Errorf("failed to update %s to a state of %s", sessionId, s.String())
|
||||
return errors.New(errors.InvalidSessionState, op, fmt.Sprintf("failed to update %s to a state of %s", sessionId, s.String()))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("update session state: error creating new state: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("error creating new state"))
|
||||
}
|
||||
return &updatedSession, returnedStates, nil
|
||||
}
|
||||
|
||||
func fetchStates(ctx context.Context, r db.Reader, sessionId string, opt ...db.Option) ([]*State, error) {
|
||||
const op = "session.fetchStates"
|
||||
var states []*State
|
||||
if err := r.SearchWhere(ctx, &states, "session_id = ?", []interface{}{sessionId}, opt...); err != nil {
|
||||
return nil, fmt.Errorf("fetch session states: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if len(states) == 0 {
|
||||
return nil, nil
|
||||
|
||||
@@ -234,7 +234,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
wantIsError errors.Code
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
@@ -253,7 +253,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-hostId",
|
||||
@@ -265,7 +265,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-targetId",
|
||||
@@ -277,7 +277,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-hostSetId",
|
||||
@@ -289,7 +289,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-authTokenId",
|
||||
@@ -301,7 +301,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-scopeId",
|
||||
@@ -313,7 +313,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -334,9 +334,7 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
if tt.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(ses)
|
||||
if tt.wantIsError != nil {
|
||||
assert.True(errors.Is(err, tt.wantIsError))
|
||||
}
|
||||
assert.True(errors.Match(errors.T(tt.wantIsError), err))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -382,7 +380,7 @@ func TestRepository_updateState(t *testing.T) {
|
||||
overrideSessionVersion *uint32
|
||||
wantStateCnt int
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
wantIsError errors.Code
|
||||
}{
|
||||
{
|
||||
name: "canceling",
|
||||
@@ -421,7 +419,7 @@ func TestRepository_updateState(t *testing.T) {
|
||||
return &v
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "bad-sessionId",
|
||||
@@ -442,7 +440,7 @@ func TestRepository_updateState(t *testing.T) {
|
||||
return &s
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -466,9 +464,7 @@ func TestRepository_updateState(t *testing.T) {
|
||||
s, ss, err := repo.updateState(context.Background(), id, version, tt.newStatus)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
if tt.wantIsError != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsError), "unexpected error %s", err.Error())
|
||||
}
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -601,7 +597,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
name string
|
||||
connectWith ConnectWith
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
wantIsError errors.Code
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
@@ -615,7 +611,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
return cw
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-ClientTcpAddress",
|
||||
@@ -625,7 +621,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
return cw
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-ClientTcpPort",
|
||||
@@ -635,7 +631,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
return cw
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-EndpointTcpAddress",
|
||||
@@ -645,7 +641,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
return cw
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-EndpointTcpPort",
|
||||
@@ -655,7 +651,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
return cw
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -665,9 +661,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
c, cs, err := repo.ConnectConnection(context.Background(), tt.connectWith)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
if tt.wantIsError != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsError), "unexpected error %s", err.Error())
|
||||
}
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -701,7 +695,7 @@ func TestRepository_TerminateSession(t *testing.T) {
|
||||
session *Session
|
||||
reason TerminationReason
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
wantIsError errors.Code
|
||||
}{
|
||||
{
|
||||
name: "valid-active-session",
|
||||
@@ -722,7 +716,7 @@ func TestRepository_TerminateSession(t *testing.T) {
|
||||
}(),
|
||||
reason: ClosedByUser,
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-session-version",
|
||||
@@ -733,7 +727,7 @@ func TestRepository_TerminateSession(t *testing.T) {
|
||||
}(),
|
||||
reason: ClosedByUser,
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "open-connection",
|
||||
@@ -752,9 +746,7 @@ func TestRepository_TerminateSession(t *testing.T) {
|
||||
s, err := repo.TerminateSession(context.Background(), tt.session.PublicId, tt.session.Version, tt.reason)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
if tt.wantIsError != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsError), "unexpected error %s", err.Error())
|
||||
}
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -1037,7 +1029,7 @@ func TestRepository_CloseConnections(t *testing.T) {
|
||||
closeWith []CloseWith
|
||||
reason TerminationReason
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
wantIsError errors.Code
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
@@ -1049,7 +1041,7 @@ func TestRepository_CloseConnections(t *testing.T) {
|
||||
closeWith: []CloseWith{},
|
||||
reason: ClosedByUser,
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "missing-ConnectionId",
|
||||
@@ -1060,7 +1052,7 @@ func TestRepository_CloseConnections(t *testing.T) {
|
||||
}(),
|
||||
reason: ClosedByUser,
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -1069,9 +1061,7 @@ func TestRepository_CloseConnections(t *testing.T) {
|
||||
resp, err := repo.CloseConnections(context.Background(), tt.closeWith)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
if tt.wantIsError != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsError), "unexpected error %s", err.Error())
|
||||
}
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -1105,7 +1095,7 @@ func TestRepository_CancelSession(t *testing.T) {
|
||||
overrideSessionId *string
|
||||
overrideSessionVersion *uint32
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
wantIsError errors.Code
|
||||
wantStatus Status
|
||||
}{
|
||||
{
|
||||
@@ -1153,7 +1143,7 @@ func TestRepository_CancelSession(t *testing.T) {
|
||||
}(),
|
||||
wantStatus: StatusCanceling,
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "bad-version-id",
|
||||
@@ -1174,7 +1164,7 @@ func TestRepository_CancelSession(t *testing.T) {
|
||||
}(),
|
||||
wantStatus: StatusCanceling,
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -1197,9 +1187,7 @@ func TestRepository_CancelSession(t *testing.T) {
|
||||
s, err := repo.CancelSession(context.Background(), id, version)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
if tt.wantIsError != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsError), "unexpected error %s", err.Error())
|
||||
}
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -1418,7 +1406,7 @@ func TestRepository_ActivateSession(t *testing.T) {
|
||||
overrideSessionId *string
|
||||
overrideSessionVersion *uint32
|
||||
wantErr bool
|
||||
wantIsError error
|
||||
wantIsError errors.Code
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
@@ -1462,7 +1450,7 @@ func TestRepository_ActivateSession(t *testing.T) {
|
||||
return &id
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-session-version",
|
||||
@@ -1472,7 +1460,7 @@ func TestRepository_ActivateSession(t *testing.T) {
|
||||
return &v
|
||||
}(),
|
||||
wantErr: true,
|
||||
wantIsError: errors.ErrInvalidParameter,
|
||||
wantIsError: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -1495,9 +1483,7 @@ func TestRepository_ActivateSession(t *testing.T) {
|
||||
s, ss, err := repo.ActivateSession(context.Background(), id, version, worker.PrivateId, worker.Type, tofu)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
if tt.wantIsError != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsError), "unexpected error %s", err.Error())
|
||||
}
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -1565,7 +1551,7 @@ func TestRepository_DeleteSession(t *testing.T) {
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantErrMsg: "delete session: missing public id invalid parameter",
|
||||
wantErrMsg: "session.(Repository).DeleteSession: missing public id: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "not-found",
|
||||
@@ -1580,7 +1566,7 @@ func TestRepository_DeleteSession(t *testing.T) {
|
||||
},
|
||||
wantRowsDeleted: 0,
|
||||
wantErr: true,
|
||||
wantErrMsg: "delete session: failed db.LookupById: record not found",
|
||||
wantErrMsg: "db.LookupById: record not found",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
+49
-44
@@ -5,7 +5,6 @@ import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"math/big"
|
||||
mathrand "math/rand"
|
||||
"strings"
|
||||
@@ -119,7 +118,8 @@ var (
|
||||
)
|
||||
|
||||
// New creates a new in memory session.
|
||||
func New(c ComposedOf, opt ...Option) (*Session, error) {
|
||||
func New(c ComposedOf, _ ...Option) (*Session, error) {
|
||||
const op = "session.New"
|
||||
s := Session{
|
||||
UserId: c.UserId,
|
||||
HostId: c.HostId,
|
||||
@@ -132,8 +132,8 @@ func New(c ComposedOf, opt ...Option) (*Session, error) {
|
||||
ConnectionLimit: c.ConnectionLimit,
|
||||
WorkerFilter: c.WorkerFilter,
|
||||
}
|
||||
if err := s.validateNewSession("new session:"); err != nil {
|
||||
return nil, err
|
||||
if err := s.validateNewSession(); err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
@@ -209,50 +209,51 @@ func (s *Session) Clone() interface{} {
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the session
|
||||
// before it's written.
|
||||
func (s *Session) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
func (s *Session) VetForWrite(_ context.Context, _ db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
const op = "session.(Session).VetForWrite"
|
||||
opts := db.GetOpts(opt...)
|
||||
if s.PublicId == "" {
|
||||
return fmt.Errorf("session vet for write: missing public id: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing public id")
|
||||
}
|
||||
switch opType {
|
||||
case db.CreateOp:
|
||||
if err := s.validateNewSession("session vet for write:"); err != nil {
|
||||
return err
|
||||
if err := s.validateNewSession(); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
if len(s.Certificate) == 0 {
|
||||
return fmt.Errorf("session vet for write: certificate is missing: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing certificate")
|
||||
}
|
||||
case db.UpdateOp:
|
||||
switch {
|
||||
case contains(opts.WithFieldMaskPaths, "PublicId"):
|
||||
return fmt.Errorf("session vet for write: public id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "public id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "UserId"):
|
||||
return fmt.Errorf("session vet for write: user id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "user id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "HostId"):
|
||||
return fmt.Errorf("session vet for write: host id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "host id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "TargetId"):
|
||||
return fmt.Errorf("session vet for write: target id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "target id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "HostSetId"):
|
||||
return fmt.Errorf("session vet for write: host set id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "host set id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "AuthTokenId"):
|
||||
return fmt.Errorf("session vet for write: auth token id is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "auth token id is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "Certificate"):
|
||||
return fmt.Errorf("session vet for write: certificate is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "certificate is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "CreateTime"):
|
||||
return fmt.Errorf("session vet for write: create time is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "create time is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "UpdateTime"):
|
||||
return fmt.Errorf("session vet for write: update time is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "update time is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "Endpoint"):
|
||||
return fmt.Errorf("session vet for write: endpoint is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "endpoint is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "ExpirationTime"):
|
||||
return fmt.Errorf("session vet for write: expiration time is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "expiration time is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "ConnectionLimit"):
|
||||
return fmt.Errorf("session vet for write: connection limit is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "connection limit is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "WorkerFilter"):
|
||||
return fmt.Errorf("session vet for write: worker filter is immutable: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "worker filter is immutable")
|
||||
case contains(opts.WithFieldMaskPaths, "TerminationReason"):
|
||||
if _, err := convertToReason(s.TerminationReason); err != nil {
|
||||
return fmt.Errorf("session vet for write: termination reason '%s' is invalid: %w", s.TerminationReason, errors.ErrInvalidParameter)
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,45 +276,46 @@ func (s *Session) SetTableName(n string) {
|
||||
}
|
||||
|
||||
// validateNewSession checks everything but the session's PublicId
|
||||
func (s *Session) validateNewSession(errorPrefix string) error {
|
||||
func (s *Session) validateNewSession() error {
|
||||
const op = "session.(Session).validateNewSession"
|
||||
if s.UserId == "" {
|
||||
return fmt.Errorf("%s missing user id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing user id")
|
||||
}
|
||||
if s.HostId == "" {
|
||||
return fmt.Errorf("%s missing host id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing host id")
|
||||
}
|
||||
if s.TargetId == "" {
|
||||
return fmt.Errorf("%s missing target id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing target id")
|
||||
}
|
||||
if s.HostSetId == "" {
|
||||
return fmt.Errorf("%s missing host set id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing host set id")
|
||||
}
|
||||
if s.AuthTokenId == "" {
|
||||
return fmt.Errorf("%s missing auth token id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing auth token id")
|
||||
}
|
||||
if s.ScopeId == "" {
|
||||
return fmt.Errorf("%s missing scope id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
if s.Endpoint == "" {
|
||||
return fmt.Errorf("%s missing endpoint: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing endpoint")
|
||||
}
|
||||
if s.ExpirationTime.GetTimestamp().AsTime().IsZero() {
|
||||
return fmt.Errorf("%s missing expiration time: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing expiration time")
|
||||
}
|
||||
if s.TerminationReason != "" {
|
||||
return fmt.Errorf("%s termination reason must be empty: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "termination reason must be empty")
|
||||
}
|
||||
if s.ServerId != "" {
|
||||
return fmt.Errorf("%s server id must be empty: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "server id must be empty")
|
||||
}
|
||||
if s.ServerType != "" {
|
||||
return fmt.Errorf("%s server type must be empty: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "server type must be empty")
|
||||
}
|
||||
if s.TofuToken != nil {
|
||||
return fmt.Errorf("%s tofu token must be empty: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "tofu token must be empty")
|
||||
}
|
||||
if s.CtTofuToken != nil {
|
||||
return fmt.Errorf("%s ct must be empty: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "ct must be empty")
|
||||
}
|
||||
// It is okay for the worker filter to be empty, so it is not checked here.
|
||||
return nil
|
||||
@@ -329,18 +331,19 @@ func contains(ss []string, t string) bool {
|
||||
}
|
||||
|
||||
func newCert(wrapper wrapping.Wrapper, userId, jobId string, exp time.Time) (ed25519.PrivateKey, []byte, error) {
|
||||
const op = "session.newCert"
|
||||
if wrapper == nil {
|
||||
return nil, nil, fmt.Errorf("new session cert: missing wrapper: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing wrapper")
|
||||
}
|
||||
if userId == "" {
|
||||
return nil, nil, fmt.Errorf("new session cert: missing user id: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing user id")
|
||||
}
|
||||
if jobId == "" {
|
||||
return nil, nil, fmt.Errorf("new session cert: missing job id: %w", errors.ErrInvalidParameter)
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "missing job id")
|
||||
}
|
||||
pubKey, privKey, err := DeriveED25519Key(wrapper, userId, jobId)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("new session cert: ")
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
template := &x509.Certificate{
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
@@ -358,22 +361,24 @@ func newCert(wrapper wrapping.Wrapper, userId, jobId string, exp time.Time) (ed2
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, pubKey, privKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("new session cert: %w", err)
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithCode(errors.GenCert))
|
||||
}
|
||||
return privKey, certBytes, nil
|
||||
}
|
||||
|
||||
func (s *Session) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "session.(Session).encrypt"
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, s, nil); err != nil {
|
||||
return fmt.Errorf("error encrypting session: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
s.KeyId = cipher.KeyID()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "session.(Session).decrypt"
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, s, nil); err != nil {
|
||||
return fmt.Errorf("error decrypting session: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithCode(errors.Decrypt))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
)
|
||||
|
||||
@@ -17,20 +15,21 @@ type ConnectWith struct {
|
||||
}
|
||||
|
||||
func (c ConnectWith) validate() error {
|
||||
const op = "session.(ConnectWith).validate"
|
||||
if c.ConnectionId == "" {
|
||||
return fmt.Errorf("missing session id: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
if c.ClientTcpAddress == "" {
|
||||
return fmt.Errorf("missing client tcp address: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing client tcp address")
|
||||
}
|
||||
if c.ClientTcpPort == 0 {
|
||||
return fmt.Errorf("missing client ctp port: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing client ctp port")
|
||||
}
|
||||
if c.EndpointTcpAddress == "" {
|
||||
return fmt.Errorf("missing endpoint tcp address: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing endpoint tcp address")
|
||||
}
|
||||
if c.EndpointTcpPort == 0 {
|
||||
return fmt.Errorf("missing endpoint ctp port: %w", errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing endpoint ctp port")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -34,7 +34,7 @@ func TestSession_Create(t *testing.T) {
|
||||
args args
|
||||
want *Session
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
wantIsErr errors.Code
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
@@ -67,7 +67,7 @@ func TestSession_Create(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-hostId",
|
||||
@@ -79,7 +79,7 @@ func TestSession_Create(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-targetId",
|
||||
@@ -91,7 +91,7 @@ func TestSession_Create(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-hostSetId",
|
||||
@@ -103,7 +103,7 @@ func TestSession_Create(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-authTokenId",
|
||||
@@ -115,7 +115,7 @@ func TestSession_Create(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-scopeId",
|
||||
@@ -127,7 +127,7 @@ func TestSession_Create(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -136,7 +136,7 @@ func TestSession_Create(t *testing.T) {
|
||||
got, err := New(tt.args.composedOf)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(err, tt.wantIsErr))
|
||||
assert.True(errors.Match(errors.T(tt.wantIsErr), err))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
+15
-13
@@ -2,7 +2,6 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
@@ -69,14 +68,15 @@ var (
|
||||
|
||||
// NewState creates a new in memory session state. No options
|
||||
// are currently supported.
|
||||
func NewState(session_id string, state Status, opt ...Option) (*State, error) {
|
||||
func NewState(session_id string, state Status, _ ...Option) (*State, error) {
|
||||
const op = "session.NewState"
|
||||
s := State{
|
||||
SessionId: session_id,
|
||||
Status: state,
|
||||
}
|
||||
|
||||
if err := s.validate("new session state:"); err != nil {
|
||||
return nil, err
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
@@ -122,9 +122,10 @@ func (s *State) Clone() interface{} {
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface and validates the state
|
||||
// before it's written.
|
||||
func (s *State) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if err := s.validate("session state vet for write:"); err != nil {
|
||||
return err
|
||||
func (s *State) VetForWrite(_ context.Context, _ db.Reader, _ db.OpType, _ ...db.Option) error {
|
||||
const op = "session.(State).VetForWrite"
|
||||
if err := s.validate(); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -145,21 +146,22 @@ func (s *State) SetTableName(n string) {
|
||||
}
|
||||
|
||||
// validate checks the session state
|
||||
func (s *State) validate(errorPrefix string) error {
|
||||
func (s *State) validate() error {
|
||||
const op = "session.(State).validate"
|
||||
if s.Status == "" {
|
||||
return fmt.Errorf("%s missing status: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing status")
|
||||
}
|
||||
if s.SessionId == "" {
|
||||
return fmt.Errorf("%s missing session id: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "missing session id")
|
||||
}
|
||||
if s.StartTime != nil {
|
||||
return fmt.Errorf("%s start time is not settable: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "start time is not settable")
|
||||
}
|
||||
if s.EndTime != nil {
|
||||
return fmt.Errorf("%s end time is not settable: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "end time is not settable")
|
||||
}
|
||||
if s.PreviousEndTime != nil {
|
||||
return fmt.Errorf("%s previous end time is not settable: %w", errorPrefix, errors.ErrInvalidParameter)
|
||||
return errors.New(errors.InvalidParameter, op, "previous end time is not settable")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func TestState_Create(t *testing.T) {
|
||||
args args
|
||||
want *State
|
||||
wantErr bool
|
||||
wantIsErr error
|
||||
wantIsErr errors.Code
|
||||
create bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
@@ -49,7 +49,7 @@ func TestState_Create(t *testing.T) {
|
||||
status: StatusPending,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "empty-status",
|
||||
@@ -57,7 +57,7 @@ func TestState_Create(t *testing.T) {
|
||||
sessionId: session.PublicId,
|
||||
},
|
||||
wantErr: true,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -66,7 +66,7 @@ func TestState_Create(t *testing.T) {
|
||||
got, err := NewState(tt.args.sessionId, tt.args.status)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.True(errors.Is(err, tt.wantIsErr))
|
||||
assert.True(errors.Match(errors.T(tt.wantIsErr), err))
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
@@ -26,6 +26,7 @@ func (r TerminationReason) String() string {
|
||||
}
|
||||
|
||||
func convertToReason(s string) (TerminationReason, error) {
|
||||
const op = "session.convertToReason"
|
||||
switch s {
|
||||
case UnknownTermination.String():
|
||||
return UnknownTermination, nil
|
||||
@@ -42,6 +43,6 @@ func convertToReason(s string) (TerminationReason, error) {
|
||||
case ConnectionLimit.String():
|
||||
return ConnectionLimit, nil
|
||||
default:
|
||||
return "", fmt.Errorf("termination reason: %s is not a valid reason: %w", s, errors.ErrInvalidParameter)
|
||||
return "", errors.New(errors.InvalidParameter, op, fmt.Sprintf("%s is not a valid reason", s))
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ package session
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
|
||||
"github.com/hashicorp/go-kms-wrapping/wrappers/multiwrapper"
|
||||
@@ -15,23 +16,28 @@ import (
|
||||
// DeriveED25519Key generates a key based on the scope's session DEK, the
|
||||
// requesting user, and the generated job ID.
|
||||
func DeriveED25519Key(wrapper wrapping.Wrapper, userId, jobId string) (ed25519.PublicKey, ed25519.PrivateKey, error) {
|
||||
const op = "session.DeriveED25519Key"
|
||||
var aeadWrapper *aead.Wrapper
|
||||
switch w := wrapper.(type) {
|
||||
case *multiwrapper.MultiWrapper:
|
||||
raw := w.WrapperForKeyID("__base__")
|
||||
var ok bool
|
||||
if aeadWrapper, ok = raw.(*aead.Wrapper); !ok {
|
||||
return nil, nil, errors.New("unexpected wrapper type from multiwrapper base")
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, "unexpected wrapper type from multiwrapper base")
|
||||
}
|
||||
case *aead.Wrapper:
|
||||
aeadWrapper = w
|
||||
default:
|
||||
return nil, nil, errors.New("unknown wrapper type")
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("unknown wrapper type %T", wrapper))
|
||||
}
|
||||
reader := hkdf.New(sha256.New, aeadWrapper.GetKeyBytes(), []byte(jobId), []byte(userId))
|
||||
limitedReader := &io.LimitedReader{
|
||||
R: reader,
|
||||
N: 32,
|
||||
}
|
||||
return ed25519.GenerateKey(limitedReader)
|
||||
pub, pvt, err := ed25519.GenerateKey(limitedReader)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithCode(errors.GenKey))
|
||||
}
|
||||
return pub, pvt, nil
|
||||
}
|
||||
Reference in new issue
Block a user