Skip to content

Commit

Permalink
sharpen userlog service
Browse files Browse the repository at this point in the history
Signed-off-by: jkoberg <jkoberg@owncloud.com>
  • Loading branch information
kobergj committed Feb 10, 2023
1 parent 7d799ef commit 6c45d28
Show file tree
Hide file tree
Showing 11 changed files with 380 additions and 48 deletions.
8 changes: 4 additions & 4 deletions services/proxy/pkg/config/defaults/defaultconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ func DefaultPolicies() []config.Policy {
Endpoint: "/archiver",
Service: "com.owncloud.web.frontend",
},
{
Endpoint: "/ocs/v2.php/apps/notifications/api/v1/notifications",
Service: "com.owncloud.userlog.userlog",
},
{
Type: config.RegexRoute,
Endpoint: "/ocs/v[12].php/cloud/user/signing-key", // only `user/signing-key` is left in ocis-ocs
Expand Down Expand Up @@ -193,10 +197,6 @@ func DefaultPolicies() []config.Policy {
Endpoint: "/api/v0/settings",
Service: "com.owncloud.web.settings",
},
{
Endpoint: "/api/v0/activities",
Service: "com.owncloud.userlog.userlog",
},
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion services/storage-users/pkg/event/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (s Service) Run() error {
for e := range ch {
var errs []error

switch ev := e.(type) {
switch ev := e.Event.(type) {
case PurgeTrashBin:
executionTime := ev.ExecutionTime
if executionTime.IsZero() {
Expand Down
2 changes: 1 addition & 1 deletion services/userlog/pkg/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type SutureService struct {

// NewSutureService creates a new userlog.SutureService
func NewSutureService(cfg *ociscfg.Config) suture.Service {
cfg.Notifications.Commons = cfg.Commons
cfg.Userlog.Commons = cfg.Commons
return SutureService{
cfg: cfg.Userlog,
}
Expand Down
25 changes: 24 additions & 1 deletion services/userlog/pkg/command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/events/stream"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/oklog/run"
"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
Expand All @@ -22,6 +23,14 @@ import (
// all events we care about
var _registeredEvents = []events.Unmarshaller{
events.UploadReady{},
events.ContainerCreated{},
events.FileTouched{},
events.FileDownloaded{},
events.FileVersionRestored{},
events.ItemMoved{},
events.ItemTrashed{},
events.ItemPurged{},
events.ItemRestored{},
}

// Server is the entrypoint for the server command.
Expand All @@ -48,7 +57,9 @@ func Server(cfg *config.Config) *cli.Command {
}
return context.WithCancel(cfg.Context)
}()

mtrcs := metrics.New()
mtrcs.BuildInfo.WithLabelValues(version.GetString()).Set(1)

defer cancel()

Expand All @@ -65,7 +76,18 @@ func Server(cfg *config.Config) *cli.Command {
return fmt.Errorf("unknown store '%s' configured", cfg.Store.Type)
}

mtrcs.BuildInfo.WithLabelValues(version.GetString()).Set(1)
tm, err := pool.StringToTLSMode(cfg.GRPCClientTLS.Mode)
if err != nil {
return err
}
gwclient, err := pool.GetGatewayServiceClient(
cfg.RevaGateway,
pool.WithTLSCACert(cfg.GRPCClientTLS.CACert),
pool.WithTLSMode(tm),
)
if err != nil {
return fmt.Errorf("could not get reva client: %s", err)
}

{
server, err := http.Server(
Expand All @@ -75,6 +97,7 @@ func Server(cfg *config.Config) *cli.Command {
http.Metrics(mtrcs),
http.Store(st),
http.Consumer(consumer),
http.Gateway(gwclient),
http.RegisteredEvents(_registeredEvents),
)

Expand Down
6 changes: 4 additions & 2 deletions services/userlog/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ type Config struct {
HTTP HTTP `yaml:"http"`
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`

Events Events `yaml:"events"`
Store Store `yaml:"store"`
MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;USERLOG_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
RevaGateway string `yaml:"reva_gateway" env:"REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata"`
Events Events `yaml:"events"`
Store Store `yaml:"store"`

Context context.Context `yaml:"-"`
}
Expand Down
5 changes: 5 additions & 0 deletions services/userlog/pkg/config/defaults/defaultconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func DefaultConfig() *config.Config {
Store: config.Store{
Type: "inmemory",
},
RevaGateway: shared.DefaultRevaConfig().Address,
HTTP: config.HTTP{
Addr: "127.0.0.1:0",
Root: "/",
Expand Down Expand Up @@ -57,6 +58,10 @@ func EnsureDefaults(cfg *config.Config) {
cfg.Log = &config.Log{}
}

if cfg.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
cfg.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
}

if cfg.GRPCClientTLS == nil {
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
if cfg.Commons != nil && cfg.Commons.GRPCClientTLS != nil {
Expand Down
5 changes: 5 additions & 0 deletions services/userlog/pkg/config/parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"

ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
"github.com/owncloud/ocis/v2/services/userlog/pkg/config"
"github.com/owncloud/ocis/v2/services/userlog/pkg/config/defaults"

Expand Down Expand Up @@ -34,5 +35,9 @@ func ParseConfig(cfg *config.Config) error {

// Validate validates the config
func Validate(cfg *config.Config) error {
if cfg.MachineAuthAPIKey == "" {
return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
}

return nil
}
9 changes: 9 additions & 0 deletions services/userlog/pkg/server/http/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package http
import (
"context"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/cs3org/reva/v2/pkg/events"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/userlog/pkg/config"
Expand All @@ -24,6 +25,7 @@ type Options struct {
Namespace string
Store store.Store
Consumer events.Consumer
GatewayClient gateway.GatewayAPIClient
RegisteredEvents []events.Unmarshaller
}

Expand Down Expand Up @@ -94,6 +96,13 @@ func Consumer(consumer events.Consumer) Option {
}
}

// Gateway provides a function to configure the gateway client
func Gateway(gw gateway.GatewayAPIClient) Option {
return func(o *Options) {
o.GatewayClient = gw
}
}

// RegisteredEvents provides a function to register events
func RegisteredEvents(evs []events.Unmarshaller) Option {
return func(o *Options) {
Expand Down
2 changes: 1 addition & 1 deletion services/userlog/pkg/server/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func Server(opts ...Option) (http.Service, error) {
//),
//}

handle, err := svc.NewUserlogService(options.Config, options.Consumer, options.Store, options.RegisteredEvents)
handle, err := svc.NewUserlogService(options.Config, options.Consumer, options.Store, options.GatewayClient, options.RegisteredEvents, options.Logger)
if err != nil {
return http.Service{}, err
}
Expand Down
133 changes: 133 additions & 0 deletions services/userlog/pkg/service/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package service

import (
"encoding/json"
"fmt"
"net/http"
"time"

revactx "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/go-chi/chi/v5"
"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0"
)

// BuildMux builds the and configures the muxer
func (ul *UserlogService) BuildMux() {
m := chi.NewMux()
m.Use(middleware.ExtractAccountUUID())

m.Route("/", func(r chi.Router) {
r.Get("/*", ul.HandleGetEvents)
r.Delete("/*", ul.HandleDeleteEvents)
})

ul.m = m
}

// ServeHTTP fulfills Handler interface
func (ul *UserlogService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ul.m.ServeHTTP(w, r)
}

// HandleGetEvents is the GET handler for events
func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request) {
u, ok := revactx.ContextGetUser(r.Context())
if !ok {
w.WriteHeader(http.StatusUnauthorized)
return
}
userID := u.GetId().GetOpaqueId()

evs, err := ul.GetEvents(r.Context(), userID)
if err != nil {
return
}

resp := GetEventResponseOC10{}
for _, e := range evs {
resp.OCS.Data = append(resp.OCS.Data, ul.convertEvent(e))
}

resp.OCS.Meta.StatusCode = http.StatusOK
b, _ := json.Marshal(resp)
w.Write(b)
}

// HandleDeleteEvents is the DELETE handler for events
func (ul *UserlogService) HandleDeleteEvents(w http.ResponseWriter, r *http.Request) {
u, ok := revactx.ContextGetUser(r.Context())
if !ok {
w.WriteHeader(http.StatusUnauthorized)
return
}

var ids []string
if err := json.NewDecoder(r.Body).Decode(&ids); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}

if err := ul.DeleteEvents(u.GetId().GetOpaqueId(), ids); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
}

func (ul *UserlogService) convertEvent(event *ehmsg.Event) OC10Notification {
etype, ok := ul.registeredEvents[event.Type]
if !ok {
// this should not happen
return OC10Notification{}
}

einterface, err := etype.Unmarshal(event.Event)
if err != nil {
// this shouldn't happen either
return OC10Notification{}
}

noti := OC10Notification{
EventID: event.Id,
Service: "userlog",
Timestamp: time.Now().Format(time.RFC3339Nano),
}

switch ev := einterface.(type) {
case events.UploadReady:
ctx, _, _ := utils.Impersonate(ev.SpaceOwner, ul.gwClient, ul.cfg.MachineAuthAPIKey)
space, _ := ul.getSpace(ctx, ev.FileRef.GetResourceId().GetSpaceId())
noti.UserID = ev.ExecutingUser.GetId().GetOpaqueId()
noti.Subject = "File uploaded"
noti.Message = fmt.Sprintf("File %s was uploaded to space %s by user %s", ev.Filename, space.GetName(), ev.ExecutingUser.GetUsername())
}

return noti
}

// OC10Notification is the oc10 style representation of an event
// some fields are left out for simplicity
type OC10Notification struct {
EventID string `json:"notification_id"`
Service string `json:"app"`
Timestamp string `json:"datetime"`
UserID string `json:"user"`
Subject string `json:"subject"`
Message string `json:"message"`
}

// GetEventResponseOC10 is the response from GET events endpoint in oc10 style
type GetEventResponseOC10 struct {
OCS struct {
Meta struct {
Message string `json:"message"`
Status string `json:"status"`
StatusCode int `json:"statuscode"`
} `json:"meta"`
Data []OC10Notification `json:"data"`
} `json:"ocs"`
}
Loading

0 comments on commit 6c45d28

Please sign in to comment.