mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 21:58:58 -04:00
Merge pull request #3329 from opencloud-eu/chore/bump-reva
chore: bump reva to latest main
This commit is contained in:
42 files changed
+695
-17509
No files matched your search
@@ -2473,6 +2473,7 @@ def opencloudServer(storage = "decomposed", depends_on = [], deploy_type = "", e
|
||||
"EVENTHISTORY_STORE": "memory",
|
||||
"OC_TRANSLATION_PATH": "%s/tests/config/translations" % dirs["base"],
|
||||
"ACTIVITYLOG_WRITE_BUFFER_DURATION": "0", # Disable write buffer so that test expectations are met in time
|
||||
"OC_LDAP_LOOKUP_CACHE_TTL": "0", # disable ldap lookup cache so that test fixture change are applied right away
|
||||
# search grpc port needed for index cli tests
|
||||
"SEARCH_GRPC_ADDR": "0.0.0.0:9220",
|
||||
# debug addresses required for running services health tests
|
||||
|
||||
@@ -64,7 +64,7 @@ require (
|
||||
github.com/open-policy-agent/opa v1.19.0
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260818063016-69f82a7dde55
|
||||
github.com/opencloud-eu/reva/v2 v2.48.1-0.20260811071808-9bf68d2ccd96
|
||||
github.com/opencloud-eu/reva/v2 v2.48.1-0.20260819125853-c9416ac8b8c0
|
||||
github.com/opensearch-project/opensearch-go/v4 v4.6.0
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -94,7 +94,7 @@ require (
|
||||
github.com/xhit/go-simple-mail/v2 v2.16.0
|
||||
go-micro.dev/v4 v4.11.0
|
||||
go.etcd.io/bbolt v1.5.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0
|
||||
go.opentelemetry.io/contrib/zpages v0.69.0
|
||||
go.opentelemetry.io/otel v1.45.0
|
||||
|
||||
@@ -942,8 +942,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260818063016-69f82a7dde55 h1:dzYZ5iA6i0QoFUap9l6sZ4Ts2HXiXVfsOoiRYToYgm8=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260818063016-69f82a7dde55/go.mod h1:lTM8JeGblNpoMySTW7Lui2+c5TTLI95mwxtdUIHHrhU=
|
||||
github.com/opencloud-eu/reva/v2 v2.48.1-0.20260811071808-9bf68d2ccd96 h1:2yPjf1jhrhEkrHsX8s6gAQVogBmNY6ZTtPNOtLfc1p8=
|
||||
github.com/opencloud-eu/reva/v2 v2.48.1-0.20260811071808-9bf68d2ccd96/go.mod h1:+2IJFVwi3yBEBfw0T+jgY7M9LBSPv7ecPYDiG3/bWYg=
|
||||
github.com/opencloud-eu/reva/v2 v2.48.1-0.20260819125853-c9416ac8b8c0 h1:IYqEof5RbfOrPE0g3FH7paBHNTpicB08G0Fy8lagnRc=
|
||||
github.com/opencloud-eu/reva/v2 v2.48.1-0.20260819125853-c9416ac8b8c0/go.mod h1:OesdFn3szHcO/gifHWjbMkbkH6tgggkXL3KaNCI81ko=
|
||||
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI=
|
||||
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
@@ -1293,8 +1293,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0 h1:oECp5f+hN7nkwjU/8BxQ/q23bGPb8FIrD839owX222E=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0/go.mod h1:DqEFwLumhzMBDQv9PcWbyoDxHI/4lAk6CM4nJBH39sc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 h1:LMuyCAyfalSjDyjdC65nK6N0zoTT63+E/u95X0JovZI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0/go.mod h1:085m8qbm4hgc8rZWGDEa4vmyyo2c3nPxUslYUKUIU04=
|
||||
go.opentelemetry.io/contrib/zpages v0.69.0 h1:YQC1PumJq6lUGQNrLW14ID9a0dXSBcbC/aC6VRSIARU=
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
)
|
||||
@@ -72,6 +73,7 @@ type LDAPProvider struct {
|
||||
LdapDisabledUsersGroupDN string `yaml:"ldap_disabled_users_group_dn" env:"OC_LDAP_DISABLED_USERS_GROUP_DN;AUTH_BASIC_DISABLED_USERS_GROUP_DN" desc:"The distinguished name of the group to which added users will be classified as disabled when 'disable_user_mechanism' is set to 'group'." introductionVersion:"1.0.0"`
|
||||
UserSchema LDAPUserSchema `yaml:"user_schema"`
|
||||
GroupSchema LDAPGroupSchema `yaml:"group_schema"`
|
||||
LookupCacheTTL time.Duration `yaml:"lookup_cache_ttl" env:"OC_LDAP_LOOKUP_CACHE_TTL;AUTH_BASIC_LDAP_LOOKUP_CACHE_TTL" desc:"The time to live for the in-memory cache of LDAP lookups. This cache is used to reduce the number of LDAP queries for user and group lookups." introductionVersion:"%%NEXT%%"`
|
||||
}
|
||||
|
||||
type LDAPUserSchema struct {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
"github.com/opencloud-eu/opencloud/services/auth-basic/pkg/config"
|
||||
@@ -64,6 +66,7 @@ func DefaultConfig() *config.Config {
|
||||
Groupname: "cn",
|
||||
Member: "member",
|
||||
},
|
||||
LookupCacheTTL: 10 * time.Second,
|
||||
},
|
||||
JSON: config.JSONProvider{},
|
||||
OwnCloudSQL: config.OwnCloudSQLProvider{
|
||||
|
||||
@@ -92,5 +92,6 @@ func ldapConfigFromString(cfg config.LDAPProvider) map[string]any {
|
||||
"groupName": cfg.GroupSchema.Groupname,
|
||||
"member": cfg.GroupSchema.Member,
|
||||
},
|
||||
"lookup_cache_ttl": cfg.LookupCacheTTL,
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
)
|
||||
@@ -69,6 +70,7 @@ type LDAPDriver struct {
|
||||
IDP string `yaml:"idp" env:"OC_URL;OC_OIDC_ISSUER;GROUPS_IDP_URL" desc:"The identity provider value to set in the group IDs of the CS3 group objects for groups returned by this group provider." introductionVersion:"1.0.0"`
|
||||
UserSchema LDAPUserSchema `yaml:"user_schema"`
|
||||
GroupSchema LDAPGroupSchema `yaml:"group_schema"`
|
||||
LookupCacheTTL time.Duration `yaml:"lookup_cache_ttl" env:"OC_LDAP_LOOKUP_CACHE_TTL;GROUPS_LDAP_LOOKUP_CACHE_TTL" desc:"The time to live for the in-memory cache of LDAP lookups. This cache is used to reduce the number of LDAP queries for user and group lookups." introductionVersion:"%%NEXT%%"`
|
||||
}
|
||||
|
||||
type LDAPUserSchema struct {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
"github.com/opencloud-eu/opencloud/services/groups/pkg/config"
|
||||
@@ -61,6 +63,7 @@ func DefaultConfig() *config.Config {
|
||||
Groupname: "cn",
|
||||
Member: "member",
|
||||
},
|
||||
LookupCacheTTL: 10 * time.Second,
|
||||
},
|
||||
OwnCloudSQL: config.OwnCloudSQLDriver{
|
||||
DBUsername: "owncloud",
|
||||
|
||||
@@ -87,5 +87,6 @@ func ldapConfigFromString(cfg config.LDAPDriver) map[string]any {
|
||||
"groupName": cfg.GroupSchema.Groupname,
|
||||
"member": cfg.GroupSchema.Member,
|
||||
},
|
||||
"lookup_cache_ttl": cfg.LookupCacheTTL,
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,19 @@ import (
|
||||
|
||||
// Session contains the information of an upload session
|
||||
type Session struct {
|
||||
ID string `json:"id"`
|
||||
Space string `json:"space"`
|
||||
Filename string `json:"filename"`
|
||||
Offset int64 `json:"offset"`
|
||||
Size int64 `json:"size"`
|
||||
Executant userpb.UserId `json:"executant"`
|
||||
SpaceOwner *userpb.UserId `json:"spaceowner,omitempty"`
|
||||
Expires time.Time `json:"expires"`
|
||||
Processing bool `json:"processing"`
|
||||
ScanDate time.Time `json:"virus_scan_date"`
|
||||
ScanResult string `json:"virus_scan_result"`
|
||||
ID string `json:"id"`
|
||||
Space string `json:"space"`
|
||||
Filename string `json:"filename"`
|
||||
Offset int64 `json:"offset"`
|
||||
Size int64 `json:"size"`
|
||||
Executant userpb.UserId `json:"executant"`
|
||||
SpaceOwner *userpb.UserId `json:"spaceowner,omitempty"`
|
||||
Expires time.Time `json:"expires"`
|
||||
Processing bool `json:"processing"`
|
||||
ScanDate time.Time `json:"virus_scan_date"`
|
||||
ScanResult string `json:"virus_scan_result"`
|
||||
Status string `json:"status"`
|
||||
StatusMessage string `json:"status_message"`
|
||||
}
|
||||
|
||||
// Uploads is the entry point for the uploads command
|
||||
@@ -123,7 +125,7 @@ func ListUploadSessions(cfg *config.Config) *cobra.Command {
|
||||
fmt.Println(buildInfo(filter))
|
||||
|
||||
table = tablewriter.NewTable(os.Stdout, tablewriter.WithHeaderAutoFormat(tw.Off))
|
||||
table.Header([]string{"Space", "Upload Id", "Name", "Offset", "Size", "Executant", "Owner", "Expires", "Processing", "Scan Date", "Scan Result"})
|
||||
table.Header([]string{"Space", "Upload Id", "Name", "Status", "Message", "Offset", "Size", "Executant", "Owner", "Expires", "Scan Date", "Scan Result"})
|
||||
}
|
||||
|
||||
for _, u := range uploads {
|
||||
@@ -131,17 +133,18 @@ func ListUploadSessions(cfg *config.Config) *cobra.Command {
|
||||
sr, sd := u.ScanData()
|
||||
|
||||
session := Session{
|
||||
Space: ref.GetResourceId().GetSpaceId(),
|
||||
ID: u.ID(),
|
||||
Filename: u.Filename(),
|
||||
Offset: u.Offset(),
|
||||
Size: u.Size(),
|
||||
Executant: u.Executant(),
|
||||
SpaceOwner: u.SpaceOwner(),
|
||||
Expires: u.Expires(),
|
||||
Processing: u.IsProcessing(),
|
||||
ScanDate: sd,
|
||||
ScanResult: sr,
|
||||
Space: ref.GetResourceId().GetSpaceId(),
|
||||
ID: u.ID(),
|
||||
Filename: u.Filename(),
|
||||
Offset: u.Offset(),
|
||||
Size: u.Size(),
|
||||
Executant: u.Executant(),
|
||||
SpaceOwner: u.SpaceOwner(),
|
||||
Expires: u.Expires(),
|
||||
ScanDate: sd,
|
||||
ScanResult: sr,
|
||||
Status: string(u.Status()),
|
||||
StatusMessage: u.StatusMessage(),
|
||||
}
|
||||
|
||||
if renderJson {
|
||||
@@ -151,12 +154,13 @@ func ListUploadSessions(cfg *config.Config) *cobra.Command {
|
||||
session.Space,
|
||||
session.ID,
|
||||
session.Filename,
|
||||
session.Status,
|
||||
session.StatusMessage,
|
||||
strconv.FormatInt(session.Offset, 10),
|
||||
strconv.FormatInt(session.Size, 10),
|
||||
session.Executant.OpaqueId,
|
||||
session.SpaceOwner.GetOpaqueId(),
|
||||
session.Expires.Format(time.RFC3339),
|
||||
strconv.FormatBool(session.Processing),
|
||||
session.ScanDate.Format(time.RFC3339),
|
||||
session.ScanResult,
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
)
|
||||
@@ -55,31 +56,32 @@ type JSONDriver struct {
|
||||
File string `yaml:"file"`
|
||||
}
|
||||
type LDAPDriver struct {
|
||||
URI string `yaml:"uri" env:"OC_LDAP_URI;USERS_LDAP_URI" desc:"URI of the LDAP Server to connect to. Supported URI schemes are 'ldaps://' and 'ldap://'" introductionVersion:"1.0.0"`
|
||||
CACert string `yaml:"ca_cert" env:"OC_LDAP_CACERT;USERS_LDAP_CACERT" desc:"Path/File name for the root CA certificate (in PEM format) used to validate TLS server certificates of the LDAP service. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
|
||||
Insecure bool `yaml:"insecure" env:"OC_LDAP_INSECURE;USERS_LDAP_INSECURE" desc:"Disable TLS certificate validation for the LDAP connections. Do not set this in production environments." introductionVersion:"1.0.0"`
|
||||
BindDN string `yaml:"bind_dn" env:"OC_LDAP_BIND_DN;USERS_LDAP_BIND_DN" desc:"LDAP DN to use for simple bind authentication with the target LDAP server." introductionVersion:"1.0.0"`
|
||||
BindPassword string `yaml:"bind_password" env:"OC_LDAP_BIND_PASSWORD;USERS_LDAP_BIND_PASSWORD" desc:"Password to use for authenticating the 'bind_dn'." introductionVersion:"1.0.0"`
|
||||
UserBaseDN string `yaml:"user_base_dn" env:"OC_LDAP_USER_BASE_DN;USERS_LDAP_USER_BASE_DN" desc:"Search base DN for looking up LDAP users." introductionVersion:"1.0.0"`
|
||||
GroupBaseDN string `yaml:"group_base_dn" env:"OC_LDAP_GROUP_BASE_DN;USERS_LDAP_GROUP_BASE_DN" desc:"Search base DN for looking up LDAP groups." introductionVersion:"1.0.0"`
|
||||
URI string `yaml:"uri" env:"OC_LDAP_URI;USERS_LDAP_URI" desc:"URI of the LDAP Server to connect to. Supported URI schemes are 'ldaps://' and 'ldap://'" introductionVersion:"1.0.0"`
|
||||
CACert string `yaml:"ca_cert" env:"OC_LDAP_CACERT;USERS_LDAP_CACERT" desc:"Path/File name for the root CA certificate (in PEM format) used to validate TLS server certificates of the LDAP service. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
|
||||
Insecure bool `yaml:"insecure" env:"OC_LDAP_INSECURE;USERS_LDAP_INSECURE" desc:"Disable TLS certificate validation for the LDAP connections. Do not set this in production environments." introductionVersion:"1.0.0"`
|
||||
BindDN string `yaml:"bind_dn" env:"OC_LDAP_BIND_DN;USERS_LDAP_BIND_DN" desc:"LDAP DN to use for simple bind authentication with the target LDAP server." introductionVersion:"1.0.0"`
|
||||
BindPassword string `yaml:"bind_password" env:"OC_LDAP_BIND_PASSWORD;USERS_LDAP_BIND_PASSWORD" desc:"Password to use for authenticating the 'bind_dn'." introductionVersion:"1.0.0"`
|
||||
UserBaseDN string `yaml:"user_base_dn" env:"OC_LDAP_USER_BASE_DN;USERS_LDAP_USER_BASE_DN" desc:"Search base DN for looking up LDAP users." introductionVersion:"1.0.0"`
|
||||
GroupBaseDN string `yaml:"group_base_dn" env:"OC_LDAP_GROUP_BASE_DN;USERS_LDAP_GROUP_BASE_DN" desc:"Search base DN for looking up LDAP groups." introductionVersion:"1.0.0"`
|
||||
TenantBaseDN string `yaml:"tenant_base_dn" env:"OC_LDAP_TENANT_BASE_DN;USERS_LDAP_TENANT_BASE_DN" desc:"Search base DN for looking up LDAP tenants. Only relevant in multi-tenant setups." introductionVersion:"6.1.0"`
|
||||
TenantScope string `yaml:"tenant_scope" env:"OC_LDAP_TENANT_SCOPE;USERS_LDAP_TENANT_SCOPE" desc:"LDAP search scope to use when looking up tenants. Supported values are 'base', 'one' and 'sub'. Only relevant in multi-tenant setups." introductionVersion:"6.1.0"`
|
||||
TenantFilter string `yaml:"tenant_filter" env:"OC_LDAP_TENANT_FILTER;USERS_LDAP_TENANT_FILTER" desc:"LDAP filter to add to the default filters for tenant searches. Only relevant in multi-tenant setups." introductionVersion:"6.1.0"`
|
||||
TenantObjectClass string `yaml:"tenant_object_class" env:"OC_LDAP_TENANT_OBJECTCLASS;USERS_LDAP_TENANT_OBJECTCLASS" desc:"The object class to use for tenants in the default tenant search filter. Only relevant in multi-tenant setups." introductionVersion:"6.1.0"`
|
||||
TenantSchema LDAPTenantSchema `yaml:"tenant_schema"`
|
||||
UserScope string `yaml:"user_scope" env:"OC_LDAP_USER_SCOPE;USERS_LDAP_USER_SCOPE" desc:"LDAP search scope to use when looking up users. Supported values are 'base', 'one' and 'sub'." introductionVersion:"1.0.0"`
|
||||
GroupScope string `yaml:"group_scope" env:"OC_LDAP_GROUP_SCOPE;USERS_LDAP_GROUP_SCOPE" desc:"LDAP search scope to use when looking up groups. Supported values are 'base', 'one' and 'sub'." introductionVersion:"1.0.0"`
|
||||
UserSubstringFilterType string `yaml:"user_substring_filter_type" env:"LDAP_USER_SUBSTRING_FILTER_TYPE;USERS_LDAP_USER_SUBSTRING_FILTER_TYPE" desc:"Type of substring search filter to use for substring searches for users. Possible values: 'initial' for doing prefix only searches, 'final' for doing suffix only searches or 'any' for doing full substring searches" introductionVersion:"1.0.0"`
|
||||
UserFilter string `yaml:"user_filter" env:"OC_LDAP_USER_FILTER;USERS_LDAP_USER_FILTER" desc:"LDAP filter to add to the default filters for user search like '(objectclass=openCloudUser)'." introductionVersion:"1.0.0"`
|
||||
GroupFilter string `yaml:"group_filter" env:"OC_LDAP_GROUP_FILTER;USERS_LDAP_GROUP_FILTER" desc:"LDAP filter to add to the default filters for group searches." introductionVersion:"1.0.0"`
|
||||
UserObjectClass string `yaml:"user_object_class" env:"OC_LDAP_USER_OBJECTCLASS;USERS_LDAP_USER_OBJECTCLASS" desc:"The object class to use for users in the default user search filter like 'inetOrgPerson'." introductionVersion:"1.0.0"`
|
||||
GroupObjectClass string `yaml:"group_object_class" env:"OC_LDAP_GROUP_OBJECTCLASS;USERS_LDAP_GROUP_OBJECTCLASS" desc:"The object class to use for groups in the default group search filter like 'groupOfNames'." introductionVersion:"1.0.0"`
|
||||
IDP string `yaml:"idp" env:"OC_URL;OC_OIDC_ISSUER;USERS_IDP_URL" desc:"The identity provider value to set in the userids of the CS3 user objects for users returned by this user provider." introductionVersion:"1.0.0"`
|
||||
DisableUserMechanism string `yaml:"disable_user_mechanism" env:"OC_LDAP_DISABLE_USER_MECHANISM;USERS_LDAP_DISABLE_USER_MECHANISM" desc:"An option to control the behavior for disabling users. Valid options are 'none', 'attribute' and 'group'. If set to 'group', disabling a user via API will add the user to the configured group for disabled users, if set to 'attribute' this will be done in the ldap user entry, if set to 'none' the disable request is not processed." introductionVersion:"1.0.0"`
|
||||
UserTypeAttribute string `yaml:"user_type_attribute" env:"OC_LDAP_USER_SCHEMA_USER_TYPE;USERS_LDAP_USER_TYPE_ATTRIBUTE" desc:"LDAP Attribute to distinguish between 'Member' and 'Guest' users. Default is 'openCloudUserType'." introductionVersion:"1.0.0"`
|
||||
LdapDisabledUsersGroupDN string `yaml:"ldap_disabled_users_group_dn" env:"OC_LDAP_DISABLED_USERS_GROUP_DN;USERS_LDAP_DISABLED_USERS_GROUP_DN" desc:"The distinguished name of the group to which added users will be classified as disabled when 'disable_user_mechanism' is set to 'group'." introductionVersion:"1.0.0"`
|
||||
UserSchema LDAPUserSchema `yaml:"user_schema"`
|
||||
GroupSchema LDAPGroupSchema `yaml:"group_schema"`
|
||||
UserScope string `yaml:"user_scope" env:"OC_LDAP_USER_SCOPE;USERS_LDAP_USER_SCOPE" desc:"LDAP search scope to use when looking up users. Supported values are 'base', 'one' and 'sub'." introductionVersion:"1.0.0"`
|
||||
GroupScope string `yaml:"group_scope" env:"OC_LDAP_GROUP_SCOPE;USERS_LDAP_GROUP_SCOPE" desc:"LDAP search scope to use when looking up groups. Supported values are 'base', 'one' and 'sub'." introductionVersion:"1.0.0"`
|
||||
UserSubstringFilterType string `yaml:"user_substring_filter_type" env:"LDAP_USER_SUBSTRING_FILTER_TYPE;USERS_LDAP_USER_SUBSTRING_FILTER_TYPE" desc:"Type of substring search filter to use for substring searches for users. Possible values: 'initial' for doing prefix only searches, 'final' for doing suffix only searches or 'any' for doing full substring searches" introductionVersion:"1.0.0"`
|
||||
UserFilter string `yaml:"user_filter" env:"OC_LDAP_USER_FILTER;USERS_LDAP_USER_FILTER" desc:"LDAP filter to add to the default filters for user search like '(objectclass=openCloudUser)'." introductionVersion:"1.0.0"`
|
||||
GroupFilter string `yaml:"group_filter" env:"OC_LDAP_GROUP_FILTER;USERS_LDAP_GROUP_FILTER" desc:"LDAP filter to add to the default filters for group searches." introductionVersion:"1.0.0"`
|
||||
UserObjectClass string `yaml:"user_object_class" env:"OC_LDAP_USER_OBJECTCLASS;USERS_LDAP_USER_OBJECTCLASS" desc:"The object class to use for users in the default user search filter like 'inetOrgPerson'." introductionVersion:"1.0.0"`
|
||||
GroupObjectClass string `yaml:"group_object_class" env:"OC_LDAP_GROUP_OBJECTCLASS;USERS_LDAP_GROUP_OBJECTCLASS" desc:"The object class to use for groups in the default group search filter like 'groupOfNames'." introductionVersion:"1.0.0"`
|
||||
IDP string `yaml:"idp" env:"OC_URL;OC_OIDC_ISSUER;USERS_IDP_URL" desc:"The identity provider value to set in the userids of the CS3 user objects for users returned by this user provider." introductionVersion:"1.0.0"`
|
||||
DisableUserMechanism string `yaml:"disable_user_mechanism" env:"OC_LDAP_DISABLE_USER_MECHANISM;USERS_LDAP_DISABLE_USER_MECHANISM" desc:"An option to control the behavior for disabling users. Valid options are 'none', 'attribute' and 'group'. If set to 'group', disabling a user via API will add the user to the configured group for disabled users, if set to 'attribute' this will be done in the ldap user entry, if set to 'none' the disable request is not processed." introductionVersion:"1.0.0"`
|
||||
UserTypeAttribute string `yaml:"user_type_attribute" env:"OC_LDAP_USER_SCHEMA_USER_TYPE;USERS_LDAP_USER_TYPE_ATTRIBUTE" desc:"LDAP Attribute to distinguish between 'Member' and 'Guest' users. Default is 'openCloudUserType'." introductionVersion:"1.0.0"`
|
||||
LdapDisabledUsersGroupDN string `yaml:"ldap_disabled_users_group_dn" env:"OC_LDAP_DISABLED_USERS_GROUP_DN;USERS_LDAP_DISABLED_USERS_GROUP_DN" desc:"The distinguished name of the group to which added users will be classified as disabled when 'disable_user_mechanism' is set to 'group'." introductionVersion:"1.0.0"`
|
||||
UserSchema LDAPUserSchema `yaml:"user_schema"`
|
||||
GroupSchema LDAPGroupSchema `yaml:"group_schema"`
|
||||
LookupCacheTTL time.Duration `yaml:"lookup_cache_ttl" env:"OC_LDAP_LOOKUP_CACHE_TTL;USERS_LDAP_LOOKUP_CACHE_TTL" desc:"The time to live for the in-memory cache of LDAP lookups. This cache is used to reduce the number of LDAP queries for user and group lookups." introductionVersion:"%%NEXT%%"`
|
||||
}
|
||||
|
||||
type LDAPUserSchema struct {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
"github.com/opencloud-eu/opencloud/services/users/pkg/config"
|
||||
@@ -66,6 +68,7 @@ func DefaultConfig() *config.Config {
|
||||
Groupname: "cn",
|
||||
Member: "member",
|
||||
},
|
||||
LookupCacheTTL: 10 * time.Second,
|
||||
},
|
||||
JSON: config.JSONDriver{},
|
||||
OwnCloudSQL: config.OwnCloudSQLDriver{
|
||||
|
||||
@@ -103,5 +103,6 @@ func ldapConfigFromString(cfg config.LDAPDriver) map[string]any {
|
||||
"externalId": cfg.TenantSchema.ExternalID,
|
||||
"name": cfg.TenantSchema.Name,
|
||||
},
|
||||
"lookup_cache_ttl": cfg.LookupCacheTTL,
|
||||
}
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package expirable
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/golang-lru/v2/internal"
|
||||
)
|
||||
|
||||
// EvictCallback is used to get a callback when a cache entry is evicted
|
||||
type EvictCallback[K comparable, V any] func(key K, value V)
|
||||
|
||||
// LRU implements a thread-safe LRU with expirable entries.
|
||||
type LRU[K comparable, V any] struct {
|
||||
size int
|
||||
evictList *internal.LruList[K, V]
|
||||
items map[K]*internal.Entry[K, V]
|
||||
onEvict EvictCallback[K, V]
|
||||
|
||||
// expirable options
|
||||
mu sync.Mutex
|
||||
ttl time.Duration
|
||||
done chan struct{}
|
||||
|
||||
// buckets for expiration
|
||||
buckets []bucket[K, V]
|
||||
// uint8 because it's number between 0 and numBuckets
|
||||
nextCleanupBucket uint8
|
||||
}
|
||||
|
||||
// bucket is a container for holding entries to be expired
|
||||
type bucket[K comparable, V any] struct {
|
||||
entries map[K]*internal.Entry[K, V]
|
||||
newestEntry time.Time
|
||||
}
|
||||
|
||||
// noEvictionTTL - very long ttl to prevent eviction
|
||||
const noEvictionTTL = time.Hour * 24 * 365 * 10
|
||||
|
||||
// because of uint8 usage for nextCleanupBucket, should not exceed 256.
|
||||
// casting it as uint8 explicitly requires type conversions in multiple places
|
||||
const numBuckets = 100
|
||||
|
||||
// NewLRU returns a new thread-safe cache with expirable entries.
|
||||
//
|
||||
// Size parameter set to 0 makes cache of unlimited size, e.g. turns LRU mechanism off.
|
||||
//
|
||||
// Providing 0 TTL turns expiring off.
|
||||
//
|
||||
// Delete expired entries every 1/100th of ttl value. Goroutine which deletes expired entries runs indefinitely.
|
||||
func NewLRU[K comparable, V any](size int, onEvict EvictCallback[K, V], ttl time.Duration) *LRU[K, V] {
|
||||
if size < 0 {
|
||||
size = 0
|
||||
}
|
||||
if ttl <= 0 {
|
||||
ttl = noEvictionTTL
|
||||
}
|
||||
|
||||
res := LRU[K, V]{
|
||||
ttl: ttl,
|
||||
size: size,
|
||||
evictList: internal.NewList[K, V](),
|
||||
items: make(map[K]*internal.Entry[K, V]),
|
||||
onEvict: onEvict,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
// initialize the buckets
|
||||
res.buckets = make([]bucket[K, V], numBuckets)
|
||||
for i := 0; i < numBuckets; i++ {
|
||||
res.buckets[i] = bucket[K, V]{entries: make(map[K]*internal.Entry[K, V])}
|
||||
}
|
||||
|
||||
// enable deleteExpired() running in separate goroutine for cache with non-zero TTL
|
||||
//
|
||||
// Important: done channel is never closed, so deleteExpired() goroutine will never exit,
|
||||
// it's decided to add functionality to close it in the version later than v2.
|
||||
if res.ttl != noEvictionTTL {
|
||||
go func(done <-chan struct{}) {
|
||||
ticker := time.NewTicker(res.ttl / numBuckets)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
res.deleteExpired()
|
||||
}
|
||||
}
|
||||
}(res.done)
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
// Purge clears the cache completely.
|
||||
// onEvict is called for each evicted key.
|
||||
func (c *LRU[K, V]) Purge() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
for k, v := range c.items {
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(k, v.Value)
|
||||
}
|
||||
delete(c.items, k)
|
||||
}
|
||||
for _, b := range c.buckets {
|
||||
for _, ent := range b.entries {
|
||||
delete(b.entries, ent.Key)
|
||||
}
|
||||
}
|
||||
c.evictList.Init()
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
// Returns false if there was no eviction: the item was already in the cache,
|
||||
// or the size was not exceeded.
|
||||
func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
now := time.Now()
|
||||
|
||||
// Check for existing item
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
c.removeFromBucket(ent) // remove the entry from its current bucket as expiresAt is renewed
|
||||
ent.Value = value
|
||||
ent.ExpiresAt = now.Add(c.ttl)
|
||||
c.addToBucket(ent)
|
||||
return false
|
||||
}
|
||||
|
||||
// Add new item
|
||||
ent := c.evictList.PushFrontExpirable(key, value, now.Add(c.ttl))
|
||||
c.items[key] = ent
|
||||
c.addToBucket(ent) // adds the entry to the appropriate bucket and sets entry.expireBucket
|
||||
|
||||
evict := c.size > 0 && c.evictList.Length() > c.size
|
||||
// Verify size not exceeded
|
||||
if evict {
|
||||
c.removeOldest()
|
||||
}
|
||||
return evict
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *LRU[K, V]) Get(key K) (value V, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
var ent *internal.Entry[K, V]
|
||||
if ent, ok = c.items[key]; ok {
|
||||
// Expired item check
|
||||
if time.Now().After(ent.ExpiresAt) {
|
||||
return value, false
|
||||
}
|
||||
c.evictList.MoveToFront(ent)
|
||||
return ent.Value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Contains checks if a key is in the cache, without updating the recent-ness
|
||||
// or deleting it for being stale.
|
||||
func (c *LRU[K, V]) Contains(key K) (ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
_, ok = c.items[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating
|
||||
// the "recently used"-ness of the key.
|
||||
func (c *LRU[K, V]) Peek(key K) (value V, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
var ent *internal.Entry[K, V]
|
||||
if ent, ok = c.items[key]; ok {
|
||||
// Expired item check
|
||||
if time.Now().After(ent.ExpiresAt) {
|
||||
return value, false
|
||||
}
|
||||
return ent.Value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache, returning if the
|
||||
// key was contained.
|
||||
func (c *LRU[K, V]) Remove(key K) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveOldest removes the oldest item from the cache.
|
||||
func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if ent := c.evictList.Back(); ent != nil {
|
||||
c.removeElement(ent)
|
||||
return ent.Key, ent.Value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetOldest returns the oldest entry
|
||||
func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if ent := c.evictList.Back(); ent != nil {
|
||||
return ent.Key, ent.Value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||
func (c *LRU[K, V]) Keys() []K {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
keys := make([]K, 0, len(c.items))
|
||||
for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() {
|
||||
keys = append(keys, ent.Key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns a slice of the values in the cache, from oldest to newest.
|
||||
// Expired entries are filtered out.
|
||||
func (c *LRU[K, V]) Values() []V {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
values := make([]V, len(c.items))
|
||||
i := 0
|
||||
now := time.Now()
|
||||
for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() {
|
||||
if now.After(ent.ExpiresAt) {
|
||||
continue
|
||||
}
|
||||
values[i] = ent.Value
|
||||
i++
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (c *LRU[K, V]) Len() int {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.evictList.Length()
|
||||
}
|
||||
|
||||
// Resize changes the cache size. Size of 0 means unlimited.
|
||||
func (c *LRU[K, V]) Resize(size int) (evicted int) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if size <= 0 {
|
||||
c.size = 0
|
||||
return 0
|
||||
}
|
||||
diff := c.evictList.Length() - size
|
||||
if diff < 0 {
|
||||
diff = 0
|
||||
}
|
||||
for i := 0; i < diff; i++ {
|
||||
c.removeOldest()
|
||||
}
|
||||
c.size = size
|
||||
return diff
|
||||
}
|
||||
|
||||
// Close destroys cleanup goroutine. To clean up the cache, run Purge() before Close().
|
||||
// func (c *LRU[K, V]) Close() {
|
||||
// c.mu.Lock()
|
||||
// defer c.mu.Unlock()
|
||||
// select {
|
||||
// case <-c.done:
|
||||
// return
|
||||
// default:
|
||||
// }
|
||||
// close(c.done)
|
||||
// }
|
||||
|
||||
// removeOldest removes the oldest item from the cache. Has to be called with lock!
|
||||
func (c *LRU[K, V]) removeOldest() {
|
||||
if ent := c.evictList.Back(); ent != nil {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// removeElement is used to remove a given list element from the cache. Has to be called with lock!
|
||||
func (c *LRU[K, V]) removeElement(e *internal.Entry[K, V]) {
|
||||
c.evictList.Remove(e)
|
||||
delete(c.items, e.Key)
|
||||
c.removeFromBucket(e)
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(e.Key, e.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// deleteExpired deletes expired records from the oldest bucket, waiting for the newest entry
|
||||
// in it to expire first.
|
||||
func (c *LRU[K, V]) deleteExpired() {
|
||||
c.mu.Lock()
|
||||
bucketIdx := c.nextCleanupBucket
|
||||
timeToExpire := time.Until(c.buckets[bucketIdx].newestEntry)
|
||||
// wait for newest entry to expire before cleanup without holding lock
|
||||
if timeToExpire > 0 {
|
||||
c.mu.Unlock()
|
||||
time.Sleep(timeToExpire)
|
||||
c.mu.Lock()
|
||||
}
|
||||
for _, ent := range c.buckets[bucketIdx].entries {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
c.nextCleanupBucket = (c.nextCleanupBucket + 1) % numBuckets
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// addToBucket adds entry to expire bucket so that it will be cleaned up when the time comes. Has to be called with lock!
|
||||
func (c *LRU[K, V]) addToBucket(e *internal.Entry[K, V]) {
|
||||
bucketID := (numBuckets + c.nextCleanupBucket - 1) % numBuckets
|
||||
e.ExpireBucket = bucketID
|
||||
c.buckets[bucketID].entries[e.Key] = e
|
||||
if c.buckets[bucketID].newestEntry.Before(e.ExpiresAt) {
|
||||
c.buckets[bucketID].newestEntry = e.ExpiresAt
|
||||
}
|
||||
}
|
||||
|
||||
// removeFromBucket removes the entry from its corresponding bucket. Has to be called with lock!
|
||||
func (c *LRU[K, V]) removeFromBucket(e *internal.Entry[K, V]) {
|
||||
delete(c.buckets[e.ExpireBucket].entries, e.Key)
|
||||
}
|
||||
+14
@@ -53,6 +53,20 @@ func (re *Event) Ack() error {
|
||||
return re.msg.Ack()
|
||||
}
|
||||
|
||||
func (re *Event) Nak() error {
|
||||
if re.msg == nil {
|
||||
return errors.New("cannot nack event without message")
|
||||
}
|
||||
return re.msg.Nak()
|
||||
}
|
||||
|
||||
func (re *Event) Term() error {
|
||||
if re.msg == nil {
|
||||
return errors.New("cannot terminate event without message")
|
||||
}
|
||||
return re.msg.Term()
|
||||
}
|
||||
|
||||
func (re *Event) InProgress() error {
|
||||
if re.msg == nil {
|
||||
return errors.New("cannot mark event as in progress without message")
|
||||
|
||||
Generated
Vendored
+25
@@ -82,6 +82,7 @@ var (
|
||||
events.PostprocessingFinished{},
|
||||
events.PostprocessingStepFinished{},
|
||||
events.RestartPostprocessing{},
|
||||
events.StartPostprocessingStep{},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -441,6 +442,22 @@ func (fs *Decomposedfs) Postprocessing(ch <-chan events.Event) {
|
||||
}); err != nil {
|
||||
sublog.Error().Err(err).Msg("Failed to publish BytesReceived event")
|
||||
}
|
||||
case events.StartPostprocessingStep:
|
||||
sublog := log.With().Str("event", "StartPostprocessingStep").Str("uploadid", ev.UploadID).Logger()
|
||||
if ev.UploadID == "" {
|
||||
sublog.Error().Msg("UploadID is empty, cannot start postprocessing step")
|
||||
continue
|
||||
}
|
||||
session, err := fs.sessionStore.Get(ctx, ev.UploadID)
|
||||
if err != nil {
|
||||
sublog.Error().Err(err).Msg("Failed to get upload")
|
||||
continue
|
||||
}
|
||||
session.SetStatus(upload.SessionStatusProcessing, "started postprocessing step: "+string(ev.StepToStart))
|
||||
err = session.Persist(ctx)
|
||||
if err != nil {
|
||||
sublog.Error().Err(err).Msg("Failed to persist upload session after starting postprocessing step")
|
||||
}
|
||||
case events.PostprocessingStepFinished:
|
||||
sublog := log.With().Str("event", "PostprocessingStepFinished").Str("uploadid", ev.UploadID).Logger()
|
||||
if ev.FinishedStep != events.PPStepAntivirus {
|
||||
@@ -552,6 +569,14 @@ func (fs *Decomposedfs) Postprocessing(ch <-chan events.Event) {
|
||||
sublog = log.With().Str("spaceid", session.SpaceID()).Str("nodeid", session.NodeID()).Logger()
|
||||
|
||||
session.SetScanData(res.Description, res.Scandate)
|
||||
var status string
|
||||
switch ev.Outcome {
|
||||
case events.PPOutcomeAbort, events.PPOutcomeDelete:
|
||||
status = upload.SessionStatusFailed
|
||||
default:
|
||||
status = upload.SessionStatusProcessing
|
||||
}
|
||||
session.SetStatus(status, "finished postprocessing step: "+string(ev.FinishedStep))
|
||||
if err := session.Persist(ctx); err != nil {
|
||||
sublog.Error().Err(err).Msg("Failed to persist scan results")
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
@@ -102,6 +102,7 @@ const (
|
||||
SpaceImageAttr string = OcPrefix + "space.image"
|
||||
SpaceAliasAttr string = OcPrefix + "space.alias"
|
||||
SpaceTenantIDAttr string = OcPrefix + "space.tenantid"
|
||||
SpaceContentTypeAttr string = OcPrefix + "space.contenttype"
|
||||
|
||||
UserAcePrefix string = "u:"
|
||||
GroupAcePrefix string = "g:"
|
||||
|
||||
+12
-1
@@ -99,6 +99,7 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr
|
||||
}
|
||||
|
||||
description := utils.ReadPlainFromOpaque(req.Opaque, "description")
|
||||
contentType := utils.ReadPlainFromOpaque(req.Opaque, "contentType")
|
||||
alias := utils.ReadPlainFromOpaque(req.Opaque, "spaceAlias")
|
||||
if alias == "" {
|
||||
alias = templates.WithSpacePropertiesAndUser(u, req.Type, req.Name, spaceID, fs.o.GeneralSpaceAliasTemplate)
|
||||
@@ -192,6 +193,10 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr
|
||||
metadata.SetString(prefixes.SpaceDescriptionAttr, description)
|
||||
}
|
||||
|
||||
if contentType != "" {
|
||||
metadata.SetString(prefixes.SpaceContentTypeAttr, contentType)
|
||||
}
|
||||
|
||||
if alias != "" {
|
||||
metadata.SetString(prefixes.SpaceAliasAttr, alias)
|
||||
}
|
||||
@@ -607,6 +612,9 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up
|
||||
if description, ok := space.Opaque.Map["description"]; ok {
|
||||
metadata[prefixes.SpaceDescriptionAttr] = description.Value
|
||||
}
|
||||
if contentType, ok := space.Opaque.Map["contentType"]; ok {
|
||||
metadata[prefixes.SpaceContentTypeAttr] = contentType.Value
|
||||
}
|
||||
if alias := utils.ReadPlainFromOpaque(space.Opaque, "spaceAlias"); alias != "" {
|
||||
metadata.SetString(prefixes.SpaceAliasAttr, alias)
|
||||
}
|
||||
@@ -660,7 +668,7 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up
|
||||
|
||||
if !permissions.IsManager(sp) {
|
||||
// We are not a space manager. We need to check for additional permissions.
|
||||
k := []string{prefixes.NameAttr, prefixes.SpaceDescriptionAttr}
|
||||
k := []string{prefixes.NameAttr, prefixes.SpaceDescriptionAttr, prefixes.SpaceContentTypeAttr}
|
||||
if !permissions.IsEditor(sp) {
|
||||
k = append(k, prefixes.SpaceReadmeAttr, prefixes.SpaceAliasAttr, prefixes.SpaceImageAttr)
|
||||
}
|
||||
@@ -1118,6 +1126,9 @@ func (fs *Decomposedfs) StorageSpaceFromNode(ctx context.Context, n *node.Node,
|
||||
if sd := spaceAttributes.String(prefixes.SpaceDescriptionAttr); sd != "" {
|
||||
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "description", sd)
|
||||
}
|
||||
if se := spaceAttributes.String(prefixes.SpaceContentTypeAttr); se != "" {
|
||||
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "contentType", se)
|
||||
}
|
||||
if sr := spaceAttributes.String(prefixes.SpaceReadmeAttr); sr != "" {
|
||||
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "readme", storagespace.FormatResourceID(
|
||||
&provider.ResourceId{StorageId: space.Root.StorageId, SpaceId: space.GetRoot().GetSpaceId(), OpaqueId: sr},
|
||||
|
||||
Generated
Vendored
+24
-3
@@ -46,6 +46,12 @@ type DecomposedFsSession struct {
|
||||
info tusd.FileInfo
|
||||
}
|
||||
|
||||
const (
|
||||
SessionStatusUploading = "uploading"
|
||||
SessionStatusProcessing = "processing"
|
||||
SessionStatusFailed = "failed"
|
||||
)
|
||||
|
||||
// Context returns a context with the user, logger and lockid used when initiating the upload session
|
||||
func (session *DecomposedFsSession) Context(ctx context.Context) context.Context { // restore logger from file info
|
||||
sub := session.store.log.With().Int("pid", os.Getpid()).Logger()
|
||||
@@ -307,10 +313,9 @@ func (session *DecomposedFsSession) MTime() time.Time {
|
||||
return t
|
||||
}
|
||||
|
||||
// IsProcessing returns true if all bytes have been received. The session then has entered postprocessing state.
|
||||
// IsProcessing returns true if the upload is in the processing state, meaning that the upload has finished and postprocessing is still running.
|
||||
func (session *DecomposedFsSession) IsProcessing() bool {
|
||||
// We might need a more sophisticated way to determine processing status soon
|
||||
return session.info.Size == session.info.Offset && session.info.MetaData["scanResult"] == ""
|
||||
return session.info.MetaData["status"] == SessionStatusProcessing
|
||||
}
|
||||
|
||||
// binPath returns the path to the file storing the binary data.
|
||||
@@ -339,6 +344,22 @@ func (session *DecomposedFsSession) ScanData() (string, time.Time) {
|
||||
return session.info.MetaData["scanResult"], d
|
||||
}
|
||||
|
||||
// SetStatus sets the status of the upload session
|
||||
func (session *DecomposedFsSession) SetStatus(status, msg string) {
|
||||
session.info.MetaData["status"] = status
|
||||
session.info.MetaData["statusMessage"] = msg
|
||||
}
|
||||
|
||||
// Status returns the status of the upload session
|
||||
func (session *DecomposedFsSession) Status() string {
|
||||
return session.info.MetaData["status"]
|
||||
}
|
||||
|
||||
// StatusMessage returns the status message of the upload session
|
||||
func (session *DecomposedFsSession) StatusMessage() string {
|
||||
return session.info.MetaData["statusMessage"]
|
||||
}
|
||||
|
||||
// sessionPath returns the path to the .info file storing the file's info.
|
||||
func sessionPath(root, id string) string {
|
||||
return filepath.Join(root, "uploads", id+".info")
|
||||
|
||||
Generated
Vendored
+3
-1
@@ -94,7 +94,9 @@ func (store DecomposedFsStore) New(ctx context.Context) *DecomposedFsSession {
|
||||
Storage: map[string]string{
|
||||
"Type": "DecomposedFsStore",
|
||||
},
|
||||
MetaData: tusd.MetaData{},
|
||||
MetaData: tusd.MetaData{
|
||||
"status": string(SessionStatusUploading),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Generated
Vendored
+17
-1
@@ -194,8 +194,18 @@ func (session *DecomposedFsSession) FinishUploadDecomposed(ctx context.Context)
|
||||
|
||||
n, err := session.store.CreateNodeForUpload(ctx, session, attrs)
|
||||
if err != nil {
|
||||
session.SetStatus(SessionStatusFailed, err.Error())
|
||||
if perr := session.Persist(ctx); perr != nil {
|
||||
log.Error().Err(perr).Msg("failed to persist upload session after setting status to failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
session.SetStatus(SessionStatusProcessing, "")
|
||||
if err = session.Persist(ctx); err != nil {
|
||||
log.Error().Err(err).Msg("failed to persist upload session after setting status to processing")
|
||||
}
|
||||
|
||||
// increase the processing counter for every started processing
|
||||
// will be decreased in Cleanup()
|
||||
metrics.UploadProcessing.Inc()
|
||||
@@ -493,12 +503,18 @@ func (session *DecomposedFsSession) Cleanup(revertNodeMetadata, cleanBin, cleanI
|
||||
|
||||
// URL returns a url to download an upload
|
||||
func (session *DecomposedFsSession) URL(_ context.Context) (string, error) {
|
||||
|
||||
u := joinurl(session.store.tknopts.DownloadEndpoint, "tus/", session.ID())
|
||||
if session.store.tknopts.DataGatewayEndpoint == "" {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// we need to create a token
|
||||
type transferClaims struct {
|
||||
jwt.RegisteredClaims
|
||||
Target string `json:"target"`
|
||||
}
|
||||
|
||||
u := joinurl(session.store.tknopts.DownloadEndpoint, "tus/", session.ID())
|
||||
ttl := time.Duration(session.store.tknopts.TransferExpires) * time.Second
|
||||
claims := transferClaims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
|
||||
+5
@@ -94,6 +94,11 @@ type UploadSession interface {
|
||||
|
||||
// ScanData returns the scan data for the UploadSession
|
||||
ScanData() (string, time.Time)
|
||||
|
||||
// Status returns the status for the UploadSession
|
||||
Status() string
|
||||
// StatusMessage returns the status message for the UploadSession
|
||||
StatusMessage() string
|
||||
}
|
||||
|
||||
// UploadSessionFilter can be used to filter upload sessions
|
||||
|
||||
Generated
Vendored
+16
@@ -338,6 +338,22 @@ func (s *OcisSession) ScanData() (string, time.Time) {
|
||||
return s.info.MetaData["scanResult"], d
|
||||
}
|
||||
|
||||
func (s *OcisSession) SetStatus(status string) {
|
||||
s.info.MetaData["status"] = status
|
||||
}
|
||||
|
||||
func (s *OcisSession) Status() string {
|
||||
return s.info.MetaData["status"]
|
||||
}
|
||||
|
||||
func (s *OcisSession) SetStatusMessage(message string) {
|
||||
s.info.MetaData["status_message"] = message
|
||||
}
|
||||
|
||||
func (s *OcisSession) StatusMessage() string {
|
||||
return s.info.MetaData["status_message"]
|
||||
}
|
||||
|
||||
// sessionPath returns the path to the .info file storing the file's info.
|
||||
func sessionPath(root, id string) string {
|
||||
return filepath.Join(root, "uploads", id+".info")
|
||||
|
||||
Generated
Vendored
+6
-1
@@ -371,12 +371,17 @@ func (session *OcisSession) Cleanup(revertNodeMetadata, cleanBin, cleanInfo bool
|
||||
|
||||
// URL returns a url to download an upload
|
||||
func (session *OcisSession) URL(_ context.Context) (string, error) {
|
||||
u := joinurl(session.store.tknopts.DownloadEndpoint, "tus/", session.ID())
|
||||
if session.store.tknopts.DataGatewayEndpoint == "" {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// we need to create a token
|
||||
type transferClaims struct {
|
||||
jwt.RegisteredClaims
|
||||
Target string `json:"target"`
|
||||
}
|
||||
|
||||
u := joinurl(session.store.tknopts.DownloadEndpoint, "tus/", session.ID())
|
||||
ttl := time.Duration(session.store.tknopts.TransferExpires) * time.Second
|
||||
claims := transferClaims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
|
||||
+73
-4
@@ -23,10 +23,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
identityUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/golang-lru/v2/expirable"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/sharedconf"
|
||||
@@ -38,12 +40,44 @@ import (
|
||||
|
||||
// Identity provides methods to query users and groups from an LDAP server
|
||||
type Identity struct {
|
||||
User userConfig `mapstructure:",squash"`
|
||||
Group groupConfig `mapstructure:",squash"`
|
||||
Tenant tenantConfig `mapstructure:",squash"`
|
||||
User userConfig `mapstructure:",squash"`
|
||||
Group groupConfig `mapstructure:",squash"`
|
||||
Tenant tenantConfig `mapstructure:",squash"`
|
||||
LookupCacheTTL time.Duration `mapstructure:"lookup_cache_ttl"`
|
||||
|
||||
lookupCache entryCache
|
||||
}
|
||||
|
||||
const tracerName = "pkg/utils/ldap"
|
||||
// entryCache is a small nil-safe wrapper around the expirable LRU used to cache
|
||||
// LDAP lookups. A zero-value entryCache (nil lru) is a valid, disabled cache:
|
||||
// Get always misses and Add is a no-op. This lets the cache be turned off
|
||||
// entirely (e.g. in acceptance tests) by configuring a lookup_cache_ttl of 0.
|
||||
type entryCache struct {
|
||||
lru *expirable.LRU[string, *ldap.Entry]
|
||||
}
|
||||
|
||||
// Get returns the cached entry for key. It reports a miss when the cache is
|
||||
// disabled (nil lru).
|
||||
func (c entryCache) Get(key string) (*ldap.Entry, bool) {
|
||||
if c.lru == nil {
|
||||
return nil, false
|
||||
}
|
||||
return c.lru.Get(key)
|
||||
}
|
||||
|
||||
// Add stores entry under key. It is a no-op when the cache is disabled (nil lru).
|
||||
func (c entryCache) Add(key string, entry *ldap.Entry) {
|
||||
if c.lru == nil {
|
||||
return
|
||||
}
|
||||
c.lru.Add(key, entry)
|
||||
}
|
||||
|
||||
const (
|
||||
tracerName = "pkg/utils/ldap"
|
||||
|
||||
lookupCacheSize = 1024
|
||||
)
|
||||
|
||||
type userConfig struct {
|
||||
BaseDN string `mapstructure:"user_base_dn"`
|
||||
@@ -223,6 +257,14 @@ func (i *Identity) Setup() error {
|
||||
}
|
||||
}
|
||||
|
||||
if i.LookupCacheTTL > 0 {
|
||||
i.lookupCache = entryCache{lru: expirable.NewLRU[string, *ldap.Entry](lookupCacheSize, nil, i.LookupCacheTTL)}
|
||||
} else {
|
||||
// A TTL of 0 disables the lookup cache entirely. A zero-value
|
||||
// entryCache always misses and never stores anything.
|
||||
i.lookupCache = entryCache{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -254,6 +296,12 @@ func (i *Identity) GetLDAPUserByFilter(ctx context.Context, lc ldap.Client, filt
|
||||
log := appctx.GetLogger(ctx)
|
||||
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPUserByFilter")
|
||||
defer span.End()
|
||||
|
||||
cacheKey := fmt.Sprintf("user:filter:%s", filter)
|
||||
if cached, ok := i.lookupCache.Get(cacheKey); ok {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
i.User.BaseDN, i.User.scopeVal, ldap.NeverDerefAliases, 1, 0, false,
|
||||
filter,
|
||||
@@ -275,6 +323,7 @@ func (i *Identity) GetLDAPUserByFilter(ctx context.Context, lc ldap.Client, filt
|
||||
return nil, errtypes.NotFound(filter)
|
||||
}
|
||||
span.SetStatus(codes.Ok, "")
|
||||
i.lookupCache.Add(cacheKey, res.Entries[0])
|
||||
|
||||
return res.Entries[0], nil
|
||||
}
|
||||
@@ -286,6 +335,11 @@ func (i *Identity) GetLDAPUserByDN(ctx context.Context, lc ldap.Client, dn strin
|
||||
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPUserByDN")
|
||||
defer span.End()
|
||||
|
||||
cacheKey := fmt.Sprintf("user:dn:%s", dn)
|
||||
if cached, ok := i.lookupCache.Get(cacheKey); ok {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
filter := fmt.Sprintf("(objectclass=%s)", i.User.Objectclass)
|
||||
if i.User.Filter != "" {
|
||||
filter = fmt.Sprintf("(&%s%s)", i.User.Filter, filter)
|
||||
@@ -310,6 +364,7 @@ func (i *Identity) GetLDAPUserByDN(ctx context.Context, lc ldap.Client, dn strin
|
||||
if len(res.Entries) == 0 {
|
||||
return nil, errtypes.NotFound(dn)
|
||||
}
|
||||
i.lookupCache.Add(cacheKey, res.Entries[0])
|
||||
|
||||
return res.Entries[0], nil
|
||||
}
|
||||
@@ -483,6 +538,12 @@ func (i *Identity) GetLDAPGroupByFilter(ctx context.Context, lc ldap.Client, fil
|
||||
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPGroupByFilter")
|
||||
defer span.End()
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
cacheKey := fmt.Sprintf("group:filter:%s", filter)
|
||||
if cached, ok := i.lookupCache.Get(cacheKey); ok {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
i.Group.BaseDN, i.Group.scopeVal, ldap.NeverDerefAliases, 1, 0, false,
|
||||
filter,
|
||||
@@ -511,6 +572,7 @@ func (i *Identity) GetLDAPGroupByFilter(ctx context.Context, lc ldap.Client, fil
|
||||
return nil, errtypes.NotFound(filter)
|
||||
}
|
||||
span.SetStatus(codes.Ok, "")
|
||||
i.lookupCache.Add(cacheKey, res.Entries[0])
|
||||
return res.Entries[0], nil
|
||||
}
|
||||
|
||||
@@ -901,6 +963,12 @@ func (i *Identity) GetLDAPTenantByFilter(ctx context.Context, lc ldap.Client, fi
|
||||
log := appctx.GetLogger(ctx)
|
||||
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPTenantByFilter")
|
||||
defer span.End()
|
||||
|
||||
cacheKey := fmt.Sprintf("tenant:filter:%s", filter)
|
||||
if cached, ok := i.lookupCache.Get(cacheKey); ok {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
i.Tenant.BaseDN, i.Tenant.scopeVal, ldap.NeverDerefAliases, 1, 0, false,
|
||||
filter,
|
||||
@@ -922,6 +990,7 @@ func (i *Identity) GetLDAPTenantByFilter(ctx context.Context, lc ldap.Client, fi
|
||||
return nil, errtypes.NotFound(filter)
|
||||
}
|
||||
span.SetStatus(codes.Ok, "")
|
||||
i.lookupCache.Add(cacheKey, res.Entries[0])
|
||||
|
||||
return res.Entries[0], nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
package otelgrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -8,4 +8,4 @@ Use [NewClientHandler] with [grpc.WithStatsHandler] to instrument a gRPC client.
|
||||
|
||||
Use [NewServerHandler] with [grpc.StatsHandler] to instrument a gRPC server.
|
||||
*/
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
package otelgrpc
|
||||
Generated
Vendored
+43
-2
@@ -1,21 +1,62 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
package otelgrpc
|
||||
|
||||
// gRPC tracing middleware
|
||||
// https://opentelemetry.io/docs/specs/semconv/rpc/
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
grpc_codes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// serverAddrAttrsFromCanonicalTarget extracts server address attributes from a
|
||||
// canonical gRPC target as returned by [google.golang.org/grpc.ClientConn.CanonicalTarget].
|
||||
// Canonical targets always have the form "scheme://[authority]/endpoint", e.g.:
|
||||
//
|
||||
// passthrough:///127.0.0.1:7777 → ServerAddress("127.0.0.1"), ServerPort(7777)
|
||||
// dns:///example.com:443 → ServerAddress("example.com"), ServerPort(443)
|
||||
// dns://authority/example.com:443 → ServerAddress("example.com"), ServerPort(443)
|
||||
// unix:///tmp/grpc.sock → ServerAddress("/tmp/grpc.sock")
|
||||
func serverAddrAttrsFromCanonicalTarget(target string) []attribute.KeyValue {
|
||||
// Fast path: confirmed host:port with numeric port, no scheme involved.
|
||||
if h, pStr, err := net.SplitHostPort(target); err == nil {
|
||||
if p, err := strconv.Atoi(pStr); err == nil {
|
||||
return []attribute.KeyValue{
|
||||
semconv.ServerAddress(h),
|
||||
semconv.ServerPort(p),
|
||||
}
|
||||
}
|
||||
}
|
||||
// gRPC URI: scheme://authority/endpoint → endpoint in Path
|
||||
// scheme:endpoint → endpoint in Opaque
|
||||
u, err := url.Parse(target)
|
||||
if err != nil {
|
||||
return []attribute.KeyValue{semconv.ServerAddress(target)}
|
||||
}
|
||||
ep := u.Path
|
||||
if u.Scheme != "unix" && u.Scheme != "unix-abstract" {
|
||||
// Strip the leading "/" added by url.Parse for hierarchical URIs;
|
||||
// preserve it for unix socket paths where the slash is meaningful.
|
||||
ep = strings.TrimPrefix(ep, "/")
|
||||
}
|
||||
if ep == "" {
|
||||
ep = u.Opaque
|
||||
}
|
||||
if ep != "" {
|
||||
return serverAddrAttrs(ep)
|
||||
}
|
||||
return []attribute.KeyValue{semconv.ServerAddress(target)}
|
||||
}
|
||||
|
||||
// serverAddrAttrs returns the server address attributes for the hostport.
|
||||
func serverAddrAttrs(hostport string) []attribute.KeyValue {
|
||||
h, pStr, err := net.SplitHostPort(hostport)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
package otelgrpc
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go
Generated
Vendored
+2
-2
@@ -2,14 +2,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package internal provides internal functionality for the otelgrpc package.
|
||||
package internal // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal"
|
||||
package internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
oldsemconv "go.opentelemetry.io/otel/semconv/v1.37.0" //nolint:depguard // Use of v1.37.0 is required for backward compatibility stability opt-in.
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
)
|
||||
|
||||
// ParseFullMethod returns a span name following the OpenTelemetry semantic
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
package otelgrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
Generated
Vendored
+33
-9
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
package otelgrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
oldrpcconv "go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv" //nolint:depguard // Use of v1.37.0 is required for backward compatibility stability opt-in.
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.41.0/rpcconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.43.0/rpcconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
grpc_codes "google.golang.org/grpc/codes"
|
||||
@@ -26,6 +26,11 @@ import (
|
||||
|
||||
type gRPCContextKey struct{}
|
||||
|
||||
// dialTargetContextKey carries the dial target seeded by client interceptors
|
||||
// so the stats handler can use the hostname for server.address instead of the
|
||||
// post-DNS IP from RemoteAddr.
|
||||
type dialTargetContextKey struct{}
|
||||
|
||||
type gRPCContext struct {
|
||||
metricAttrs []attribute.KeyValue
|
||||
record bool
|
||||
@@ -328,6 +333,20 @@ func (*config) handleRPC(
|
||||
|
||||
switch rs := rs.(type) {
|
||||
case *stats.Begin:
|
||||
// Set server.address early from the dial target when available (upcoming
|
||||
// interceptors will seed this). Covers both success and failure paths since Begin
|
||||
// fires on every RPC regardless of outcome.
|
||||
if rs.Client {
|
||||
if target, ok := ctx.Value(dialTargetContextKey{}).(string); ok && target != "" {
|
||||
attrs := serverAddrAttrsFromCanonicalTarget(target)
|
||||
if span.IsRecording() {
|
||||
span.SetAttributes(attrs...)
|
||||
}
|
||||
if gctx != nil {
|
||||
gctx.metricAttrs = append(gctx.metricAttrs, attrs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
case *stats.InPayload:
|
||||
case *stats.InHeader:
|
||||
if !rs.Client && rs.LocalAddr != nil {
|
||||
@@ -339,13 +358,18 @@ func (*config) handleRPC(
|
||||
case *stats.OutPayload:
|
||||
case *stats.OutTrailer:
|
||||
case *stats.OutHeader:
|
||||
// Only use the resolved IP from RemoteAddr when no dial target was seeded
|
||||
// (i.e. NewClientHandler callers without interceptors). When dialTargetContextKey
|
||||
// is present, Begin already set server.address to the hostname.
|
||||
if rs.Client && rs.RemoteAddr != nil && (span.IsRecording() || gctx != nil) {
|
||||
attrs := serverAddrAttrs(rs.RemoteAddr.String())
|
||||
if span.IsRecording() {
|
||||
span.SetAttributes(attrs...)
|
||||
}
|
||||
if gctx != nil {
|
||||
gctx.metricAttrs = append(gctx.metricAttrs, attrs...)
|
||||
if target, ok := ctx.Value(dialTargetContextKey{}).(string); !ok || target == "" {
|
||||
attrs := serverAddrAttrs(rs.RemoteAddr.String())
|
||||
if span.IsRecording() {
|
||||
span.SetAttributes(attrs...)
|
||||
}
|
||||
if gctx != nil {
|
||||
gctx.metricAttrs = append(gctx.metricAttrs, attrs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
case *stats.End:
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
package otelgrpc
|
||||
|
||||
// Version is the current release version of the gRPC instrumentation.
|
||||
const Version = "0.69.0"
|
||||
const Version = "0.70.0"
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
<!-- Generated. DO NOT MODIFY. -->
|
||||
# Migration from v1.40.0 to v1.41.0
|
||||
|
||||
The `go.opentelemetry.io/otel/semconv/v1.41.0` package should be a drop-in replacement for `go.opentelemetry.io/otel/semconv/v1.40.0` with the following exceptions.
|
||||
|
||||
## Removed
|
||||
|
||||
The following declarations have been removed.
|
||||
Refer to the [OpenTelemetry Semantic Conventions documentation] for deprecation instructions.
|
||||
|
||||
If the type is not listed in the documentation as deprecated, it has been removed in this version due to lack of applicability or use.
|
||||
If you use any of these non-deprecated declarations in your Go application, please [open an issue] describing your use-case.
|
||||
|
||||
- `DeploymentEnvironmentName`
|
||||
|
||||
[OpenTelemetry Semantic Conventions documentation]: https://github.com/open-telemetry/semantic-conventions
|
||||
[open an issue]: https://github.com/open-telemetry/opentelemetry-go/issues/new?template=Blank+issue
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# Semconv v1.41.0
|
||||
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.41.0)
|
||||
-17289
File diff suppressed because it is too large.
Load diff
-11
@@ -1,11 +0,0 @@
|
||||
// Code generated from semantic convention specification. DO NOT EDIT.
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package semconv implements OpenTelemetry semantic conventions.
|
||||
//
|
||||
// OpenTelemetry semantic conventions are agreed standardized naming
|
||||
// patterns for OpenTelemetry things. This package represents the v1.41.0
|
||||
// version of the OpenTelemetry semantic conventions.
|
||||
package semconv
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
// Code generated from semantic convention specification. DO NOT EDIT.
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
// ErrorType returns an [attribute.KeyValue] identifying the error type of err.
|
||||
//
|
||||
// If err is nil, the returned attribute has the default value
|
||||
// [ErrorTypeOther].
|
||||
//
|
||||
// If err or one of the errors in its chain has the method
|
||||
//
|
||||
// ErrorType() string
|
||||
//
|
||||
// the returned attribute has that method's return value. If multiple errors in
|
||||
// the chain implement this method, the value from the first match found by
|
||||
// [errors.As] is used. Otherwise, the returned attribute has a value derived
|
||||
// from the concrete type of err after unwrapping any wrappers created with
|
||||
// [fmt.Errorf].
|
||||
//
|
||||
// The key of the returned attribute is [ErrorTypeKey].
|
||||
func ErrorType(err error) attribute.KeyValue {
|
||||
if err == nil {
|
||||
return ErrorTypeOther
|
||||
}
|
||||
|
||||
return ErrorTypeKey.String(errorType(err))
|
||||
}
|
||||
|
||||
func errorType(err error) string {
|
||||
var s string
|
||||
if et, ok := err.(interface{ ErrorType() string }); ok {
|
||||
// Fast path: check the top-level error first.
|
||||
s = et.ErrorType()
|
||||
} else {
|
||||
// Fallback: search the error chain for an ErrorType method.
|
||||
var et interface{ ErrorType() string }
|
||||
if errors.As(err, &et) {
|
||||
// Prioritize the ErrorType method if available.
|
||||
s = et.ErrorType()
|
||||
}
|
||||
}
|
||||
if s == "" {
|
||||
// Fallback to reflection if the ErrorType method is not supported or
|
||||
// returns an empty value.
|
||||
|
||||
t := reflect.TypeOf(unwrapFmtWrapped(err))
|
||||
pkg, name := t.PkgPath(), t.Name()
|
||||
if pkg != "" && name != "" {
|
||||
s = pkg + "." + name
|
||||
} else {
|
||||
// The type has no package path or name (predeclared, not-defined,
|
||||
// or alias for a not-defined type).
|
||||
//
|
||||
// This is not guaranteed to be unique, but is a best effort.
|
||||
s = t.String()
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var fmtWrapErrorType = reflect.TypeOf(fmt.Errorf("wrapped: %w", errors.New("err")))
|
||||
|
||||
func unwrapFmtWrapped(err error) error {
|
||||
for reflect.TypeOf(err) == fmtWrapErrorType {
|
||||
u := errors.Unwrap(err)
|
||||
if u == nil {
|
||||
return err // When the wrapped error is nil, use the concrete type of the wrapper.
|
||||
}
|
||||
err = u
|
||||
}
|
||||
return err
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// Code generated from semantic convention specification. DO NOT EDIT.
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv
|
||||
|
||||
const (
|
||||
// ExceptionEventName is the name of the Span event representing an exception.
|
||||
ExceptionEventName = "exception"
|
||||
)
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// Code generated from semantic convention specification. DO NOT EDIT.
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconv
|
||||
|
||||
// SchemaURL is the schema URL that matches the version of the semantic conventions
|
||||
// that this package defines. Semconv packages starting from v1.4.0 must declare
|
||||
// non-empty schema URL in the form https://opentelemetry.io/schemas/<version>
|
||||
const SchemaURL = "https://opentelemetry.io/schemas/1.41.0"
|
||||
Generated
Vendored
File renamed without changes.
Vendored
+4
-4
@@ -796,6 +796,7 @@ github.com/hashicorp/go-plugin/runner
|
||||
# github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
## explicit; go 1.18
|
||||
github.com/hashicorp/golang-lru/v2
|
||||
github.com/hashicorp/golang-lru/v2/expirable
|
||||
github.com/hashicorp/golang-lru/v2/internal
|
||||
github.com/hashicorp/golang-lru/v2/simplelru
|
||||
# github.com/hashicorp/yamux v0.1.2
|
||||
@@ -1364,7 +1365,7 @@ github.com/opencloud-eu/icap-client
|
||||
# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260818063016-69f82a7dde55
|
||||
## explicit; go 1.23
|
||||
github.com/opencloud-eu/libre-graph-api-go
|
||||
# github.com/opencloud-eu/reva/v2 v2.48.1-0.20260811071808-9bf68d2ccd96
|
||||
# github.com/opencloud-eu/reva/v2 v2.48.1-0.20260819125853-c9416ac8b8c0
|
||||
## explicit; go 1.25.8
|
||||
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
|
||||
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
|
||||
@@ -2302,7 +2303,7 @@ go.opencensus.io/trace/tracestate
|
||||
## explicit; go 1.24.0
|
||||
go.opentelemetry.io/auto/sdk
|
||||
go.opentelemetry.io/auto/sdk/internal/telemetry
|
||||
# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0
|
||||
# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0
|
||||
## explicit; go 1.25.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal
|
||||
@@ -2333,11 +2334,10 @@ go.opentelemetry.io/otel/semconv/v1.20.0
|
||||
go.opentelemetry.io/otel/semconv/v1.20.0/httpconv
|
||||
go.opentelemetry.io/otel/semconv/v1.37.0
|
||||
go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv
|
||||
go.opentelemetry.io/otel/semconv/v1.41.0
|
||||
go.opentelemetry.io/otel/semconv/v1.41.0/rpcconv
|
||||
go.opentelemetry.io/otel/semconv/v1.43.0
|
||||
go.opentelemetry.io/otel/semconv/v1.43.0/httpconv
|
||||
go.opentelemetry.io/otel/semconv/v1.43.0/otelconv
|
||||
go.opentelemetry.io/otel/semconv/v1.43.0/rpcconv
|
||||
# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0
|
||||
## explicit; go 1.25.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace
|
||||
|
||||
Reference in new issue
Block a user