Skip to content

Commit

Permalink
transaction: Define TransactionError struct to provide more detailed …
Browse files Browse the repository at this point in the history
…error

If the transaction fails during start, there's no way to get the error
detail in a programmatic way, so let's inherit from error to allow more
per-type checks.
  • Loading branch information
3v1n0 committed Sep 29, 2023
1 parent 5a2dc34 commit c4e91d9
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
35 changes: 32 additions & 3 deletions transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,10 @@ func StartFunc(service, user string, handler func(Style, string) (string, error)
// transaction provides an interface to the remainder of the API.
func StartConfDir(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) {
if !CheckPamHasStartConfdir() {
return nil, errors.New("StartConfDir() was used, but the pam version on the system is not recent enough")
return nil, &TransactionError{
errors.New("StartConfDir() was used, but the pam version on the system is not recent enough"),
SystemErr,
}
}

return start(service, user, handler, confDir)
Expand All @@ -252,7 +255,10 @@ func start(service, user string, handler ConversationHandler, confDir string) (*
switch handler.(type) {
case BinaryConversationHandler:
if !CheckPamHasBinaryProtocol() {
return nil, errors.New("BinaryConversationHandler() was used, but it is not supported by this platform")
return nil, &TransactionError{
errors.New("BinaryConversationHandler() was used, but it is not supported by this platform"),
SystemErr,
}
}
}
t := &Transaction{
Expand All @@ -276,11 +282,34 @@ func start(service, user string, handler ConversationHandler, confDir string) (*
t.status = C.pam_start_confdir(s, u, t.conv, c, &t.handle)
}
if t.status != Success.toC() {
return nil, t
return nil, &TransactionError{t, ReturnType(t.status)}
}
return t, nil
}

// transactionError is a private interface that is implemented by both
// TransactionError and Transaction
type transactionError interface {
error
Status() ReturnType
}

// TransactionError extends error to provide more detailed information
type TransactionError struct {
error
status ReturnType
}

// Status exposes the ReturnType for the error
func (e *TransactionError) Status() ReturnType {
return e.status
}

// Error pretty prints the error from the status message
func (e *TransactionError) Error() string {
return errors.Join(e.error, ReturnType(e.status)).Error()
}

func (t *Transaction) Error() string {
return t.Status().Error()
}
Expand Down
8 changes: 8 additions & 0 deletions transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
if len(s) == 0 {
t.Fatalf("error #expected an error message")
}
switch et := err.(type) {
case transactionError:
if et.Status() != Abort {
t.Fatalf("error #unexpected status: %v", et.Status())
}
default:
t.Fatalf("error #unexpected type: %v", et)
}
}

func TestPAM_ConfDir_InfoMessage(t *testing.T) {
Expand Down

0 comments on commit c4e91d9

Please sign in to comment.