diff --git a/changelog/unreleased/share-add-info-configurable.md b/changelog/unreleased/share-add-info-configurable.md new file mode 100644 index 0000000000..b5576aa85f --- /dev/null +++ b/changelog/unreleased/share-add-info-configurable.md @@ -0,0 +1,7 @@ +Enhancement: Make the additional info attribute for shares configurable + +AdditionalInfoAttribute can be configured via the `additional_info_attribute` +key in the form of a Go template string. If not explicitly set, the default +value is `{{.Mail}}` + +https://github.com/cs3org/reva/pull/1588 diff --git a/internal/http/services/owncloud/ocs/config/config.go b/internal/http/services/owncloud/ocs/config/config.go index 3cb402e269..e62ba20c60 100644 --- a/internal/http/services/owncloud/ocs/config/config.go +++ b/internal/http/services/owncloud/ocs/config/config.go @@ -25,14 +25,15 @@ import ( // Config holds the config options that need to be passed down to all ocs handlers type Config struct { - Prefix string `mapstructure:"prefix"` - Config data.ConfigData `mapstructure:"config"` - Capabilities data.CapabilitiesData `mapstructure:"capabilities"` - GatewaySvc string `mapstructure:"gatewaysvc"` - DefaultUploadProtocol string `mapstructure:"default_upload_protocol"` - UserAgentChunkingMap map[string]string `mapstructure:"user_agent_chunking_map"` - SharePrefix string `mapstructure:"share_prefix"` - HomeNamespace string `mapstructure:"home_namespace"` + Prefix string `mapstructure:"prefix"` + Config data.ConfigData `mapstructure:"config"` + Capabilities data.CapabilitiesData `mapstructure:"capabilities"` + GatewaySvc string `mapstructure:"gatewaysvc"` + DefaultUploadProtocol string `mapstructure:"default_upload_protocol"` + UserAgentChunkingMap map[string]string `mapstructure:"user_agent_chunking_map"` + SharePrefix string `mapstructure:"share_prefix"` + HomeNamespace string `mapstructure:"home_namespace"` + AdditionalInfoAttribute string `mapstructure:"additional_info_attribute"` } // Init sets sane defaults @@ -53,5 +54,9 @@ func (c *Config) Init() { c.HomeNamespace = "/home" } + if c.AdditionalInfoAttribute == "" { + c.AdditionalInfoAttribute = "{{.Mail}}" + } + c.GatewaySvc = sharedconf.GetGatewaySVC(c.GatewaySvc) } diff --git a/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go b/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go index 5bb51e7b08..ea8ad4a95a 100644 --- a/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go +++ b/internal/http/services/owncloud/ocs/handlers/apps/sharing/sharees/sharees.go @@ -30,16 +30,19 @@ import ( "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/pkg/rhttp/router" + "github.com/cs3org/reva/pkg/storage/utils/templates" ) // Handler implements the ownCloud sharing API type Handler struct { - gatewayAddr string + gatewayAddr string + additionalInfoAttribute string } // Init initializes this and any contained handlers func (h *Handler) Init(c *config.Config) error { h.gatewayAddr = c.GatewaySvc + h.additionalInfoAttribute = c.AdditionalInfoAttribute return nil } @@ -116,7 +119,7 @@ func (h *Handler) userAsMatch(u *userpb.User) *conversions.MatchData { ShareType: int(conversions.ShareTypeUser), // api compatibility with oc10: always use the username ShareWith: u.Username, - ShareWithAdditionalInfo: u.Mail, + ShareWithAdditionalInfo: h.getAdditionalInfoAttribute(u), }, } } @@ -125,10 +128,13 @@ func (h *Handler) groupAsMatch(g *grouppb.Group) *conversions.MatchData { return &conversions.MatchData{ Label: g.DisplayName, Value: &conversions.MatchValueData{ - ShareType: int(conversions.ShareTypeGroup), - // api compatibility with oc10 + ShareType: int(conversions.ShareTypeGroup), ShareWith: g.GroupName, ShareWithAdditionalInfo: g.Mail, }, } } + +func (h *Handler) getAdditionalInfoAttribute(u *userpb.User) string { + return templates.WithUser(u, h.additionalInfoAttribute) +} diff --git a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go index 028c3e9172..c62a3409df 100644 --- a/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go +++ b/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go @@ -19,6 +19,7 @@ package shares import ( + "bytes" "context" "encoding/base64" "encoding/json" @@ -28,6 +29,7 @@ import ( "path" "strconv" "strings" + "text/template" "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" @@ -52,17 +54,18 @@ import ( // Handler implements the shares part of the ownCloud sharing API type Handler struct { - gatewayAddr string - publicURL string - sharePrefix string - homeNamespace string - userIdentifierCache *ttlcache.Cache + gatewayAddr string + publicURL string + sharePrefix string + homeNamespace string + additionalInfoTemplate *template.Template + userIdentifierCache *ttlcache.Cache } // we only cache the minimal set of data instead of the full user metadata type userIdentifiers struct { DisplayName string - UserName string + Username string Mail string } @@ -73,6 +76,8 @@ func (h *Handler) Init(c *config.Config) error { h.sharePrefix = c.SharePrefix h.homeNamespace = c.HomeNamespace + h.additionalInfoTemplate, _ = template.New("additionalInfo").Parse(c.AdditionalInfoAttribute) + h.userIdentifierCache = ttlcache.NewCache() _ = h.userIdentifierCache.SetTTL(60 * time.Second) @@ -899,7 +904,7 @@ func (h *Handler) mustGetIdentifiers(ctx context.Context, c gateway.GatewayAPICl } ui = &userIdentifiers{ DisplayName: res.Group.DisplayName, - UserName: res.Group.GroupName, + Username: res.Group.GroupName, Mail: res.Group.Mail, } } else { @@ -928,7 +933,7 @@ func (h *Handler) mustGetIdentifiers(ctx context.Context, c gateway.GatewayAPICl } ui = &userIdentifiers{ DisplayName: res.User.DisplayName, - UserName: res.User.Username, + Username: res.User.Username, Mail: res.User.Mail, } } @@ -940,34 +945,44 @@ func (h *Handler) mustGetIdentifiers(ctx context.Context, c gateway.GatewayAPICl func (h *Handler) mapUserIds(ctx context.Context, c gateway.GatewayAPIClient, s *conversions.ShareData) { if s.UIDOwner != "" { owner := h.mustGetIdentifiers(ctx, c, s.UIDOwner, false) - s.UIDOwner = owner.UserName + s.UIDOwner = owner.Username if s.DisplaynameOwner == "" { s.DisplaynameOwner = owner.DisplayName } if s.AdditionalInfoFileOwner == "" { - s.AdditionalInfoFileOwner = owner.Mail + s.AdditionalInfoFileOwner = h.getAdditionalInfoAttribute(ctx, owner) } } if s.UIDFileOwner != "" { fileOwner := h.mustGetIdentifiers(ctx, c, s.UIDFileOwner, false) - s.UIDFileOwner = fileOwner.UserName + s.UIDFileOwner = fileOwner.Username if s.DisplaynameFileOwner == "" { s.DisplaynameFileOwner = fileOwner.DisplayName } if s.AdditionalInfoOwner == "" { - s.AdditionalInfoOwner = fileOwner.Mail + s.AdditionalInfoOwner = h.getAdditionalInfoAttribute(ctx, fileOwner) } } if s.ShareWith != "" && s.ShareWith != "***redacted***" { shareWith := h.mustGetIdentifiers(ctx, c, s.ShareWith, s.ShareType == conversions.ShareTypeGroup) - s.ShareWith = shareWith.UserName + s.ShareWith = shareWith.Username if s.ShareWithDisplayname == "" { s.ShareWithDisplayname = shareWith.DisplayName } if s.ShareWithAdditionalInfo == "" { - s.ShareWithAdditionalInfo = shareWith.Mail + s.ShareWithAdditionalInfo = h.getAdditionalInfoAttribute(ctx, shareWith) } } } + +func (h *Handler) getAdditionalInfoAttribute(ctx context.Context, u *userIdentifiers) string { + b := bytes.Buffer{} + if err := h.additionalInfoTemplate.Execute(&b, u); err != nil { + log := appctx.GetLogger(ctx) + log.Warn().Err(err).Msg("failed to parse additional info template") + return "" + } + return b.String() +} diff --git a/pkg/cbox/publicshare/sql/sql.go b/pkg/cbox/publicshare/sql/sql.go index 033b3acb9e..152d3c42b2 100644 --- a/pkg/cbox/publicshare/sql/sql.go +++ b/pkg/cbox/publicshare/sql/sql.go @@ -252,7 +252,7 @@ func (m *manager) UpdatePublicShare(ctx context.Context, u *user.User, req *link func (m *manager) getByToken(ctx context.Context, token string, u *user.User) (*link.PublicShare, string, error) { s := conversions.DBShare{Token: token} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE share_type=? AND token=?" + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND share_type=? AND token=?" if err := m.db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions); err != nil { if err == sql.ErrNoRows { return nil, "", errtypes.NotFound(token) @@ -265,7 +265,7 @@ func (m *manager) getByToken(ctx context.Context, token string, u *user.User) (* func (m *manager) getByID(ctx context.Context, id *link.PublicShareId, u *user.User) (*link.PublicShare, string, error) { uid := conversions.FormatUserID(u.Id) s := conversions.DBShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, stime, permissions FROM oc_share WHERE share_type=? AND id=? AND (uid_owner=? OR uid_initiator=?)" + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND share_type=? AND id=? AND (uid_owner=? OR uid_initiator=?)" if err := m.db.QueryRow(query, publicShareType, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.Token, &s.Expiration, &s.ShareName, &s.STime, &s.Permissions); err != nil { if err == sql.ErrNoRows { return nil, "", errtypes.NotFound(id.OpaqueId) @@ -307,7 +307,7 @@ func (m *manager) GetPublicShare(ctx context.Context, u *user.User, ref *link.Pu func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters []*link.ListPublicSharesRequest_Filter, md *provider.ResourceInfo, sign bool) ([]*link.PublicShare, error) { uid := conversions.FormatUserID(u.Id) - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (uid_owner=? or uid_initiator=?) AND (share_type=?)" + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner=? or uid_initiator=?) AND (share_type=?)" var filterQuery string params := []interface{}{uid, uid, publicShareType} diff --git a/pkg/cbox/share/sql/sql.go b/pkg/cbox/share/sql/sql.go index db86988f21..fac64aab4b 100644 --- a/pkg/cbox/share/sql/sql.go +++ b/pkg/cbox/share/sql/sql.go @@ -318,12 +318,12 @@ func (m *mgr) ListReceivedShares(ctx context.Context) ([]*collaboration.Received user := user.ContextMustGetUser(ctx) uid := conversions.FormatUserID(user.Id) - params := []interface{}{uid, uid} + params := []interface{}{uid, uid, uid, uid} for _, v := range user.Groups { params = append(params, v) } - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, id, stime, permissions, share_type, accepted FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND id not in (SELECT distinct(id) FROM oc_share_acl WHERE rejected_by=?)" + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, id, stime, permissions, share_type, accepted FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner != ? AND uid_initiator != ?) AND id not in (SELECT distinct(id) FROM oc_share_acl WHERE rejected_by=?)" if len(user.Groups) > 0 { query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" } else {