diff --git a/internal/errors/code.go b/internal/errors/code.go index 5fc50dce0..71e21274a 100644 --- a/internal/errors/code.go +++ b/internal/errors/code.go @@ -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 diff --git a/internal/errors/code_test.go b/internal/errors/code_test.go index 2cc758b97..c2754de3a 100644 --- a/internal/errors/code_test.go +++ b/internal/errors/code_test.go @@ -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, diff --git a/internal/errors/info.go b/internal/errors/info.go index 9dcb13c40..a21fc3370 100644 --- a/internal/errors/info.go +++ b/internal/errors/info.go @@ -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, diff --git a/internal/iam/repository_user.go b/internal/iam/repository_user.go index c6b12fcc1..6146f6dce 100644 --- a/internal/iam/repository_user.go +++ b/internal/iam/repository_user.go @@ -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{ diff --git a/internal/session/connection.go b/internal/session/connection.go index 075cf00cd..296c676ed 100644 --- a/internal/session/connection.go +++ b/internal/session/connection.go @@ -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 } diff --git a/internal/session/connection_closed_reason.go b/internal/session/connection_closed_reason.go index b2bc83564..fe2945ad0 100644 --- a/internal/session/connection_closed_reason.go +++ b/internal/session/connection_closed_reason.go @@ -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)) } } diff --git a/internal/session/connection_closed_with.go b/internal/session/connection_closed_with.go index b187072e5..fb25a3f27 100644 --- a/internal/session/connection_closed_with.go +++ b/internal/session/connection_closed_with.go @@ -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 diff --git a/internal/session/connection_state.go b/internal/session/connection_state.go index bc38a3226..858bf9408 100644 --- a/internal/session/connection_state.go +++ b/internal/session/connection_state.go @@ -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 } diff --git a/internal/session/connection_state_test.go b/internal/session/connection_state_test.go index 30a3e6e25..f92e10a61 100644 --- a/internal/session/connection_state_test.go +++ b/internal/session/connection_state_test.go @@ -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) diff --git a/internal/session/connection_test.go b/internal/session/connection_test.go index 8c209aa87..fa4def6f5 100644 --- a/internal/session/connection_test.go +++ b/internal/session/connection_test.go @@ -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) diff --git a/internal/session/error.go b/internal/session/error.go index c66bf0919..33dc1b5ec 100644 --- a/internal/session/error.go +++ b/internal/session/error.go @@ -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") ) diff --git a/internal/session/ids.go b/internal/session/ids.go index 6f0fc76b8..3f678c7c0 100644 --- a/internal/session/ids.go +++ b/internal/session/ids.go @@ -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 } diff --git a/internal/session/repository.go b/internal/session/repository.go index f4debef40..90edac418 100644 --- a/internal/session/repository.go +++ b/internal/session/repository.go @@ -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 diff --git a/internal/session/repository_connection.go b/internal/session/repository_connection.go index 016eac807..ce0b42e3d 100644 --- a/internal/session/repository_connection.go +++ b/internal/session/repository_connection.go @@ -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 diff --git a/internal/session/repository_connection_test.go b/internal/session/repository_connection_test.go index 212e2b1fd..dcd9add64 100644 --- a/internal/session/repository_connection_test.go +++ b/internal/session/repository_connection_test.go @@ -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 { diff --git a/internal/session/repository_session.go b/internal/session/repository_session.go index 31e5fd236..b0b95095c 100644 --- a/internal/session/repository_session.go +++ b/internal/session/repository_session.go @@ -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 diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 847eefa40..150c38106 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -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 { diff --git a/internal/session/session.go b/internal/session/session.go index 87b364cc0..f91b5e90a 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -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 } diff --git a/internal/session/session_connect_with.go b/internal/session/session_connect_with.go index 9fe5385d6..1e1fc3253 100644 --- a/internal/session/session_connect_with.go +++ b/internal/session/session_connect_with.go @@ -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 } diff --git a/internal/session/session_test.go b/internal/session/session_test.go index b29fa44ab..c78c87402 100644 --- a/internal/session/session_test.go +++ b/internal/session/session_test.go @@ -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) diff --git a/internal/session/state.go b/internal/session/state.go index 3afb8a830..9e9e9e09f 100644 --- a/internal/session/state.go +++ b/internal/session/state.go @@ -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 } diff --git a/internal/session/state_test.go b/internal/session/state_test.go index 635ecfcf4..c19a0c7b5 100644 --- a/internal/session/state_test.go +++ b/internal/session/state_test.go @@ -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) diff --git a/internal/session/term_reason.go b/internal/session/term_reason.go index c37a44a1b..6e75b1f6d 100644 --- a/internal/session/term_reason.go +++ b/internal/session/term_reason.go @@ -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)) } } diff --git a/internal/session/util.go b/internal/session/util.go index 90503d590..02f3d8f1f 100644 --- a/internal/session/util.go +++ b/internal/session/util.go @@ -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 }