mattermost-community-enterp.../notification/notification.go
Claude fad2fe9d3c Initial commit: Mattermost Community Enterprise
Open source implementation of Mattermost Enterprise features:

Authentication & SSO:
- LDAP authentication and sync
- LDAP diagnostics
- SAML 2.0 SSO
- OAuth providers (Google, Office365, OpenID Connect)

Infrastructure:
- Redis-based cluster implementation
- Prometheus metrics
- IP filtering
- Push proxy authentication

Search:
- Bleve search engine (lightweight Elasticsearch alternative)

Compliance & Security:
- Compliance reporting
- Data retention policies
- Message export (Actiance, GlobalRelay, CSV)
- Access control (PAP/PDP)

User Management:
- Account migration (LDAP/SAML)
- ID-loaded push notifications
- Outgoing OAuth connections

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:49:14 +09:00

140 lines
4.6 KiB
Go

// Copyright (c) 2024 Mattermost Community Enterprise
// Notification Interface Implementation (ID-loaded push notifications)
package notification
import (
"net/http"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/v8/channels/store"
)
// NotificationConfig holds configuration for the notification interface
type NotificationConfig struct {
Store store.Store
Config func() *model.Config
Logger mlog.LoggerIFace
}
// NotificationImpl implements the NotificationInterface
type NotificationImpl struct {
store store.Store
config func() *model.Config
logger mlog.LoggerIFace
}
// NewNotificationInterface creates a new notification interface
func NewNotificationInterface(cfg *NotificationConfig) *NotificationImpl {
return &NotificationImpl{
store: cfg.Store,
config: cfg.Config,
logger: cfg.Logger,
}
}
// GetNotificationMessage retrieves the full notification message for an ID-loaded push notification
// This is used when a push notification was sent with minimal data (ID-only) and the client
// needs to fetch the full message content
func (n *NotificationImpl) GetNotificationMessage(rctx request.CTX, ack *model.PushNotificationAck, userID string) (*model.PushNotification, *model.AppError) {
if ack == nil {
return nil, model.NewAppError("GetNotificationMessage", "notification.ack_nil", nil, "Push notification ack is nil", http.StatusBadRequest)
}
if ack.PostId == "" {
return nil, model.NewAppError("GetNotificationMessage", "notification.post_id_missing", nil, "Post ID is missing from ack", http.StatusBadRequest)
}
if n.store == nil {
return nil, model.NewAppError("GetNotificationMessage", "notification.store_not_available", nil, "Store is not available", http.StatusInternalServerError)
}
// Get the post
post, err := n.store.Post().GetSingle(rctx, ack.PostId, false)
if err != nil {
return nil, model.NewAppError("GetNotificationMessage", "notification.post_not_found", map[string]any{"PostId": ack.PostId}, err.Error(), http.StatusNotFound)
}
// Verify the user has access to this post
channel, err := n.store.Channel().Get(post.ChannelId, true)
if err != nil {
return nil, model.NewAppError("GetNotificationMessage", "notification.channel_not_found", map[string]any{"ChannelId": post.ChannelId}, err.Error(), http.StatusNotFound)
}
// Check if user is a member of the channel
member, err := n.store.Channel().GetMember(rctx, post.ChannelId, userID)
if err != nil || member == nil {
return nil, model.NewAppError("GetNotificationMessage", "notification.user_not_member", map[string]any{"UserId": userID, "ChannelId": post.ChannelId}, "User is not a member of the channel", http.StatusForbidden)
}
// Get the sender information
sender, err := n.store.User().Get(rctx.Context(), post.UserId)
if err != nil {
n.logger.Warn("Could not get sender for notification",
mlog.String("sender_id", post.UserId),
mlog.Err(err),
)
}
// Get team information
var teamID string
if channel.TeamId != "" {
teamID = channel.TeamId
}
// Build the push notification
notification := &model.PushNotification{
PostId: post.Id,
ChannelId: post.ChannelId,
ChannelName: channel.DisplayName,
TeamId: teamID,
Message: post.Message,
Type: ack.NotificationType,
IsIdLoaded: true,
}
// Add sender info if available
if sender != nil {
notification.SenderId = sender.Id
notification.SenderName = sender.GetDisplayName(model.ShowNicknameFullName)
}
// Handle root post for threads
if post.RootId != "" {
notification.RootId = post.RootId
}
// Set the category based on notification type
switch ack.NotificationType {
case model.PushTypeMessage:
notification.Category = "CAN_REPLY"
case model.PushTypeClear:
notification.Category = ""
case model.PushTypeUpdateBadge:
notification.Category = ""
default:
notification.Category = "CAN_REPLY"
}
n.logger.Debug("Retrieved notification message",
mlog.String("post_id", post.Id),
mlog.String("user_id", userID),
mlog.String("channel_id", post.ChannelId),
)
return notification, nil
}
// CheckLicense validates that the license allows ID-loaded push notifications
// For community enterprise, we always allow this feature
func (n *NotificationImpl) CheckLicense() *model.AppError {
// In the community enterprise implementation, we don't require a license
// The official Mattermost enterprise requires a valid license for this feature
n.logger.Debug("CheckLicense called - community enterprise allows ID-loaded notifications without license")
return nil
}