Skip to content

Commit b0c41c7

Browse files
committed
Support versioning in Broker API and authd
Adds a com.ubuntu.authd.Broker2 D-Bus interface in accordance with https://dbus.freedesktop.org/doc/dbus-api-design.html#api-versioning Also authd now properly considers the possibility of multiple interfaces and, for now, chooses the latest one available.
1 parent c68d003 commit b0c41c7

12 files changed

Lines changed: 352 additions & 61 deletions

File tree

authd-oidc-brokers/cmd/authd-oidc/daemon/daemon.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,12 @@ func (a *App) serve(config daemonConfig) error {
163163
}
164164
}
165165

166-
b, err := broker.New(broker.Config{
166+
brokerConfig := broker.Config{
167167
ConfigFile: config.Paths.BrokerConf,
168168
DataDir: config.Paths.DataDir,
169-
})
170-
if err != nil {
171-
return err
172169
}
173170

174-
s, err := dbusservice.New(ctx, b)
171+
s, err := dbusservice.New(ctx, brokerConfig)
175172
if err != nil {
176173
return err
177174
}

authd-oidc-brokers/internal/broker/broker.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ import (
3535
)
3636

3737
const (
38+
// LatestAPIVersion is the latest API version supported by the broker. It should be incremented when a non backward
39+
// compatible change is made to the API.
40+
LatestAPIVersion = 2
41+
3842
maxAuthAttempts = 3
3943
maxRequestDuration = 5 * time.Second
4044
)
@@ -49,7 +53,8 @@ type Config struct {
4953

5054
// Broker is the real implementation of the broker to track sessions and process oidc calls.
5155
type Broker struct {
52-
cfg Config
56+
cfg Config
57+
apiVersion uint
5358

5459
provider providers.Provider
5560
oidcCfg oidc.Config
@@ -98,7 +103,7 @@ type option struct {
98103
type Option func(*option)
99104

100105
// New returns a new oidc Broker with the providers listed in the configuration file.
101-
func New(cfg Config, args ...Option) (b *Broker, err error) {
106+
func New(cfg Config, apiVersion uint, args ...Option) (b *Broker, err error) {
102107
p := providers.CurrentProvider()
103108

104109
if cfg.ConfigFile != "" {
@@ -146,6 +151,7 @@ func New(cfg Config, args ...Option) (b *Broker, err error) {
146151

147152
b = &Broker{
148153
cfg: cfg,
154+
apiVersion: apiVersion,
149155
provider: opts.provider,
150156
oidcCfg: oidc.Config{ClientID: clientID},
151157
privateKey: privateKey,

authd-oidc-brokers/internal/broker/broker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestNew(t *testing.T) {
6868
bCfg := &broker.Config{DataDir: tc.dataDir}
6969
bCfg.SetIssuerURL(tc.issuer)
7070
bCfg.SetClientID(tc.clientID)
71-
b, err := broker.New(*bCfg)
71+
b, err := broker.New(*bCfg, broker.LatestAPIVersion)
7272
if tc.wantErr {
7373
require.Error(t, err, "New should have returned an error")
7474
return

authd-oidc-brokers/internal/broker/helper_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func newBrokerForTests(t *testing.T, cfg *brokerForTestConfig) (b *broker.Broker
123123
cfg.SetIssuerURL(issuerURL)
124124
}
125125

126-
b, err := broker.New(cfg.Config, broker.WithCustomProvider(provider))
126+
b, err := broker.New(cfg.Config, broker.LatestAPIVersion, broker.WithCustomProvider(provider))
127127
require.NoError(t, err, "Setup: New should not have returned an error")
128128
return b
129129
}

authd-oidc-brokers/internal/dbusservice/dbusservice.go

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import (
1111
"github.com/godbus/dbus/v5/introspect"
1212
)
1313

14-
const intro = `
15-
<node>
14+
const (
15+
introspectableHeader = `<node>`
16+
introspectableInterface = `
1617
<interface name="%s">
1718
<method name="NewSession">
1819
<arg type="s" direction="in" name="username"/>
@@ -50,51 +51,86 @@ const intro = `
5051
<method name="DeleteUser">
5152
<arg type="s" direction="in" name="username"/>
5253
</method>
53-
</interface>` + introspect.IntrospectDataString + `</node> `
54+
</interface>`
55+
introspectableFooter = introspect.IntrospectDataString + `</node> `
56+
)
5457

55-
// Service is the handler exposing our broker methods on the system bus.
58+
// Service is the object representing the dbus service, which contains the exported interfaces and the necessary
59+
// information to disconnect from the bus and stop the service.
5660
type Service struct {
57-
name string
61+
name string
62+
interfaces []*Interface
63+
disconnect func()
64+
serve chan struct{}
65+
}
66+
67+
// Interface is the object representing a dbus interface, which contains the broker to which delegate the calls and the
68+
// name of the interface itself.
69+
type Interface struct {
70+
iface string
5871
broker *broker.Broker
72+
}
5973

60-
serve chan struct{}
61-
disconnect func()
74+
var interfaceNames = []string{
75+
"com.ubuntu.authd.Broker",
76+
"com.ubuntu.authd.Broker2",
6277
}
6378

6479
// New returns a new dbus service after exporting to the system bus our name.
65-
func New(_ context.Context, broker *broker.Broker) (s *Service, err error) {
80+
func New(_ context.Context, brokerConfig broker.Config) (*Service, error) {
6681
name := consts.DbusName
6782
object := dbus.ObjectPath(consts.DbusObject)
68-
iface := "com.ubuntu.authd.Broker"
69-
s = &Service{
70-
name: name,
71-
broker: broker,
72-
serve: make(chan struct{}),
83+
84+
service := &Service{
85+
name: name,
86+
serve: make(chan struct{}),
7387
}
7488

75-
conn, err := s.getBus()
89+
conn, err := service.getBus()
7690
if err != nil {
7791
return nil, err
7892
}
7993

80-
if err := conn.Export(s, object, iface); err != nil {
81-
return nil, err
94+
var introspectableBody string
95+
for i, iface := range interfaceNames {
96+
b, err := broker.New(brokerConfig, uint(i)+1) // There's no 0 version, so we start from 1.
97+
if err != nil {
98+
service.disconnect()
99+
return nil, fmt.Errorf("error initializing broker for %q: %v", iface, err)
100+
}
101+
102+
s := &Interface{
103+
iface: iface,
104+
broker: b,
105+
}
106+
107+
if err := conn.Export(s, object, iface); err != nil {
108+
service.disconnect()
109+
return nil, err
110+
}
111+
112+
service.interfaces = append(service.interfaces, s)
113+
introspectableBody = introspectableBody + fmt.Sprintf(introspectableInterface, iface)
82114
}
83-
if err := conn.Export(introspect.Introspectable(fmt.Sprintf(intro, iface)), object, "org.freedesktop.DBus.Introspectable"); err != nil {
115+
116+
// Build combined introspection XML for all versioned interfaces and export once.
117+
introspectable := introspect.Introspectable(introspectableHeader + introspectableBody + introspectableFooter)
118+
if err := conn.Export(introspectable, object, "org.freedesktop.DBus.Introspectable"); err != nil {
119+
service.disconnect()
84120
return nil, err
85121
}
86122

87123
reply, err := conn.RequestName(consts.DbusName, dbus.NameFlagDoNotQueue)
88124
if err != nil {
89-
s.disconnect()
125+
service.disconnect()
90126
return nil, err
91127
}
92128
if reply != dbus.RequestNameReplyPrimaryOwner {
93-
s.disconnect()
129+
service.disconnect()
94130
return nil, fmt.Errorf("%q is already taken in the bus", name)
95131
}
96132

97-
return s, nil
133+
return service, nil
98134
}
99135

100136
// Addr returns the address of the service.
@@ -117,5 +153,6 @@ func (s *Service) Stop() error {
117153
close(s.serve)
118154
s.disconnect()
119155
}
156+
120157
return nil
121158
}

authd-oidc-brokers/internal/dbusservice/localbus.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
package dbusservice
66

77
import (
8+
"context"
89
"os"
910

1011
"github.com/canonical/authd/authd-oidc-brokers/internal/testutils"
12+
"github.com/canonical/authd/log"
1113
"github.com/godbus/dbus/v5"
1214
)
1315

authd-oidc-brokers/internal/dbusservice/methods.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
// NewSession is the method through which the broker and the daemon will communicate once dbusInterface.NewSession is called.
13-
func (s *Service) NewSession(username, lang, mode string) (sessionID, encryptionKey string, dbusErr *dbus.Error) {
13+
func (s *Interface) NewSession(username, lang, mode string) (sessionID, encryptionKey string, dbusErr *dbus.Error) {
1414
log.Debugf(context.Background(), "Creating new session (username=%s, lang=%s, mode=%s)", username, lang, mode)
1515
sessionID, encryptionKey, err := s.broker.NewSession(username, lang, mode)
1616
if err != nil {
@@ -21,7 +21,7 @@ func (s *Service) NewSession(username, lang, mode string) (sessionID, encryption
2121
}
2222

2323
// GetAuthenticationModes is the method through which the broker and the daemon will communicate once dbusInterface.GetAuthenticationModes is called.
24-
func (s *Service) GetAuthenticationModes(sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, dbusErr *dbus.Error) {
24+
func (s *Interface) GetAuthenticationModes(sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, dbusErr *dbus.Error) {
2525
log.Debugf(context.Background(), "Getting authentication modes for session %s", sessionID)
2626
authenticationModes, err := s.broker.GetAuthenticationModes(sessionID, supportedUILayouts)
2727
if err != nil {
@@ -32,7 +32,7 @@ func (s *Service) GetAuthenticationModes(sessionID string, supportedUILayouts []
3232
}
3333

3434
// SelectAuthenticationMode is the method through which the broker and the daemon will communicate once dbusInterface.SelectAuthenticationMode is called.
35-
func (s *Service) SelectAuthenticationMode(sessionID, authenticationModeName string) (uiLayoutInfo map[string]string, dbusErr *dbus.Error) {
35+
func (s *Interface) SelectAuthenticationMode(sessionID, authenticationModeName string) (uiLayoutInfo map[string]string, dbusErr *dbus.Error) {
3636
log.Debugf(context.Background(), "Selecting authentication mode %s for session %s", authenticationModeName, sessionID)
3737
uiLayoutInfo, err := s.broker.SelectAuthenticationMode(sessionID, authenticationModeName)
3838
if err != nil {
@@ -43,7 +43,7 @@ func (s *Service) SelectAuthenticationMode(sessionID, authenticationModeName str
4343
}
4444

4545
// IsAuthenticated is the method through which the broker and the daemon will communicate once dbusInterface.IsAuthenticated is called.
46-
func (s *Service) IsAuthenticated(sessionID, authenticationData string) (access, data string, dbusErr *dbus.Error) {
46+
func (s *Interface) IsAuthenticated(sessionID, authenticationData string) (access, data string, dbusErr *dbus.Error) {
4747
// Do *not* log authenticationData here, because it may contain the user's password in cleartext.
4848
log.Debugf(context.Background(), "Handling IsAuthenticated call for session %s", sessionID)
4949
access, data, err := s.broker.IsAuthenticated(sessionID, authenticationData)
@@ -59,7 +59,7 @@ func (s *Service) IsAuthenticated(sessionID, authenticationData string) (access,
5959
}
6060

6161
// EndSession is the method through which the broker and the daemon will communicate once dbusInterface.EndSession is called.
62-
func (s *Service) EndSession(sessionID string) (dbusErr *dbus.Error) {
62+
func (s *Interface) EndSession(sessionID string) (dbusErr *dbus.Error) {
6363
log.Debugf(context.Background(), "Ending session %s", sessionID)
6464
err := s.broker.EndSession(sessionID)
6565
if err != nil {
@@ -69,14 +69,14 @@ func (s *Service) EndSession(sessionID string) (dbusErr *dbus.Error) {
6969
}
7070

7171
// CancelIsAuthenticated is the method through which the broker and the daemon will communicate once dbusInterface.CancelIsAuthenticated is called.
72-
func (s *Service) CancelIsAuthenticated(sessionID string) (dbusErr *dbus.Error) {
72+
func (s *Interface) CancelIsAuthenticated(sessionID string) (dbusErr *dbus.Error) {
7373
log.Debugf(context.Background(), "Cancelling IsAuthenticated call for session %s", sessionID)
7474
s.broker.CancelIsAuthenticated(sessionID)
7575
return nil
7676
}
7777

7878
// UserPreCheck is the method through which the broker and the daemon will communicate once dbusInterface.UserPreCheck is called.
79-
func (s *Service) UserPreCheck(username string) (userinfo string, dbusErr *dbus.Error) {
79+
func (s *Interface) UserPreCheck(username string) (userinfo string, dbusErr *dbus.Error) {
8080
log.Debugf(context.Background(), "UserPreCheck: %s", username)
8181
userinfo, err := s.broker.UserPreCheck(username)
8282
if err != nil {
@@ -87,7 +87,7 @@ func (s *Service) UserPreCheck(username string) (userinfo string, dbusErr *dbus.
8787
}
8888

8989
// DeleteUser is the method through which the broker and the daemon will communicate once dbusInterface.DeleteUser is called.
90-
func (s *Service) DeleteUser(username string) (dbusErr *dbus.Error) {
90+
func (s *Interface) DeleteUser(username string) (dbusErr *dbus.Error) {
9191
log.Debugf(context.Background(), "DeleteUser: %s", username)
9292
if err := s.broker.DeleteUser(username); err != nil {
9393
return dbus.MakeFailedError(err)

0 commit comments

Comments
 (0)