NOISSUE - Allow superadmin to send messages over channel (#3310)

Signed-off-by: Felix Gateru <felix.gateru@gmail.com>
This commit is contained in:
Felix Gateru
2026-01-14 20:38:44 +03:00
committed by GitHub
parent c7996ce6cf
commit 927230dbb4
4 changed files with 57 additions and 8 deletions
+1 -2
View File
@@ -6,7 +6,6 @@ package private
import (
"context"
"github.com/absmach/supermq/auth"
"github.com/absmach/supermq/channels"
dom "github.com/absmach/supermq/domains"
pkgDomains "github.com/absmach/supermq/pkg/domains"
@@ -54,7 +53,7 @@ func (svc service) Authorize(ctx context.Context, req channels.AuthzReq) error {
return err
}
pr := policies.Policy{
Subject: auth.EncodeDomainUserID(req.DomainID, req.ClientID),
Subject: req.ClientID,
SubjectType: policies.UserType,
Object: req.ChannelID,
Permission: permission,
+3 -1
View File
@@ -322,13 +322,15 @@ func TestPublish(t *testing.T) {
authCall := authn.On("Authenticate", mock.Anything, tc.key).Return(tc.authnRes1, tc.authnErr)
domainsCall := domains.On("RetrieveIDByRoute", mock.Anything, mock.Anything).Return(&grpcCommonV1.RetrieveEntityRes{Entity: &grpcCommonV1.EntityBasic{Id: tc.domainID}}, nil)
tc.clientType = policies.ClientType
clientID := tc.clientID
if tc.bearerToken {
tc.clientType = policies.UserType
clientID = policies.EncodeDomainUserID(tc.domainID, tc.clientID)
}
channelsCall := channels.On("Authorize", mock.Anything, &grpcChannelsV1.AuthzReq{
DomainId: tc.domainID,
ChannelId: tc.chanID,
ClientId: tc.clientID,
ClientId: clientID,
ClientType: tc.clientType,
Type: uint32(connections.Publish),
}).Return(tc.authzRes, tc.authzErr)
+6 -3
View File
@@ -230,7 +230,7 @@ func (h *handler) authAccess(ctx context.Context, username, password, domainID,
clientType = policies.ClientType
}
id, err := h.authenticate(ctx, clientType, token)
id, err := h.authenticate(ctx, clientType, token, domainID)
if err != nil {
return "", mgate.NewHTTPProxyError(http.StatusUnauthorized, errors.Wrap(svcerr.ErrAuthentication, err))
}
@@ -258,14 +258,17 @@ func (h *handler) authAccess(ctx context.Context, username, password, domainID,
return id, nil
}
func (h *handler) authenticate(ctx context.Context, authType, token string) (string, error) {
func (h *handler) authenticate(ctx context.Context, authType, token, domainID string) (string, error) {
switch authType {
case policies.UserType:
authnSession, err := h.authn.Authenticate(ctx, token)
if err != nil {
return "", err
}
return authnSession.UserID, nil
if authnSession.Role == smqauthn.AdminRole {
return authnSession.UserID, nil
}
return policies.EncodeDomainUserID(domainID, authnSession.UserID), nil
case policies.ClientType:
authnRes, err := h.clients.Authenticate(ctx, &grpcClientsV1.AuthnReq{Token: token})
if err != nil {
+47 -2
View File
@@ -126,6 +126,7 @@ func TestAuthPublish(t *testing.T) {
domainID string
clientID string
authNToken string
superAdmin bool
authNRes *grpcClientsV1.AuthnRes
authNRes1 smqauthn.Session
authNErr error
@@ -214,6 +215,23 @@ func TestAuthPublish(t *testing.T) {
authZRes: &grpcChannelsV1.AuthzRes{Authorized: true},
err: nil,
},
{
desc: "publish with superadmin token successfully",
session: &tokenSession,
topic: &topic,
authKey: token,
payload: &payload,
status: http.StatusOK,
clientType: policies.UserType,
chanID: chanID,
domainID: domainID,
clientID: userID,
superAdmin: true,
authNRes1: smqauthn.Session{UserID: userID, Role: smqauthn.AdminRole},
authNErr: nil,
authZRes: &grpcChannelsV1.AuthzRes{Authorized: true},
err: nil,
},
{
desc: "publish with invalid token",
session: &invalidTokenSession,
@@ -341,14 +359,19 @@ func TestAuthPublish(t *testing.T) {
ctx = session.NewContext(ctx, tc.session)
}
tc.clientType = policies.ClientType
clientID := tc.clientID
if tc.session != nil && strings.HasPrefix(string(tc.session.Password), apiutil.BearerPrefix) {
tc.clientType = policies.UserType
clientID = policies.EncodeDomainUserID(tc.domainID, tc.clientID)
if tc.superAdmin {
clientID = tc.clientID
}
}
clientsCall := clients.On("Authenticate", ctx, &grpcClientsV1.AuthnReq{Token: tc.authNToken}).Return(tc.authNRes, tc.authNErr)
authCall := authn.On("Authenticate", ctx, mock.Anything).Return(tc.authNRes1, tc.authNErr)
channelsCall := channels.On("Authorize", mock.Anything, &grpcChannelsV1.AuthzReq{
ClientType: tc.clientType,
ClientId: tc.clientID,
ClientId: clientID,
Type: uint32(connections.Publish),
ChannelId: tc.chanID,
DomainId: tc.domainID,
@@ -417,6 +440,7 @@ func TestAuthSubscribe(t *testing.T) {
domainID string
clientID string
authNToken string
superAdmin bool
authNRes *grpcClientsV1.AuthnRes
authNRes1 smqauthn.Session
authNErr error
@@ -501,6 +525,22 @@ func TestAuthSubscribe(t *testing.T) {
authZRes: &grpcChannelsV1.AuthzRes{Authorized: true},
err: nil,
},
{
desc: "subscribe with superadmin token successfully",
session: &tokenSession,
topics: &topics,
authKey: token,
status: http.StatusOK,
clientType: policies.UserType,
chanID: chanID,
domainID: domainID,
clientID: userID,
superAdmin: true,
authNRes1: smqauthn.Session{UserID: userID, Role: smqauthn.AdminRole},
authNErr: nil,
authZRes: &grpcChannelsV1.AuthzRes{Authorized: true},
err: nil,
},
{
desc: "subscribe with invalid token",
session: &invalidTokenSession,
@@ -620,14 +660,19 @@ func TestAuthSubscribe(t *testing.T) {
ctx = session.NewContext(ctx, tc.session)
}
tc.clientType = policies.ClientType
clientID := tc.clientID
if tc.session != nil && strings.HasPrefix(string(tc.session.Password), apiutil.BearerPrefix) {
tc.clientType = policies.UserType
clientID = policies.EncodeDomainUserID(tc.domainID, tc.clientID)
if tc.superAdmin {
clientID = tc.clientID
}
}
clientsCall := clients.On("Authenticate", ctx, &grpcClientsV1.AuthnReq{Token: tc.authNToken}).Return(tc.authNRes, tc.authNErr)
authCall := authn.On("Authenticate", ctx, mock.Anything).Return(tc.authNRes1, tc.authNErr)
channelsCall := channels.On("Authorize", mock.Anything, &grpcChannelsV1.AuthzReq{
ClientType: tc.clientType,
ClientId: tc.clientID,
ClientId: clientID,
Type: uint32(connections.Subscribe),
ChannelId: tc.chanID,
DomainId: tc.domainID,