mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-02 10:21:07 -06:00
Merge pull request #3467 from owncloud/user-group-audit
implement user and group audit events
This commit is contained in:
@@ -85,6 +85,20 @@ func StartAuditLogger(ctx context.Context, ch <-chan interface{}, log log.Logger
|
||||
auditEvent = types.SpaceEnabled(ev)
|
||||
case events.SpaceDeleted:
|
||||
auditEvent = types.SpaceDeleted(ev)
|
||||
case events.UserCreated:
|
||||
auditEvent = types.UserCreated(ev)
|
||||
case events.UserDeleted:
|
||||
auditEvent = types.UserDeleted(ev)
|
||||
case events.UserFeatureChanged:
|
||||
auditEvent = types.UserFeatureChanged(ev)
|
||||
case events.GroupCreated:
|
||||
auditEvent = types.GroupCreated(ev)
|
||||
case events.GroupDeleted:
|
||||
auditEvent = types.GroupDeleted(ev)
|
||||
case events.GroupMemberAdded:
|
||||
auditEvent = types.GroupMemberAdded(ev)
|
||||
case events.GroupMemberRemoved:
|
||||
auditEvent = types.GroupMemberRemoved(ev)
|
||||
default:
|
||||
log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev))
|
||||
continue
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package types
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
)
|
||||
|
||||
// short identifiers for audit actions
|
||||
const (
|
||||
@@ -30,6 +35,17 @@ const (
|
||||
ActionSpaceDisabled = "space_disabled"
|
||||
ActionSpaceEnabled = "space_enabled"
|
||||
ActionSpaceDeleted = "space_deleted"
|
||||
|
||||
// Users
|
||||
ActionUserCreated = "user_created"
|
||||
ActionUserDeleted = "user_deleted"
|
||||
ActionUserFeatureChanged = "user_feature_changed"
|
||||
|
||||
// Groups
|
||||
ActionGroupCreated = "group_created"
|
||||
ActionGroupDeleted = "group_deleted"
|
||||
ActionGroupMemberAdded = "group_member_added"
|
||||
ActionGroupMemberRemoved = "group_member_removed"
|
||||
)
|
||||
|
||||
// MessageShareCreated returns the human readable string that describes the action
|
||||
@@ -136,3 +152,49 @@ func MessageSpaceEnabled(spaceID string) string {
|
||||
func MessageSpaceDeleted(spaceID string) string {
|
||||
return fmt.Sprintf("Space '%s' was deleted", spaceID)
|
||||
}
|
||||
|
||||
// MessageUserCreated returns the human readable string that describes the action
|
||||
func MessageUserCreated(userID string) string {
|
||||
return fmt.Sprintf("User '%s' was created", userID)
|
||||
}
|
||||
|
||||
// MessageUserDeleted returns the human readable string that describes the action
|
||||
func MessageUserDeleted(userID string) string {
|
||||
return fmt.Sprintf("User '%s' was deleted", userID)
|
||||
}
|
||||
|
||||
// MessageUserFeatureChanged returns the human readable string that describes the action
|
||||
func MessageUserFeatureChanged(userID string, features []events.UserFeature) string {
|
||||
// Result is: "User %username%'s feature changed: %featurename%=%featurevalue% %featurename%=%featurevalue%"
|
||||
var sb strings.Builder
|
||||
sb.WriteString("User ")
|
||||
sb.WriteString(userID)
|
||||
sb.WriteString("'s feature changed: ")
|
||||
for _, f := range features {
|
||||
sb.WriteString(f.Name)
|
||||
sb.WriteRune('=')
|
||||
sb.WriteString(f.Value)
|
||||
sb.WriteRune(' ')
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// MessageGroupCreated returns the human readable string that describes the action
|
||||
func MessageGroupCreated(groupID string) string {
|
||||
return fmt.Sprintf("Group '%s' was created", groupID)
|
||||
}
|
||||
|
||||
// MessageGroupDeleted returns the human readable string that describes the action
|
||||
func MessageGroupDeleted(groupID string) string {
|
||||
return fmt.Sprintf("Group '%s' was deleted", groupID)
|
||||
}
|
||||
|
||||
// MessageGroupMemberAdded returns the human readable string that describes the action
|
||||
func MessageGroupMemberAdded(userID, groupID string) string {
|
||||
return fmt.Sprintf("User '%s' was added to group '%s'", userID, groupID)
|
||||
}
|
||||
|
||||
// MessageGroupMemberRemoved returns the human readable string that describes the action
|
||||
func MessageGroupMemberRemoved(userID, groupID string) string {
|
||||
return fmt.Sprintf("User '%s' was removed from group '%s'", userID, groupID)
|
||||
}
|
||||
|
||||
@@ -369,6 +369,75 @@ func SpaceDeleted(ev events.SpaceDeleted) AuditEventSpaceDeleted {
|
||||
}
|
||||
}
|
||||
|
||||
// UserCreated converts a UserCreated event to an AuditEventUserCreated
|
||||
func UserCreated(ev events.UserCreated) AuditEventUserCreated {
|
||||
base := BasicAuditEvent("", "", MessageUserCreated(ev.UserID), ActionUserCreated)
|
||||
return AuditEventUserCreated{
|
||||
AuditEvent: base,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// UserDeleted converts a UserDeleted event to an AuditEventUserDeleted
|
||||
func UserDeleted(ev events.UserDeleted) AuditEventUserDeleted {
|
||||
base := BasicAuditEvent("", "", MessageUserDeleted(ev.UserID), ActionUserDeleted)
|
||||
return AuditEventUserDeleted{
|
||||
AuditEvent: base,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// UserFeatureChanged converts a UserFeatureChanged event to an AuditEventUserFeatureChanged
|
||||
func UserFeatureChanged(ev events.UserFeatureChanged) AuditEventUserFeatureChanged {
|
||||
msg := MessageUserFeatureChanged(ev.UserID, ev.Features)
|
||||
base := BasicAuditEvent("", "", msg, ActionUserFeatureChanged)
|
||||
return AuditEventUserFeatureChanged{
|
||||
AuditEvent: base,
|
||||
UserID: ev.UserID,
|
||||
Features: ev.Features,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupCreated converts a GroupCreated event to an AuditEventGroupCreated
|
||||
func GroupCreated(ev events.GroupCreated) AuditEventGroupCreated {
|
||||
base := BasicAuditEvent("", "", MessageGroupCreated(ev.GroupID), ActionGroupCreated)
|
||||
return AuditEventGroupCreated{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupDeleted converts a GroupDeleted event to an AuditEventGroupDeleted
|
||||
func GroupDeleted(ev events.GroupDeleted) AuditEventGroupDeleted {
|
||||
base := BasicAuditEvent("", "", MessageGroupDeleted(ev.GroupID), ActionGroupDeleted)
|
||||
return AuditEventGroupDeleted{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupMemberAdded converts a GroupMemberAdded event to an AuditEventGroupMemberAdded
|
||||
func GroupMemberAdded(ev events.GroupMemberAdded) AuditEventGroupMemberAdded {
|
||||
msg := MessageGroupMemberAdded(ev.GroupID, ev.UserID)
|
||||
base := BasicAuditEvent("", "", msg, ActionGroupMemberAdded)
|
||||
return AuditEventGroupMemberAdded{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupMemberRemoved converts a GroupMemberRemoved event to an AuditEventGroupMemberRemove
|
||||
func GroupMemberRemoved(ev events.GroupMemberRemoved) AuditEventGroupMemberRemoved {
|
||||
msg := MessageGroupMemberRemoved(ev.GroupID, ev.UserID)
|
||||
base := BasicAuditEvent("", "", msg, ActionGroupMemberRemoved)
|
||||
return AuditEventGroupMemberRemoved{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) {
|
||||
switch {
|
||||
case uid != nil && uid.OpaqueId != "":
|
||||
|
||||
@@ -28,5 +28,12 @@ func RegisteredEvents() []events.Unmarshaller {
|
||||
events.SpaceEnabled{},
|
||||
events.SpaceDisabled{},
|
||||
events.SpaceDeleted{},
|
||||
events.UserCreated{},
|
||||
events.UserDeleted{},
|
||||
events.UserFeatureChanged{},
|
||||
events.GroupCreated{},
|
||||
events.GroupDeleted{},
|
||||
events.GroupMemberAdded{},
|
||||
events.GroupMemberRemoved{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package types
|
||||
|
||||
import "github.com/cs3org/reva/v2/pkg/events"
|
||||
|
||||
// AuditEvent is the basic audit event
|
||||
type AuditEvent struct {
|
||||
RemoteAddr string // the remote client IP
|
||||
@@ -197,3 +199,48 @@ type AuditEventSpaceEnabled struct {
|
||||
type AuditEventSpaceDeleted struct {
|
||||
AuditEventSpaces
|
||||
}
|
||||
|
||||
// AuditEventUserCreated is the event logged when a user is created
|
||||
type AuditEventUserCreated struct {
|
||||
AuditEvent
|
||||
UserID string
|
||||
}
|
||||
|
||||
// AuditEventUserDeleted is the event logged when a user is deleted
|
||||
type AuditEventUserDeleted struct {
|
||||
AuditEvent
|
||||
UserID string
|
||||
}
|
||||
|
||||
// AuditEventUserFeatureChanged is the event logged when a user feature is changed
|
||||
type AuditEventUserFeatureChanged struct {
|
||||
AuditEvent
|
||||
UserID string
|
||||
Features []events.UserFeature
|
||||
}
|
||||
|
||||
// AuditEventGroupCreated is the event logged when a group is created
|
||||
type AuditEventGroupCreated struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
}
|
||||
|
||||
// AuditEventGroupDeleted is the event logged when a group is deleted
|
||||
type AuditEventGroupDeleted struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
}
|
||||
|
||||
// AuditEventGroupMemberAdded is the event logged when a group member is added
|
||||
type AuditEventGroupMemberAdded struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
UserID string
|
||||
}
|
||||
|
||||
// AuditEventGroupMemberRemoved is the event logged when a group member is removed
|
||||
type AuditEventGroupMemberRemoved struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
UserID string
|
||||
}
|
||||
|
||||
12
changelog/unreleased/user-group-audit.md
Normal file
12
changelog/unreleased/user-group-audit.md
Normal file
@@ -0,0 +1,12 @@
|
||||
Enhancement: Implement audit events for user and groups
|
||||
|
||||
Added audit events for users and groups. This will log:
|
||||
* User creation
|
||||
* User deletion
|
||||
* User property change (currently only email)
|
||||
* Group creation
|
||||
* Group deletion
|
||||
* Group member add
|
||||
* Group member remove
|
||||
|
||||
https://github.com/owncloud/ocis/pull/3467
|
||||
@@ -27,6 +27,8 @@ include ../.make/generate.mk
|
||||
ci-go-generate: $(MOCKERY) # CI runs ci-node-generate automatically before this target
|
||||
$(MOCKERY) --dir pkg/service/v0 --case underscore --name GatewayClient
|
||||
$(MOCKERY) --dir pkg/service/v0 --case underscore --name HTTPClient
|
||||
$(MOCKERY) --dir pkg/service/v0 --case underscore --name Publisher
|
||||
|
||||
|
||||
.PHONY: ci-node-generate
|
||||
ci-node-generate:
|
||||
|
||||
34
graph/mocks/publisher.go
Normal file
34
graph/mocks/publisher.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Code generated by mockery v2.9.4. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
events "go-micro.dev/v4/events"
|
||||
)
|
||||
|
||||
// Publisher is an autogenerated mock type for the Publisher type
|
||||
type Publisher struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Publish provides a mock function with given fields: _a0, _a1, _a2
|
||||
func (_m *Publisher) Publish(_a0 string, _a1 interface{}, _a2 ...events.PublishOption) error {
|
||||
_va := make([]interface{}, len(_a2))
|
||||
for _i := range _a2 {
|
||||
_va[_i] = _a2[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, _a0, _a1)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, interface{}, ...events.PublishOption) error); ok {
|
||||
r0 = rf(_a0, _a1, _a2...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
@@ -23,6 +23,7 @@ type Config struct {
|
||||
|
||||
Spaces Spaces `yaml:"spaces"`
|
||||
Identity Identity `yaml:"identity"`
|
||||
Events Events `yaml:"events"`
|
||||
|
||||
Context context.Context `yaml:"-"`
|
||||
}
|
||||
@@ -62,3 +63,9 @@ type Identity struct {
|
||||
Backend string `yaml:"backend" env:"GRAPH_IDENTITY_BACKEND"`
|
||||
LDAP LDAP `yaml:"ldap"`
|
||||
}
|
||||
|
||||
// Events combines the configuration options for the event bus.
|
||||
type Events struct {
|
||||
Endpoint string `yaml:"events_endpoint" env:"GRAPH_EVENTS_ENDPOINT" desc:"the address of the streaming service"`
|
||||
Cluster string `yaml:"events_cluster" env:"GRAPH_EVENTS_CLUSTER" desc:"the clusterID of the streaming service. Mandatory when using nats"`
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ func DefaultConfig() *config.Config {
|
||||
GroupIDAttribute: "owncloudUUID",
|
||||
},
|
||||
},
|
||||
Events: config.Events{
|
||||
Endpoint: "127.0.0.1:9233",
|
||||
Cluster: "ocis-cluster",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/asim/go-micro/plugins/events/natsjs/v4"
|
||||
"github.com/cs3org/reva/v2/pkg/events/server"
|
||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||
graphMiddleware "github.com/owncloud/ocis/graph/pkg/middleware"
|
||||
svc "github.com/owncloud/ocis/graph/pkg/service/v0"
|
||||
@@ -8,6 +10,7 @@ import (
|
||||
"github.com/owncloud/ocis/ocis-pkg/middleware"
|
||||
"github.com/owncloud/ocis/ocis-pkg/service/http"
|
||||
"github.com/owncloud/ocis/ocis-pkg/version"
|
||||
"github.com/pkg/errors"
|
||||
"go-micro.dev/v4"
|
||||
)
|
||||
|
||||
@@ -25,6 +28,17 @@ func Server(opts ...Option) (http.Service, error) {
|
||||
http.Flags(options.Flags...),
|
||||
)
|
||||
|
||||
publisher, err := server.NewNatsStream(
|
||||
natsjs.Address(options.Config.Events.Endpoint),
|
||||
natsjs.ClusterID(options.Config.Events.Cluster),
|
||||
)
|
||||
if err != nil {
|
||||
options.Logger.Error().
|
||||
Err(err).
|
||||
Msg("Error initializing events publisher")
|
||||
return http.Service{}, errors.Wrap(err, "could not initialize events publisher")
|
||||
}
|
||||
|
||||
handle := svc.NewService(
|
||||
svc.Logger(options.Logger),
|
||||
svc.Config(options.Config),
|
||||
@@ -42,6 +56,7 @@ func Server(opts ...Option) (http.Service, error) {
|
||||
account.JWTSecret(options.Config.TokenManager.JWTSecret),
|
||||
),
|
||||
),
|
||||
svc.EventsPublisher(publisher),
|
||||
)
|
||||
|
||||
{
|
||||
|
||||
@@ -7,11 +7,13 @@ import (
|
||||
"github.com/ReneKroon/ttlcache/v2"
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/owncloud/ocis/graph/pkg/config"
|
||||
"github.com/owncloud/ocis/graph/pkg/identity"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
settingssvc "github.com/owncloud/ocis/protogen/gen/ocis/services/settings/v0"
|
||||
mevents "go-micro.dev/v4/events"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -51,6 +53,11 @@ type GatewayClient interface {
|
||||
GetQuota(ctx context.Context, in *gateway.GetQuotaRequest, opts ...grpc.CallOption) (*provider.GetQuotaResponse, error)
|
||||
}
|
||||
|
||||
// Publisher is the interface for events publisher
|
||||
type Publisher interface {
|
||||
Publish(string, interface{}, ...mevents.PublishOption) error
|
||||
}
|
||||
|
||||
// HTTPClient is the subset of the http.Client that is being used to interact with the download gateway
|
||||
type HTTPClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
@@ -69,6 +76,7 @@ type Graph struct {
|
||||
httpClient HTTPClient
|
||||
roleService settingssvc.RoleService
|
||||
spacePropertiesCache *ttlcache.Cache
|
||||
eventsPublisher events.Publisher
|
||||
}
|
||||
|
||||
// ServeHTTP implements the Service interface.
|
||||
@@ -86,6 +94,14 @@ func (g Graph) GetHTTPClient() HTTPClient {
|
||||
return g.httpClient
|
||||
}
|
||||
|
||||
func (g Graph) publishEvent(ev interface{}) {
|
||||
if err := events.Publish(g.eventsPublisher, ev); err != nil {
|
||||
g.logger.Error().
|
||||
Err(err).
|
||||
Msg("could not publish user created event")
|
||||
}
|
||||
}
|
||||
|
||||
type listResponse struct {
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
@@ -25,20 +25,23 @@ import (
|
||||
|
||||
var _ = Describe("Graph", func() {
|
||||
var (
|
||||
svc service.Service
|
||||
gatewayClient *mocks.GatewayClient
|
||||
httpClient *mocks.HTTPClient
|
||||
ctx context.Context
|
||||
svc service.Service
|
||||
gatewayClient *mocks.GatewayClient
|
||||
httpClient *mocks.HTTPClient
|
||||
eventsPublisher mocks.Publisher
|
||||
ctx context.Context
|
||||
)
|
||||
|
||||
JustBeforeEach(func() {
|
||||
ctx = context.Background()
|
||||
gatewayClient = &mocks.GatewayClient{}
|
||||
httpClient = &mocks.HTTPClient{}
|
||||
eventsPublisher = mocks.Publisher{}
|
||||
svc = service.NewService(
|
||||
service.Config(defaults.DefaultConfig()),
|
||||
service.WithGatewayClient(gatewayClient),
|
||||
service.WithHTTPClient(httpClient),
|
||||
service.EventsPublisher(&eventsPublisher),
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/graph/pkg/service/v0/errorcode"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
@@ -81,6 +82,9 @@ func (g Graph) PostGroup(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if grp != nil && grp.Id != nil {
|
||||
g.publishEvent(events.GroupCreated{GroupID: *grp.Id})
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, grp)
|
||||
}
|
||||
@@ -197,6 +201,8 @@ func (g Graph) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
g.publishEvent(events.GroupDeleted{GroupID: groupID})
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
@@ -279,6 +285,8 @@ func (g Graph) PostGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
g.publishEvent(events.GroupMemberAdded{GroupID: groupID, UserID: id})
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
@@ -322,6 +330,7 @@ func (g Graph) DeleteGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
return
|
||||
}
|
||||
g.publishEvent(events.GroupMemberRemoved{GroupID: groupID, UserID: memberID})
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package svc
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/owncloud/ocis/graph/pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/ocis-pkg/roles"
|
||||
@@ -14,13 +15,14 @@ type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
GatewayClient GatewayClient
|
||||
HTTPClient HTTPClient
|
||||
RoleService settingssvc.RoleService
|
||||
RoleManager *roles.Manager
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
GatewayClient GatewayClient
|
||||
HTTPClient HTTPClient
|
||||
RoleService settingssvc.RoleService
|
||||
RoleManager *roles.Manager
|
||||
EventsPublisher events.Publisher
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
@@ -82,3 +84,10 @@ func RoleManager(val *roles.Manager) Option {
|
||||
o.RoleManager = val
|
||||
}
|
||||
}
|
||||
|
||||
// EventsPublisher provides a function to set the EventsPublisher option.
|
||||
func EventsPublisher(val events.Publisher) Option {
|
||||
return func(o *Options) {
|
||||
o.EventsPublisher = val
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ func NewService(opts ...Option) Service {
|
||||
logger: &options.Logger,
|
||||
identityBackend: backend,
|
||||
spacePropertiesCache: ttlcache.NewCache(),
|
||||
eventsPublisher: options.EventsPublisher,
|
||||
}
|
||||
if options.GatewayClient == nil {
|
||||
var err error
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
@@ -133,6 +134,9 @@ func (g Graph) PostUser(w http.ResponseWriter, r *http.Request) {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, fmt.Sprintf("could not assign role to account %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
g.publishEvent(events.UserCreated{UserID: *u.Id})
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, u)
|
||||
}
|
||||
@@ -187,6 +191,9 @@ func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
g.publishEvent(events.UserDeleted{UserID: userID})
|
||||
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
@@ -211,12 +218,18 @@ func (g Graph) PatchUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var features []events.UserFeature
|
||||
if mail, ok := changes.GetMailOk(); ok {
|
||||
if !isValidEmail(*mail) {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest,
|
||||
fmt.Sprintf("'%s' is not a valid email address", *mail))
|
||||
return
|
||||
}
|
||||
features = append(features, events.UserFeature{Name: "email", Value: *mail})
|
||||
}
|
||||
|
||||
if name, ok := changes.GetDisplayNameOk(); ok {
|
||||
features = append(features, events.UserFeature{Name: "displayname", Value: *name})
|
||||
}
|
||||
|
||||
u, err := g.identityBackend.UpdateUser(r.Context(), nameOrID, *changes)
|
||||
@@ -229,6 +242,12 @@ func (g Graph) PatchUser(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
g.publishEvent(
|
||||
events.UserFeatureChanged{
|
||||
UserID: nameOrID,
|
||||
Features: features,
|
||||
},
|
||||
)
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, u)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user