Skip to content

Commit e57a44e

Browse files
authored
Align connect-time and user-limited subscribe checks with OnSubscribe (#1279)
Channels a client asks for on connect are not subscribed in a namespace with a subscribe stream proxy: like with a subscribe proxy, only the proxy decides on them, as it does for a regular subscribe. A user-limited channel is not allowed for a connection without user ID, also when the channel lists an empty user (e.g. "user#" or "user#42,"). The connect-time check already required a user ID.
1 parent 51ff050 commit e57a44e

2 files changed

Lines changed: 63 additions & 2 deletions

File tree

‎internal/client/handler.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,9 @@ func (h *Handler) OnClientConnecting(
512512
return centrifuge.ConnectReply{}, centrifuge.ErrorInternal
513513
}
514514

515-
if !isPrivateChannel && !chOpts.SubscribeProxyEnabled && validChannelName {
515+
// Channels authorized by a subscribe proxy or a subscribe stream proxy are
516+
// never subscribed here: only the proxy decides on them.
517+
if !isPrivateChannel && !chOpts.SubscribeProxyEnabled && !chOpts.SubscribeStreamProxyEnabled && validChannelName {
516518
if isUserLimitedChannel && (userID != "" && h.cfgContainer.UserAllowed(ch, userID)) {
517519
channelOk = true
518520
} else if chOpts.SubscribeForClient && (userID != "" || chOpts.SubscribeForAnonymous) && !isUserLimitedChannel {
@@ -831,7 +833,7 @@ func (h *Handler) OnSubscribe(c Client, e centrifuge.SubscribeEvent, subscribePr
831833
options = token.Options
832834
allowed = true
833835
options.Source = subsource.SubscriptionToken
834-
} else if isUserLimitedChannel && h.cfgContainer.UserAllowed(e.Channel, c.UserID()) {
836+
} else if isUserLimitedChannel && c.UserID() != "" && h.cfgContainer.UserAllowed(e.Channel, c.UserID()) {
835837
allowed = true
836838
options.Source = subsource.UserLimited
837839
} else if (chOpts.SubscribeProxyEnabled) && !isUserLimitedChannel {

‎internal/client/handler_test.go‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,37 @@ func TestClientConnectNoCredentialsNoTokenAnonymous(t *testing.T) {
229229
require.Equal(t, "", reply.Credentials.UserID)
230230
}
231231

232+
// Channels a client asks for on connect are not subscribed in a namespace with
233+
// a subscribe stream proxy, even when allow_subscribe_for_client is on: only the
234+
// subscribe stream proxy decides on them.
235+
func TestClientConnectingChannelsSubscribeStreamProxy(t *testing.T) {
236+
node := tools.NodeWithMemoryEngine()
237+
defer func() { _ = node.Shutdown(context.Background()) }()
238+
239+
cfg := config.DefaultConfig()
240+
cfg.Client.AllowAnonymousConnectWithoutToken = true
241+
cfg.Channel.WithoutNamespace.SubscribeForClient = true
242+
cfg.Channel.WithoutNamespace.SubscribeForAnonymous = true
243+
cfgContainer, err := config.NewContainer(cfg)
244+
require.NoError(t, err)
245+
h := NewHandler(node, cfgContainer, emptyJWTVerifier(t, cfgContainer), nil, &ProxyMap{})
246+
247+
connect := func() centrifuge.ConnectReply {
248+
reply, err := h.OnClientConnecting(context.Background(), centrifuge.ConnectEvent{
249+
Channels: []string{"room"},
250+
}, nil, false)
251+
require.NoError(t, err)
252+
return reply
253+
}
254+
255+
require.Contains(t, connect().Subscriptions, "room")
256+
257+
cfg.Channel.Proxy.SubscribeStream.Endpoint = "grpc://localhost:12000"
258+
cfg.Channel.WithoutNamespace.SubscribeStreamProxyEnabled = true
259+
require.NoError(t, cfgContainer.Reload(cfg))
260+
require.NotContains(t, connect().Subscriptions, "room")
261+
}
262+
232263
func TestClientConnectWithMalformedToken(t *testing.T) {
233264
node := tools.NodeWithMemoryEngine()
234265
defer func() { _ = node.Shutdown(context.Background()) }()
@@ -1234,6 +1265,34 @@ func TestClientOnSubscribe_UserLimitedChannelDoesNotCallProxy(t *testing.T) {
12341265
require.Equal(t, 1, numProxyCalls)
12351266
}
12361267

1268+
// An anonymous connection is not allowed to subscribe to a user-limited
1269+
// channel which lists an empty user.
1270+
func TestClientOnSubscribe_UserLimitedChannelNotAllowedForAnonymous(t *testing.T) {
1271+
node := tools.NodeWithMemoryEngineNoHandlers()
1272+
defer func() { _ = node.Shutdown(context.Background()) }()
1273+
1274+
cfg := config.DefaultConfig()
1275+
cfg.Channel.WithoutNamespace.UserLimitedChannels = true
1276+
cfgContainer, err := config.NewContainer(cfg)
1277+
require.NoError(t, err)
1278+
1279+
h := NewHandler(node, cfgContainer, nil, nil, &ProxyMap{})
1280+
1281+
for _, ch := range []string{"user#", "user#42,"} {
1282+
_, _, err = h.OnSubscribe(&tools.TestClientMock{
1283+
IDFunc: func() string {
1284+
return "1"
1285+
},
1286+
UserIDFunc: func() string {
1287+
return ""
1288+
},
1289+
}, centrifuge.SubscribeEvent{
1290+
Channel: ch,
1291+
}, nil, nil)
1292+
require.ErrorIs(t, err, centrifuge.ErrorPermissionDenied, ch)
1293+
}
1294+
}
1295+
12371296
func TestClientOnSubscribe_UserLimitedChannelNotAllowedForAnotherUser(t *testing.T) {
12381297
node := tools.NodeWithMemoryEngineNoHandlers()
12391298
defer func() { _ = node.Shutdown(context.Background()) }()

0 commit comments

Comments
 (0)