mirror of
https://github.com/mattermost/mattermost.git
synced 2026-09-24 16:05:00 +08:00
add audit logs to DCR (#34598)
This commit is contained in:
@@ -342,6 +342,9 @@ func getAuthorizedOAuthApps(c *Context, w http.ResponseWriter, r *http.Request)
|
||||
func registerOAuthClient(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
// Session and permission checks removed for DCR endpoint to allow external client registration
|
||||
|
||||
auditRec := c.MakeAuditRecord(model.AuditEventRegisterOAuthClient, model.AuditStatusFail)
|
||||
defer c.LogAuditRec(auditRec)
|
||||
|
||||
var clientRequest model.ClientRegistrationRequest
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&clientRequest); jsonErr != nil {
|
||||
dcrError := model.NewDCRError(model.DCRErrorInvalidClientMetadata, "Invalid JSON in request body")
|
||||
@@ -353,6 +356,18 @@ func registerOAuthClient(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Add DCR request parameters to audit record
|
||||
model.AddEventParameterToAuditRec(auditRec, "redirect_uris", clientRequest.RedirectURIs)
|
||||
if clientRequest.ClientName != nil {
|
||||
model.AddEventParameterToAuditRec(auditRec, "client_name", *clientRequest.ClientName)
|
||||
}
|
||||
if clientRequest.TokenEndpointAuthMethod != nil {
|
||||
model.AddEventParameterToAuditRec(auditRec, "token_endpoint_auth_method", *clientRequest.TokenEndpointAuthMethod)
|
||||
}
|
||||
if clientRequest.ClientURI != nil {
|
||||
model.AddEventParameterToAuditRec(auditRec, "client_uri", *clientRequest.ClientURI)
|
||||
}
|
||||
|
||||
// Check if OAuth service provider is enabled
|
||||
if !*c.App.Config().ServiceSettings.EnableOAuthServiceProvider {
|
||||
dcrError := model.NewDCRError(model.DCRErrorUnsupportedOperation, "OAuth service provider is disabled")
|
||||
@@ -400,6 +415,11 @@ func registerOAuthClient(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
auditRec.Success()
|
||||
auditRec.AddEventResultState(app)
|
||||
auditRec.AddEventObjectType("oauth_app")
|
||||
c.LogAudit("client_id=" + app.Id)
|
||||
|
||||
siteURL := *c.App.Config().ServiceSettings.SiteURL
|
||||
response := app.ToClientRegistrationResponse(siteURL)
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ package api4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -13,6 +15,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/app"
|
||||
)
|
||||
|
||||
func TestCreateOAuthApp(t *testing.T) {
|
||||
@@ -810,3 +813,81 @@ func TestRegisterOAuthClient_PublicClient_Success(t *testing.T) {
|
||||
assert.Equal(t, *request.ClientURI, *response.ClientURI)
|
||||
assert.Equal(t, "user", response.Scope)
|
||||
}
|
||||
|
||||
func TestRegisterOAuthClientAudit(t *testing.T) {
|
||||
logFile, err := os.CreateTemp("", "dcr_audit.log")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(logFile.Name())
|
||||
|
||||
os.Setenv("MM_EXPERIMENTALAUDITSETTINGS_FILEENABLED", "true")
|
||||
os.Setenv("MM_EXPERIMENTALAUDITSETTINGS_FILENAME", logFile.Name())
|
||||
defer os.Unsetenv("MM_EXPERIMENTALAUDITSETTINGS_FILEENABLED")
|
||||
defer os.Unsetenv("MM_EXPERIMENTALAUDITSETTINGS_FILENAME")
|
||||
|
||||
options := []app.Option{app.WithLicense(model.NewTestLicense("advanced_logging"))}
|
||||
th := SetupWithServerOptions(t, options)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.EnableOAuthServiceProvider = true
|
||||
cfg.ServiceSettings.EnableDynamicClientRegistration = model.NewPointer(true)
|
||||
})
|
||||
|
||||
t.Run("Successful DCR registration is audited", func(t *testing.T) {
|
||||
clientName := "Test Audit Client"
|
||||
request := &model.ClientRegistrationRequest{
|
||||
RedirectURIs: []string{"https://example.com/callback"},
|
||||
ClientName: model.NewPointer(clientName),
|
||||
}
|
||||
|
||||
response, resp, err := th.Client.RegisterOAuthClient(context.Background(), request)
|
||||
require.NoError(t, err)
|
||||
CheckCreatedStatus(t, resp)
|
||||
require.NotNil(t, response)
|
||||
|
||||
// Flush audit logs
|
||||
err = th.Server.Audit.Flush()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, logFile.Sync())
|
||||
|
||||
// Read and verify audit log
|
||||
data, err := io.ReadAll(logFile)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, data)
|
||||
|
||||
auditLog := string(data)
|
||||
assert.Contains(t, auditLog, "registerOAuthClient")
|
||||
assert.Contains(t, auditLog, clientName)
|
||||
assert.Contains(t, auditLog, response.ClientID)
|
||||
assert.Contains(t, auditLog, "success")
|
||||
})
|
||||
|
||||
t.Run("Failed DCR registration is audited", func(t *testing.T) {
|
||||
// Truncate log file for this test
|
||||
require.NoError(t, logFile.Truncate(0))
|
||||
_, err := logFile.Seek(0, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Invalid request (missing redirect URIs)
|
||||
request := &model.ClientRegistrationRequest{
|
||||
ClientName: model.NewPointer("Invalid Client"),
|
||||
}
|
||||
|
||||
_, resp, err := th.Client.RegisterOAuthClient(context.Background(), request)
|
||||
require.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
// Flush audit logs
|
||||
err = th.Server.Audit.Flush()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, logFile.Sync())
|
||||
|
||||
// Read and verify audit log
|
||||
data, err := io.ReadAll(logFile)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, data)
|
||||
|
||||
auditLog := string(data)
|
||||
assert.Contains(t, auditLog, "registerOAuthClient")
|
||||
assert.Contains(t, auditLog, "fail")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -222,6 +222,7 @@ const (
|
||||
AuditEventLoginWithOAuth = "loginWithOAuth" // login using OAuth authentication provider
|
||||
AuditEventMobileLoginWithOAuth = "mobileLoginWithOAuth" // mobile application login using OAuth authentication provider
|
||||
AuditEventRegenerateOAuthAppSecret = "regenerateOAuthAppSecret" // regenerate secret key for OAuth app
|
||||
AuditEventRegisterOAuthClient = "registerOAuthClient" // register OAuth client via dynamic client registration (RFC 7591)
|
||||
AuditEventSignupWithOAuth = "signupWithOAuth" // create account using OAuth authentication provider
|
||||
AuditEventUpdateOAuthApp = "updateOAuthApp" // update OAuth app
|
||||
AuditEventUpdateOutgoingOAuthConnection = "updateOutgoingOAuthConnection" // update outgoing OAuth connection
|
||||
|
||||
Reference in New Issue
Block a user