Skip to content

Commit

Permalink
pam/gdm: Support conversations using JSON data sent to PAM via gdm bi…
Browse files Browse the repository at this point in the history
…nary protocol

Add an utility function to perform GDM-PAM binary conversations using
the custom JSON protocol.

This function does not much per se, but it allows us to do some proper
testing via the dummy module transaction and so we can test how this
data is passed back and forth the two sides, and stress the C <-> go
structures conversions.
  • Loading branch information
3v1n0 committed Dec 2, 2023
1 parent c321b1e commit b14c161
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
28 changes: 28 additions & 0 deletions pam/gdm/conversation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gdm

import "C"

import (
"errors"

"github.com/msteinert/pam"
)

func sendToGdm(pamMTx pam.ModuleTransaction, data []byte) ([]byte, error) {
binReq, err := NewBinaryJSONProtoRequest(data)
if err != nil {
return nil, err
}
defer binReq.Release()
res, err := pamMTx.StartConv(binReq)
if err != nil {
return nil, err
}

binRes, ok := res.(pam.BinaryConvResponse)
if !ok {
return nil, errors.New("returned value is not in binary form")
}
defer binRes.Release()
return binRes.Decode(decodeJSONProtoMessage)
}
107 changes: 107 additions & 0 deletions pam/gdm/conversation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package gdm

import (
"testing"

"github.com/msteinert/pam"
"github.com/stretchr/testify/require"
"github.com/ubuntu/authd/pam/pam_test"
)

func TestSendToGdm(t *testing.T) {
t.Parallel()
t.Cleanup(pam_test.MaybeDoLeakCheck)

testCases := map[string]struct {
value []byte

wantReturn []byte
wantError error
}{
"JSON null data can be sent and received": {
value: []byte(`null`),
},

"JSON number can be sent and received": {
value: []byte(`1.5`),
},

"Single char is sent and received as string": {
value: []byte(`"m"`),
},

"JSON null is returned": {
value: []byte(`"give me 🚫"`),
wantReturn: []byte("null"),
},

"Utf-8 data is sent and returned": {
value: []byte(`"give me 🍕"`),
wantReturn: []byte(`"😋"`),
},

// Error cases
"Error on empty data": {
value: []byte{},
wantError: ErrInvalidJSON,
},

"Error on nil data": {
value: nil,
wantError: ErrInvalidJSON,
},

"Error with empty data returned": {
value: []byte(`"give me 🗑‼"`),
wantReturn: []byte{},
wantError: ErrInvalidJSON,
},

"Error with nil data returned": {
value: []byte(`"give me 🚫"`),
wantReturn: []byte(nil),
wantError: ErrInvalidJSON,
},
}

for name, tc := range testCases {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
t.Cleanup(pam_test.MaybeDoLeakCheck)

convFuncCalled := true
mt := pam_test.NewModuleTransactionDummy(pam.BinaryPointerConversationFunc(
func(ptr pam.BinaryPointer) (pam.BinaryPointer, error) {
convFuncCalled = true
require.NotNil(t, ptr)
req, err := decodeJSONProtoMessage(ptr)
require.NoError(t, err)
require.Equal(t, tc.value, req)
if tc.wantReturn != nil {
msg, err := newJSONProtoMessage(tc.wantReturn)
return pam.BinaryPointer(msg), err
}
msg, err := newJSONProtoMessage(req)
return pam.BinaryPointer(msg), err
}))

data, err := sendToGdm(mt, tc.value)
require.True(t, convFuncCalled)

if tc.wantError != nil {
require.Error(t, tc.wantError, err)
return
}

require.NoError(t, err)

if tc.wantReturn != nil {
require.Equal(t, tc.wantReturn, data)
return
}

require.Equal(t, tc.value, data)
})
}
}

0 comments on commit b14c161

Please sign in to comment.