mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-07 04:40:05 -06:00
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"strings"
|
||||
@@ -28,6 +29,7 @@ var (
|
||||
_resourceTypeResource = "resource"
|
||||
_resourceTypeSpace = "storagespace"
|
||||
_resourceTypeShare = "share"
|
||||
_resourceTypeGlobal = "global"
|
||||
|
||||
_domain = "userlog"
|
||||
)
|
||||
@@ -116,6 +118,22 @@ func (c *Converter) ConvertEvent(eventid string, event interface{}) (OC10Notific
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertGlobalEvent converts a global event to an OC10Notification
|
||||
func (c *Converter) ConvertGlobalEvent(typ string, data json.RawMessage) (OC10Notification, error) {
|
||||
switch typ {
|
||||
default:
|
||||
return OC10Notification{}, fmt.Errorf("unknown global event type: %s", typ)
|
||||
case "deprovision":
|
||||
var dd DeprovisionData
|
||||
if err := json.Unmarshal(data, &dd); err != nil {
|
||||
return OC10Notification{}, err
|
||||
}
|
||||
|
||||
return c.deprovisionMessage(PlatformDeprovision, dd.DeprovisionDate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *Converter) spaceDeletedMessage(eventid string, executant *user.UserId, spaceid string, spacename string, ts time.Time) (OC10Notification, error) {
|
||||
usr, err := c.getUser(context.Background(), executant)
|
||||
if err != nil {
|
||||
@@ -287,6 +305,28 @@ func (c *Converter) policiesMessage(eventid string, nt NotificationTemplate, exe
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Converter) deprovisionMessage(nt NotificationTemplate, deproDate string) (OC10Notification, error) {
|
||||
subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, c.translationPath, map[string]interface{}{
|
||||
"date": deproDate,
|
||||
})
|
||||
if err != nil {
|
||||
return OC10Notification{}, err
|
||||
}
|
||||
|
||||
return OC10Notification{
|
||||
EventID: "deprovision",
|
||||
Service: c.serviceName,
|
||||
// UserName: executant.GetUsername(), // TODO: do we need the deprovisioner?
|
||||
Timestamp: time.Now().Format(time.RFC3339Nano), // Fake timestamp? Or we store one with the event?
|
||||
ResourceType: _resourceTypeResource,
|
||||
Subject: subj,
|
||||
SubjectRaw: subjraw,
|
||||
Message: msg,
|
||||
MessageRaw: msgraw,
|
||||
MessageDetails: map[string]interface{}{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Converter) authenticate(usr *user.User) (context.Context, error) {
|
||||
if ctx, ok := c.contexts[usr.GetId().GetOpaqueId()]; ok {
|
||||
return ctx, nil
|
||||
|
||||
@@ -57,6 +57,23 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request
|
||||
resp.OCS.Data = append(resp.OCS.Data, noti)
|
||||
}
|
||||
|
||||
glevs, err := ul.GetGlobalEvents()
|
||||
if err != nil {
|
||||
ul.log.Error().Err(err).Int("returned statuscode", http.StatusInternalServerError).Msg("get global events failed")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
for t, data := range glevs {
|
||||
noti, err := conv.ConvertGlobalEvent(t, data)
|
||||
if err != nil {
|
||||
ul.log.Error().Err(err).Str("eventtype", t).Msg("failed to convert event")
|
||||
continue
|
||||
}
|
||||
|
||||
resp.OCS.Data = append(resp.OCS.Data, noti)
|
||||
}
|
||||
|
||||
resp.OCS.Meta.StatusCode = http.StatusOK
|
||||
b, _ := json.Marshal(resp)
|
||||
w.Write(b)
|
||||
@@ -89,6 +106,40 @@ func (ul *UserlogService) HandleSSE(w http.ResponseWriter, r *http.Request) {
|
||||
ul.sse.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// HandlePostEvent is the POST handler for events
|
||||
func (ul *UserlogService) HandlePostEvent(w http.ResponseWriter, r *http.Request) {
|
||||
u, ok := ctx.ContextGetUser(r.Context())
|
||||
if !ok {
|
||||
ul.log.Error().Msg("post: no user in context")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
uid := u.GetId().GetOpaqueId()
|
||||
if uid == "" {
|
||||
ul.log.Error().Msg("post: user in context is broken")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Check user is allowed to do this
|
||||
|
||||
var req PostEventsRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
ul.log.Error().Err(err).Int("returned statuscode", http.StatusBadRequest).Msg("request body is malformed")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ul.StoreGlobalEvent(req.Type, req.Data); err != nil {
|
||||
ul.log.Error().Err(err).Msg("post: error storing global event")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleDeleteEvents is the DELETE handler for events
|
||||
func (ul *UserlogService) HandleDeleteEvents(w http.ResponseWriter, r *http.Request) {
|
||||
u, ok := revactx.ContextGetUser(r.Context())
|
||||
@@ -130,3 +181,14 @@ type GetEventResponseOC10 struct {
|
||||
type DeleteEventsRequest struct {
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
// PostEventsRequest is the expected body for the post request
|
||||
type PostEventsRequest struct {
|
||||
Type string `json:"type"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
// DeprovisionData is the expected `data` for the PostEventsRequest when deprovisioning
|
||||
type DeprovisionData struct {
|
||||
DeprovisionDate string `json:"date"`
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ func NewUserlogService(opts ...Option) (*UserlogService, error) {
|
||||
|
||||
ul.m.Route("/ocs/v2.php/apps/notifications/api/v1/notifications", func(r chi.Router) {
|
||||
r.Get("/", ul.HandleGetEvents)
|
||||
r.Post("/", ul.HandlePostEvent)
|
||||
r.Delete("/", ul.HandleDeleteEvents)
|
||||
|
||||
if !ul.cfg.DisableSSE {
|
||||
@@ -235,6 +236,42 @@ func (ul *UserlogService) DeleteEvents(userid string, evids []string) error {
|
||||
})
|
||||
}
|
||||
|
||||
// StoreGlobalEvent will store a global event that will be returned with each `GetEvents` request
|
||||
func (ul *UserlogService) StoreGlobalEvent(typ string, data json.RawMessage) error {
|
||||
switch typ {
|
||||
default:
|
||||
return fmt.Errorf("unknown event type: %s", typ)
|
||||
case "deprovision":
|
||||
var req DeprovisionData
|
||||
if err := json.Unmarshal(data, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: check for proper time format
|
||||
|
||||
return ul.storeGlobalEvent(typ, req)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// GetGlobalEvents will return all global events
|
||||
func (ul *UserlogService) GetGlobalEvents() (map[string]json.RawMessage, error) {
|
||||
out := make(map[string]json.RawMessage)
|
||||
|
||||
recs, err := ul.store.Read("global-events")
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return out, err
|
||||
}
|
||||
|
||||
if len(recs) > 0 {
|
||||
if err := json.Unmarshal(recs[0].Value, &out); err != nil {
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (ul *UserlogService) addEventToUser(userid string, event events.Event) error {
|
||||
if !ul.cfg.DisableSSE {
|
||||
if err := ul.sendSSE(userid, event); err != nil {
|
||||
@@ -312,6 +349,30 @@ func (ul *UserlogService) alterUserEventList(userid string, alter func([]string)
|
||||
})
|
||||
}
|
||||
|
||||
func (ul *UserlogService) storeGlobalEvent(typ string, ev interface{}) error {
|
||||
b, err := json.Marshal(ev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
evs, err := ul.GetGlobalEvents()
|
||||
if err != nil && err != store.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
evs[typ] = b
|
||||
|
||||
val, err := json.Marshal(evs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ul.store.Write(&store.Record{
|
||||
Key: "global-events",
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
|
||||
// we need the spaceid to inform other space members
|
||||
// we need an owner to query space members
|
||||
// we need to check the user has the required role to see the event
|
||||
|
||||
@@ -54,6 +54,11 @@ var (
|
||||
Subject: Template("Share expired"),
|
||||
Message: Template("Access to {resource} expired"),
|
||||
}
|
||||
|
||||
PlatformDeprovision = NotificationTemplate{
|
||||
Subject: Template("Platform will be deprovisioned"),
|
||||
Message: Template("Attention! The platform will be deprovisioned at {date}"),
|
||||
}
|
||||
)
|
||||
|
||||
// holds the information to turn the raw template into a parseable go template
|
||||
@@ -62,6 +67,7 @@ var _placeholders = map[string]string{
|
||||
"{space}": "{{ .spacename }}",
|
||||
"{resource}": "{{ .resourcename }}",
|
||||
"{virus}": "{{ .virusdescription }}",
|
||||
"{date}": "{{ .date }}",
|
||||
}
|
||||
|
||||
// NotificationTemplate is the data structure for the notifications
|
||||
|
||||
Reference in New Issue
Block a user