mirror of
https://github.com/owncloud/ocis.git
synced 2026-10-23 23:29:02 +03:00
feat: [full-ci] [OCISDEV-1563] remove the owncloudsql driver (#13191)
Remove the deprecated owncloudsql driver configuration from the storage-users, auth-basic, users, groups and sharing services and from the migrate command. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
9
changelog/unreleased/change-remove-owncloudsql-driver.md
Normal file
9
changelog/unreleased/change-remove-owncloudsql-driver.md
Normal file
@@ -0,0 +1,9 @@
|
||||
Change: Remove the owncloudsql driver
|
||||
|
||||
We removed the deprecated `owncloudsql` driver from oCIS.
|
||||
|
||||
The services `storage-users`, `auth-basic`, `users`, `groups`, and `sharing` no
|
||||
longer accept the value `owncloudsql` for their driver. The related
|
||||
configuration sections and environment variables are removed.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/13191
|
||||
2
go.mod
2
go.mod
@@ -67,7 +67,7 @@ require (
|
||||
github.com/open-policy-agent/opa v1.21.1
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245
|
||||
github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899
|
||||
github.com/owncloud/reva/v2 v2.0.0-20261019155447-90c24c1b3575
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/xattr v0.4.12
|
||||
github.com/prometheus/client_golang v1.25.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -736,8 +736,8 @@ github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HD
|
||||
github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
|
||||
github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245 h1:JRidLTAKhnvyLMRtVtSF4lhBa0NSAOs6fof+d6JnKII=
|
||||
github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245/go.mod h1:z61VMGAJRtR1nbgXWiNoCkxUXP1B3Je9rMuJbnGd+Og=
|
||||
github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899 h1:IMu076AtnFEdabpBbowcGDYOeFLtm9ycNY1Z43YPYZU=
|
||||
github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899/go.mod h1:cgqg+UTe4ekjMbJj8hgoXSTxQ9fQramKcOUdvsFUrzc=
|
||||
github.com/owncloud/reva/v2 v2.0.0-20261019155447-90c24c1b3575 h1:GOsblJHW/dvNdxxXmCb/PZOGRIg1ci4SVs+pA7hw6t4=
|
||||
github.com/owncloud/reva/v2 v2.0.0-20261019155447-90c24c1b3575/go.mod h1:cgqg+UTe4ekjMbJj8hgoXSTxQ9fQramKcOUdvsFUrzc=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pablodz/inotifywaitgo v0.0.12 h1:DVJJTFMDlSAdO46yisPF2vzozs/r+4k6ih8MVghq78M=
|
||||
|
||||
@@ -501,15 +501,6 @@ func revaShareConfig(cfg *sharing.Config) map[string]interface{} {
|
||||
"enable_expired_shares_cleanup": cfg.UserSharingDrivers.SQL.EnableExpiredSharesCleanup,
|
||||
"janitor_run_interval": cfg.UserSharingDrivers.SQL.JanitorRunInterval,
|
||||
},
|
||||
"owncloudsql": map[string]interface{}{
|
||||
"gateway_addr": cfg.Reva.Address,
|
||||
"storage_mount_id": cfg.UserSharingDrivers.OwnCloudSQL.UserStorageMountID,
|
||||
"db_username": cfg.UserSharingDrivers.OwnCloudSQL.DBUsername,
|
||||
"db_password": cfg.UserSharingDrivers.OwnCloudSQL.DBPassword,
|
||||
"db_host": cfg.UserSharingDrivers.OwnCloudSQL.DBHost,
|
||||
"db_port": cfg.UserSharingDrivers.OwnCloudSQL.DBPort,
|
||||
"db_name": cfg.UserSharingDrivers.OwnCloudSQL.DBName,
|
||||
},
|
||||
"cs3": map[string]interface{}{
|
||||
"gateway_addr": cfg.UserSharingDrivers.CS3.ProviderAddr,
|
||||
"provider_addr": cfg.UserSharingDrivers.CS3.ProviderAddr,
|
||||
|
||||
@@ -52,9 +52,8 @@ type GRPCConfig struct {
|
||||
}
|
||||
|
||||
type AuthProviders struct {
|
||||
LDAP LDAPProvider `yaml:"ldap"`
|
||||
OwnCloudSQL OwnCloudSQLProvider `yaml:"owncloudsql"`
|
||||
JSON JSONProvider `yaml:"json,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
LDAP LDAPProvider `yaml:"ldap"`
|
||||
JSON JSONProvider `yaml:"json,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
}
|
||||
|
||||
type JSONProvider struct {
|
||||
@@ -110,15 +109,3 @@ type LDAPGroupSchema struct {
|
||||
Groupname string `yaml:"group_name" env:"OCIS_LDAP_GROUP_SCHEMA_GROUPNAME;AUTH_BASIC_LDAP_GROUP_SCHEMA_GROUPNAME" desc:"LDAP Attribute to use for the name of groups." introductionVersion:"pre5.0"`
|
||||
Member string `yaml:"member" env:"OCIS_LDAP_GROUP_SCHEMA_MEMBER;AUTH_BASIC_LDAP_GROUP_SCHEMA_MEMBER" desc:"LDAP Attribute that is used for group members." introductionVersion:"pre5.0"`
|
||||
}
|
||||
|
||||
type OwnCloudSQLProvider struct {
|
||||
DBUsername string `yaml:"db_username" env:"AUTH_BASIC_OWNCLOUDSQL_DB_USERNAME" desc:"Database user to use for authenticating with the owncloud database." introductionVersion:"pre5.0"`
|
||||
DBPassword string `yaml:"db_password" env:"AUTH_BASIC_OWNCLOUDSQL_DB_PASSWORD" desc:"Password for the database user." introductionVersion:"pre5.0"`
|
||||
DBHost string `yaml:"db_host" env:"AUTH_BASIC_OWNCLOUDSQL_DB_HOST" desc:"Hostname of the database server." introductionVersion:"pre5.0"`
|
||||
DBPort int `yaml:"db_port" env:"AUTH_BASIC_OWNCLOUDSQL_DB_PORT" desc:"Network port to use for the database connection." introductionVersion:"pre5.0"`
|
||||
DBName string `yaml:"db_name" env:"AUTH_BASIC_OWNCLOUDSQL_DB_NAME" desc:"Name of the owncloud database." introductionVersion:"pre5.0"`
|
||||
IDP string `yaml:"idp" env:"AUTH_BASIC_OWNCLOUDSQL_IDP" desc:"The identity provider value to set in the userids of the CS3 user objects for users returned by this user provider." introductionVersion:"pre5.0"`
|
||||
Nobody int64 `yaml:"nobody" env:"AUTH_BASIC_OWNCLOUDSQL_NOBODY" desc:"Fallback number if no numeric UID and GID properties are provided." introductionVersion:"pre5.0"`
|
||||
JoinUsername bool `yaml:"join_username" env:"AUTH_BASIC_OWNCLOUDSQL_JOIN_USERNAME" desc:"Join the user properties table to read usernames" introductionVersion:"pre5.0"`
|
||||
JoinOwnCloudUUID bool `yaml:"join_owncloud_uuid" env:"AUTH_BASIC_OWNCLOUDSQL_JOIN_OWNCLOUD_UUID" desc:"Join the user properties table to read user ID's." introductionVersion:"pre5.0"`
|
||||
}
|
||||
|
||||
@@ -76,16 +76,6 @@ func DefaultConfig() *config.Config {
|
||||
},
|
||||
},
|
||||
JSON: config.JSONProvider{},
|
||||
OwnCloudSQL: config.OwnCloudSQLProvider{
|
||||
DBUsername: "owncloud",
|
||||
DBHost: "mysql",
|
||||
DBPort: 3306,
|
||||
DBName: "owncloud",
|
||||
IDP: "https://localhost:9200",
|
||||
Nobody: 90,
|
||||
JoinUsername: false,
|
||||
JoinOwnCloudUUID: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,17 +30,6 @@ func AuthBasicConfigFromStruct(cfg *config.Config) map[string]interface{} {
|
||||
"users": cfg.AuthProviders.JSON.File,
|
||||
},
|
||||
"ldap": ldapConfigFromString(cfg.AuthProviders.LDAP),
|
||||
"owncloudsql": map[string]interface{}{
|
||||
"dbusername": cfg.AuthProviders.OwnCloudSQL.DBUsername,
|
||||
"dbpassword": cfg.AuthProviders.OwnCloudSQL.DBPassword,
|
||||
"dbhost": cfg.AuthProviders.OwnCloudSQL.DBHost,
|
||||
"dbport": cfg.AuthProviders.OwnCloudSQL.DBPort,
|
||||
"dbname": cfg.AuthProviders.OwnCloudSQL.DBName,
|
||||
"idp": cfg.AuthProviders.OwnCloudSQL.IDP,
|
||||
"nobody": cfg.AuthProviders.OwnCloudSQL.Nobody,
|
||||
"join_username": cfg.AuthProviders.OwnCloudSQL.JoinUsername,
|
||||
"join_ownclouduuid": cfg.AuthProviders.OwnCloudSQL.JoinOwnCloudUUID,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ type Config struct {
|
||||
|
||||
SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"GROUPS_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token." introductionVersion:"pre5.0"`
|
||||
|
||||
Driver string `yaml:"driver" env:"GROUPS_DRIVER" desc:"The driver which should be used by the groups service. Supported values are 'ldap' and 'owncloudsql'." introductionVersion:"pre5.0"`
|
||||
Driver string `yaml:"driver" env:"GROUPS_DRIVER" desc:"The driver which should be used by the groups service. Supported value is 'ldap'." introductionVersion:"pre5.0"`
|
||||
Drivers Drivers `yaml:"drivers"`
|
||||
|
||||
Context context.Context `yaml:"-"`
|
||||
@@ -53,8 +53,7 @@ type GRPCConfig struct {
|
||||
}
|
||||
|
||||
type Drivers struct {
|
||||
LDAP LDAPDriver `yaml:"ldap"`
|
||||
OwnCloudSQL OwnCloudSQLDriver `yaml:"owncloudsql"`
|
||||
LDAP LDAPDriver `yaml:"ldap"`
|
||||
|
||||
JSON JSONDriver `yaml:"json,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
REST RESTProvider `yaml:"rest,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
@@ -107,19 +106,6 @@ type LDAPGroupSchema struct {
|
||||
Member string `yaml:"member" env:"OCIS_LDAP_GROUP_SCHEMA_MEMBER;GROUPS_LDAP_GROUP_SCHEMA_MEMBER" desc:"LDAP Attribute that is used for group members." introductionVersion:"pre5.0"`
|
||||
}
|
||||
|
||||
type OwnCloudSQLDriver struct {
|
||||
DBUsername string `yaml:"db_username" env:"GROUPS_OWNCLOUDSQL_DB_USERNAME" desc:"Database user to use for authenticating with the owncloud database." introductionVersion:"pre5.0"`
|
||||
DBPassword string `yaml:"db_password" env:"GROUPS_OWNCLOUDSQL_DB_PASSWORD" desc:"Password for the database user." introductionVersion:"pre5.0"`
|
||||
DBHost string `yaml:"db_host" env:"GROUPS_OWNCLOUDSQL_DB_HOST" desc:"Hostname of the database server." introductionVersion:"pre5.0"`
|
||||
DBPort int `yaml:"db_port" env:"GROUPS_OWNCLOUDSQL_DB_PORT" desc:"Network port to use for the database connection." introductionVersion:"pre5.0"`
|
||||
DBName string `yaml:"db_name" env:"GROUPS_OWNCLOUDSQL_DB_NAME" desc:"Name of the owncloud database." introductionVersion:"pre5.0"`
|
||||
IDP string `yaml:"idp" env:"GROUPS_OWNCLOUDSQL_IDP" desc:"The identity provider value to set in the userids of the CS3 user objects for users returned by this user provider." introductionVersion:"pre5.0"`
|
||||
Nobody int64 `yaml:"nobody" env:"GROUPS_OWNCLOUDSQL_NOBODY" desc:"Fallback number if no numeric UID and GID properties are provided." introductionVersion:"pre5.0"`
|
||||
JoinUsername bool `yaml:"join_username" env:"GROUPS_OWNCLOUDSQL_JOIN_USERNAME" desc:"Join the user properties table to read usernames." introductionVersion:"pre5.0"`
|
||||
JoinOwnCloudUUID bool `yaml:"join_owncloud_uuid" env:"GROUPS_OWNCLOUDSQL_JOIN_OWNCLOUD_UUID" desc:"Join the user properties table to read user IDs." introductionVersion:"pre5.0"`
|
||||
EnableMedialSearch bool `yaml:"enable_medial_search" env:"GROUPS_OWNCLOUDSQL_ENABLE_MEDIAL_SEARCH" desc:"Allow 'medial search' when searching for users instead of just doing a prefix search. This allows finding 'Alice' when searching for 'lic'." introductionVersion:"pre5.0"`
|
||||
}
|
||||
|
||||
type JSONDriver struct {
|
||||
File string
|
||||
}
|
||||
|
||||
@@ -72,18 +72,6 @@ func DefaultConfig() *config.Config {
|
||||
Member: "member",
|
||||
},
|
||||
},
|
||||
OwnCloudSQL: config.OwnCloudSQLDriver{
|
||||
DBUsername: "owncloud",
|
||||
DBPassword: "",
|
||||
DBHost: "mysql",
|
||||
DBPort: 3306,
|
||||
DBName: "owncloud",
|
||||
IDP: "https://localhost:9200",
|
||||
Nobody: 90,
|
||||
JoinUsername: false,
|
||||
JoinOwnCloudUUID: false,
|
||||
EnableMedialSearch: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type Config struct {
|
||||
|
||||
SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"SHARING_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token." introductionVersion:"pre5.0"`
|
||||
|
||||
UserSharingDriver string `yaml:"user_sharing_driver" env:"SHARING_USER_DRIVER" desc:"Driver to be used to persist shares. Supported values are 'jsoncs3', 'json', 'cs3' (deprecated) and 'owncloudsql'." introductionVersion:"pre5.0"`
|
||||
UserSharingDriver string `yaml:"user_sharing_driver" env:"SHARING_USER_DRIVER" desc:"Driver to be used to persist shares. Supported values are 'jsoncs3', 'json' and 'cs3' (deprecated)." introductionVersion:"pre5.0"`
|
||||
UserSharingDrivers UserSharingDrivers `yaml:"user_sharing_drivers"`
|
||||
PublicSharingDriver string `yaml:"public_sharing_driver" env:"SHARING_PUBLIC_DRIVER" desc:"Driver to be used to persist public shares. Supported values are 'jsoncs3', 'json' and 'cs3' (deprecated)." introductionVersion:"pre5.0"`
|
||||
PublicSharingDrivers PublicSharingDrivers `yaml:"public_sharing_drivers"`
|
||||
@@ -62,10 +62,9 @@ type GRPCConfig struct {
|
||||
}
|
||||
|
||||
type UserSharingDrivers struct {
|
||||
JSONCS3 UserSharingJSONCS3Driver `yaml:"jsoncs3"`
|
||||
JSON UserSharingJSONDriver `yaml:"json"`
|
||||
CS3 UserSharingCS3Driver `yaml:"cs3"`
|
||||
OwnCloudSQL UserSharingOwnCloudSQLDriver `yaml:"owncloudsql"`
|
||||
JSONCS3 UserSharingJSONCS3Driver `yaml:"jsoncs3"`
|
||||
JSON UserSharingJSONDriver `yaml:"json"`
|
||||
CS3 UserSharingCS3Driver `yaml:"cs3"`
|
||||
|
||||
SQL UserSharingSQLDriver `yaml:"sql,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
}
|
||||
@@ -86,15 +85,6 @@ type UserSharingSQLDriver struct {
|
||||
UserStorageMountID string `yaml:"user_storage_mount_id"`
|
||||
}
|
||||
|
||||
type UserSharingOwnCloudSQLDriver struct {
|
||||
DBUsername string `yaml:"db_username" env:"SHARING_USER_OWNCLOUDSQL_DB_USERNAME" desc:"Username for the database." introductionVersion:"pre5.0"`
|
||||
DBPassword string `yaml:"db_password" env:"SHARING_USER_OWNCLOUDSQL_DB_PASSWORD" desc:"Password for the database." introductionVersion:"pre5.0"`
|
||||
DBHost string `yaml:"db_host" env:"SHARING_USER_OWNCLOUDSQL_DB_HOST" desc:"Hostname or IP of the database server." introductionVersion:"pre5.0"`
|
||||
DBPort int `yaml:"db_port" env:"SHARING_USER_OWNCLOUDSQL_DB_PORT" desc:"Port that the database server is listening on." introductionVersion:"pre5.0"`
|
||||
DBName string `yaml:"db_name" env:"SHARING_USER_OWNCLOUDSQL_DB_NAME" desc:"Name of the database to be used." introductionVersion:"pre5.0"`
|
||||
UserStorageMountID string `yaml:"user_storage_mount_id" env:"SHARING_USER_OWNCLOUDSQL_USER_STORAGE_MOUNT_ID" desc:"Mount ID of the ownCloudSQL users storage for mapping ownCloud 10 shares." introductionVersion:"pre5.0"`
|
||||
}
|
||||
|
||||
type UserSharingCS3Driver struct {
|
||||
ProviderAddr string `yaml:"provider_addr" env:"SHARING_USER_CS3_PROVIDER_ADDR" desc:"GRPC address of the STORAGE-SYSTEM service." introductionVersion:"pre5.0"`
|
||||
SystemUserID string `yaml:"system_user_id" env:"OCIS_SYSTEM_USER_ID;SHARING_USER_CS3_SYSTEM_USER_ID" desc:"ID of the oCIS STORAGE-SYSTEM system user. Admins need to set the ID for the STORAGE-SYSTEM system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"pre5.0"`
|
||||
|
||||
@@ -49,12 +49,6 @@ func DefaultConfig() *config.Config {
|
||||
SystemUserIDP: "internal",
|
||||
MaxConcurrency: 1,
|
||||
},
|
||||
OwnCloudSQL: config.UserSharingOwnCloudSQLDriver{
|
||||
DBUsername: "owncloud",
|
||||
DBHost: "mysql",
|
||||
DBPort: 3306,
|
||||
DBName: "owncloud",
|
||||
},
|
||||
},
|
||||
PublicSharingDriver: "jsoncs3",
|
||||
PublicSharingDrivers: config.PublicSharingDrivers{
|
||||
@@ -69,7 +63,6 @@ func DefaultConfig() *config.Config {
|
||||
ProviderAddr: "com.owncloud.api.storage-system", // system storage
|
||||
SystemUserIDP: "internal",
|
||||
},
|
||||
// TODO implement and add owncloudsql publicshare driver
|
||||
},
|
||||
Events: config.Events{
|
||||
Addr: "127.0.0.1:9233",
|
||||
|
||||
@@ -54,15 +54,6 @@ func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]
|
||||
"enable_expired_shares_cleanup": cfg.UserSharingDrivers.SQL.EnableExpiredSharesCleanup,
|
||||
"janitor_run_interval": cfg.UserSharingDrivers.SQL.JanitorRunInterval,
|
||||
},
|
||||
"owncloudsql": map[string]interface{}{
|
||||
"gateway_addr": cfg.Reva.Address,
|
||||
"storage_mount_id": cfg.UserSharingDrivers.OwnCloudSQL.UserStorageMountID,
|
||||
"db_username": cfg.UserSharingDrivers.OwnCloudSQL.DBUsername,
|
||||
"db_password": cfg.UserSharingDrivers.OwnCloudSQL.DBPassword,
|
||||
"db_host": cfg.UserSharingDrivers.OwnCloudSQL.DBHost,
|
||||
"db_port": cfg.UserSharingDrivers.OwnCloudSQL.DBPort,
|
||||
"db_name": cfg.UserSharingDrivers.OwnCloudSQL.DBName,
|
||||
},
|
||||
"cs3": map[string]interface{}{
|
||||
"gateway_addr": cfg.UserSharingDrivers.CS3.ProviderAddr,
|
||||
"provider_addr": cfg.UserSharingDrivers.CS3.ProviderAddr,
|
||||
|
||||
@@ -24,7 +24,7 @@ type Config struct {
|
||||
SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"STORAGE_USERS_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token." introductionVersion:"pre5.0"`
|
||||
GracefulShutdownTimeout int `yaml:"graceful_shutdown_timeout" env:"STORAGE_USERS_GRACEFUL_SHUTDOWN_TIMEOUT" desc:"The number of seconds to wait for the 'storage-users' service to shutdown cleanly before exiting with an error that gets logged. Note: This setting is only applicable when running the 'storage-users' service as a standalone service. See the text description for more details." introductionVersion:"pre5.0"`
|
||||
|
||||
Driver string `yaml:"driver" env:"STORAGE_USERS_DRIVER" desc:"The storage driver which should be used by the service. Defaults to 'ocis', Supported values are: 'ocis', 's3ng' and 'owncloudsql' (deprecated). The 'ocis' driver stores all data (blob and meta data) in an POSIX compliant volume. The 's3ng' driver stores metadata in a POSIX compliant volume and uploads blobs to the s3 bucket." introductionVersion:"pre5.0"`
|
||||
Driver string `yaml:"driver" env:"STORAGE_USERS_DRIVER" desc:"The storage driver which should be used by the service. Defaults to 'ocis', Supported values are: 'ocis' and 's3ng'. The 'ocis' driver stores all data (blob and meta data) in an POSIX compliant volume. The 's3ng' driver stores metadata in a POSIX compliant volume and uploads blobs to the s3 bucket." introductionVersion:"pre5.0"`
|
||||
Drivers Drivers `yaml:"drivers"`
|
||||
DataServerURL string `yaml:"data_server_url" env:"STORAGE_USERS_DATA_SERVER_URL" desc:"URL of the data server, needs to be reachable by the data gateway provided by the frontend service or the user if directly exposed." introductionVersion:"pre5.0"`
|
||||
DataGatewayURL string `yaml:"data_gateway_url" env:"STORAGE_USERS_DATA_GATEWAY_URL" desc:"URL of the data gateway server" introductionVersion:"pre5.0"`
|
||||
@@ -100,10 +100,9 @@ type CORS struct {
|
||||
|
||||
// Drivers combine all storage driver configurations
|
||||
type Drivers struct {
|
||||
OCIS OCISDriver `yaml:"ocis"`
|
||||
S3NG S3NGDriver `yaml:"s3ng"`
|
||||
OwnCloudSQL OwnCloudSQLDriver `yaml:"owncloudsql"`
|
||||
Posix PosixDriver `yaml:"posix"`
|
||||
OCIS OCISDriver `yaml:"ocis"`
|
||||
S3NG S3NGDriver `yaml:"s3ng"`
|
||||
Posix PosixDriver `yaml:"posix"`
|
||||
|
||||
S3 S3Driver `yaml:",omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
EOS EOSDriver `yaml:",omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
@@ -176,22 +175,6 @@ type S3NGDriver struct {
|
||||
DisableVersioning bool `yaml:"disable_versioning" env:"OCIS_DISABLE_VERSIONING" desc:"Disables versioning of files. When set to true, new uploads with the same filename will overwrite existing files instead of creating a new version." introductionVersion:"7.0.0"`
|
||||
}
|
||||
|
||||
// OwnCloudSQLDriver is the storage driver configuration when using 'owncloudsql' storage driver
|
||||
type OwnCloudSQLDriver struct {
|
||||
// Root is the absolute path to the location of the data
|
||||
Root string `yaml:"root" env:"STORAGE_USERS_OWNCLOUDSQL_DATADIR" desc:"The directory where the filesystem storage will store SQL migration data. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH/storage/owncloud." introductionVersion:"pre5.0"`
|
||||
// ShareFolder defines the name of the folder jailing all shares
|
||||
ShareFolder string `yaml:"share_folder" env:"STORAGE_USERS_OWNCLOUDSQL_SHARE_FOLDER" desc:"Name of the folder jailing all shares." introductionVersion:"pre5.0"`
|
||||
UserLayout string `yaml:"user_layout" env:"STORAGE_USERS_OWNCLOUDSQL_LAYOUT" desc:"Path layout to use to navigate into a users folder in an owncloud data directory" introductionVersion:"pre5.0"`
|
||||
UploadInfoDir string `yaml:"upload_info_dir" env:"STORAGE_USERS_OWNCLOUDSQL_UPLOADINFO_DIR" desc:"The directory where the filesystem will store uploads temporarily. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH/storage/uploadinfo." introductionVersion:"pre5.0"`
|
||||
DBUsername string `yaml:"db_username" env:"STORAGE_USERS_OWNCLOUDSQL_DB_USERNAME" desc:"Username for the database." introductionVersion:"pre5.0"`
|
||||
DBPassword string `yaml:"db_password" env:"STORAGE_USERS_OWNCLOUDSQL_DB_PASSWORD" desc:"Password for the database." introductionVersion:"pre5.0"`
|
||||
DBHost string `yaml:"db_host" env:"STORAGE_USERS_OWNCLOUDSQL_DB_HOST" desc:"Hostname or IP of the database server." introductionVersion:"pre5.0"`
|
||||
DBPort int `yaml:"db_port" env:"STORAGE_USERS_OWNCLOUDSQL_DB_PORT" desc:"Port that the database server is listening on." introductionVersion:"pre5.0"`
|
||||
DBName string `yaml:"db_name" env:"STORAGE_USERS_OWNCLOUDSQL_DB_NAME" desc:"Name of the database to be used." introductionVersion:"pre5.0"`
|
||||
UsersProviderEndpoint string `yaml:"users_provider_endpoint" env:"STORAGE_USERS_OWNCLOUDSQL_USERS_PROVIDER_ENDPOINT" desc:"Endpoint of the users provider." introductionVersion:"pre5.0"`
|
||||
}
|
||||
|
||||
// PosixDriver is the storage driver configuration when using 'posix' storage driver
|
||||
type PosixDriver struct {
|
||||
// Root is the absolute path to the location of the data
|
||||
|
||||
@@ -94,18 +94,6 @@ func DefaultConfig() *config.Config {
|
||||
GracefulShutdownTimeout: 30,
|
||||
Driver: "ocis",
|
||||
Drivers: config.Drivers{
|
||||
OwnCloudSQL: config.OwnCloudSQLDriver{
|
||||
Root: filepath.Join(defaults.BaseDataPath(), "storage", "owncloud"),
|
||||
ShareFolder: "/Shares",
|
||||
UserLayout: "{{.Username}}",
|
||||
UploadInfoDir: filepath.Join(defaults.BaseDataPath(), "storage", "uploadinfo"),
|
||||
DBUsername: "owncloud",
|
||||
DBPassword: "owncloud",
|
||||
DBHost: "",
|
||||
DBPort: 3306,
|
||||
DBName: "owncloud",
|
||||
UsersProviderEndpoint: "com.owncloud.api.users",
|
||||
},
|
||||
S3NG: config.S3NGDriver{
|
||||
Propagator: "sync",
|
||||
Root: filepath.Join(defaults.BaseDataPath(), "storage", "users"),
|
||||
|
||||
@@ -137,29 +137,6 @@ func LocalHome(cfg *config.Config) map[string]interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// OwnCloudSQL is the config mapping for the OwnCloudSQL storage driver
|
||||
func OwnCloudSQL(cfg *config.Config) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"datadirectory": cfg.Drivers.OwnCloudSQL.Root,
|
||||
"upload_info_dir": cfg.Drivers.OwnCloudSQL.UploadInfoDir,
|
||||
"share_folder": cfg.Drivers.OwnCloudSQL.ShareFolder,
|
||||
"user_layout": cfg.Drivers.OwnCloudSQL.UserLayout,
|
||||
"enable_home": false,
|
||||
"dbusername": cfg.Drivers.OwnCloudSQL.DBUsername,
|
||||
"dbpassword": cfg.Drivers.OwnCloudSQL.DBPassword,
|
||||
"dbhost": cfg.Drivers.OwnCloudSQL.DBHost,
|
||||
"dbport": cfg.Drivers.OwnCloudSQL.DBPort,
|
||||
"dbname": cfg.Drivers.OwnCloudSQL.DBName,
|
||||
"userprovidersvc": cfg.Drivers.OwnCloudSQL.UsersProviderEndpoint,
|
||||
"tokens": map[string]interface{}{
|
||||
"download_endpoint": cfg.DataServerURL,
|
||||
"datagateway_endpoint": cfg.DataGatewayURL,
|
||||
"transfer_shared_secret": cfg.Commons.TransferSecret,
|
||||
"transfer_expires": cfg.TransferExpires,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Ocis is the config mapping for the Ocis storage driver
|
||||
func Ocis(cfg *config.Config) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
|
||||
@@ -5,31 +5,29 @@ import "github.com/owncloud/ocis/v2/services/storage-users/pkg/config"
|
||||
// StorageProviderDrivers are the drivers for the storage provider
|
||||
func StorageProviderDrivers(cfg *config.Config) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"eos": EOS(cfg),
|
||||
"eoshome": EOSHome(cfg),
|
||||
"eosgrpc": EOSGRPC(cfg),
|
||||
"local": Local(cfg),
|
||||
"localhome": LocalHome(cfg),
|
||||
"owncloudsql": OwnCloudSQL(cfg),
|
||||
"ocis": OcisNoEvents(cfg),
|
||||
"s3": S3(cfg),
|
||||
"s3ng": S3NGNoEvents(cfg),
|
||||
"posix": Posix(cfg, true),
|
||||
"eos": EOS(cfg),
|
||||
"eoshome": EOSHome(cfg),
|
||||
"eosgrpc": EOSGRPC(cfg),
|
||||
"local": Local(cfg),
|
||||
"localhome": LocalHome(cfg),
|
||||
"ocis": OcisNoEvents(cfg),
|
||||
"s3": S3(cfg),
|
||||
"s3ng": S3NGNoEvents(cfg),
|
||||
"posix": Posix(cfg, true),
|
||||
}
|
||||
}
|
||||
|
||||
// DataProviderDrivers are the drivers for the storage provider
|
||||
func DataProviderDrivers(cfg *config.Config) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"eos": EOS(cfg),
|
||||
"eoshome": EOSHome(cfg),
|
||||
"eosgrpc": EOSGRPC(cfg),
|
||||
"local": Local(cfg),
|
||||
"localhome": LocalHome(cfg),
|
||||
"owncloudsql": OwnCloudSQL(cfg),
|
||||
"ocis": Ocis(cfg),
|
||||
"s3": S3(cfg),
|
||||
"s3ng": S3NG(cfg),
|
||||
"posix": Posix(cfg, false),
|
||||
"eos": EOS(cfg),
|
||||
"eoshome": EOSHome(cfg),
|
||||
"eosgrpc": EOSGRPC(cfg),
|
||||
"local": Local(cfg),
|
||||
"localhome": LocalHome(cfg),
|
||||
"ocis": Ocis(cfg),
|
||||
"s3": S3(cfg),
|
||||
"s3ng": S3NG(cfg),
|
||||
"posix": Posix(cfg, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type Config struct {
|
||||
|
||||
SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"USERS_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token." introductionVersion:"pre5.0"`
|
||||
|
||||
Driver string `yaml:"driver" env:"USERS_DRIVER" desc:"The driver which should be used by the users service. Supported values are 'ldap' and 'owncloudsql'." introductionVersion:"pre5.0"`
|
||||
Driver string `yaml:"driver" env:"USERS_DRIVER" desc:"The driver which should be used by the users service. Supported value is 'ldap'." introductionVersion:"pre5.0"`
|
||||
Drivers Drivers `yaml:"drivers"`
|
||||
|
||||
Context context.Context `yaml:"-"`
|
||||
@@ -52,8 +52,7 @@ type GRPCConfig struct {
|
||||
}
|
||||
|
||||
type Drivers struct {
|
||||
LDAP LDAPDriver `yaml:"ldap"`
|
||||
OwnCloudSQL OwnCloudSQLDriver `yaml:"owncloudsql"`
|
||||
LDAP LDAPDriver `yaml:"ldap"`
|
||||
|
||||
JSON JSONDriver `yaml:"json,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
REST RESTProvider `yaml:"rest,omitempty"` // not supported by the oCIS product, therefore not part of docs
|
||||
@@ -113,18 +112,6 @@ type LDAPGroupSchema struct {
|
||||
Member string `yaml:"member" env:"OCIS_LDAP_GROUP_SCHEMA_MEMBER;USERS_LDAP_GROUP_SCHEMA_MEMBER" desc:"LDAP Attribute that is used for group members." introductionVersion:"pre5.0"`
|
||||
}
|
||||
|
||||
type OwnCloudSQLDriver struct {
|
||||
DBUsername string `yaml:"db_username" env:"USERS_OWNCLOUDSQL_DB_USERNAME" desc:"Database user to use for authenticating with the owncloud database." introductionVersion:"pre5.0"`
|
||||
DBPassword string `yaml:"db_password" env:"USERS_OWNCLOUDSQL_DB_PASSWORD" desc:"Password for the database user." introductionVersion:"pre5.0"`
|
||||
DBHost string `yaml:"db_host" env:"USERS_OWNCLOUDSQL_DB_HOST" desc:"Hostname of the database server." introductionVersion:"pre5.0"`
|
||||
DBPort int `yaml:"db_port" env:"USERS_OWNCLOUDSQL_DB_PORT" desc:"Network port to use for the database connection." introductionVersion:"pre5.0"`
|
||||
DBName string `yaml:"db_name" env:"USERS_OWNCLOUDSQL_DB_NAME" desc:"Name of the owncloud database." introductionVersion:"pre5.0"`
|
||||
IDP string `yaml:"idp" env:"USERS_OWNCLOUDSQL_IDP" desc:"The identity provider value to set in the userids of the CS3 user objects for users returned by this user provider." introductionVersion:"pre5.0"`
|
||||
Nobody int64 `yaml:"nobody" env:"USERS_OWNCLOUDSQL_NOBODY" desc:"Fallback number if no numeric UID and GID properties are provided." introductionVersion:"pre5.0"`
|
||||
JoinUsername bool `yaml:"join_username" env:"USERS_OWNCLOUDSQL_JOIN_USERNAME" desc:"Join the user properties table to read usernames" introductionVersion:"pre5.0"`
|
||||
JoinOwnCloudUUID bool `yaml:"join_owncloud_uuid" env:"USERS_OWNCLOUDSQL_JOIN_OWNCLOUD_UUID" desc:"Join the user properties table to read user IDs." introductionVersion:"pre5.0"`
|
||||
EnableMedialSearch bool `yaml:"enable_medial_search" env:"USERS_OWNCLOUDSQL_ENABLE_MEDIAL_SEARCH" desc:"Allow 'medial search' when searching for users instead of just doing a prefix search. This allows finding 'Alice' when searching for 'lic'." introductionVersion:"pre5.0"`
|
||||
}
|
||||
type RESTProvider struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
|
||||
@@ -77,18 +77,6 @@ func DefaultConfig() *config.Config {
|
||||
},
|
||||
},
|
||||
JSON: config.JSONDriver{},
|
||||
OwnCloudSQL: config.OwnCloudSQLDriver{
|
||||
DBUsername: "owncloud",
|
||||
DBPassword: "secret",
|
||||
DBHost: "mysql",
|
||||
DBPort: 3306,
|
||||
DBName: "owncloud",
|
||||
IDP: "https://localhost:9200",
|
||||
Nobody: 90,
|
||||
JoinUsername: false,
|
||||
JoinOwnCloudUUID: false,
|
||||
EnableMedialSearch: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,18 +30,6 @@ func UsersConfigFromStruct(cfg *config.Config) map[string]interface{} {
|
||||
"users": cfg.Drivers.JSON.File,
|
||||
},
|
||||
"ldap": ldapConfigFromString(cfg.Drivers.LDAP),
|
||||
"owncloudsql": map[string]interface{}{
|
||||
"dbusername": cfg.Drivers.OwnCloudSQL.DBUsername,
|
||||
"dbpassword": cfg.Drivers.OwnCloudSQL.DBPassword,
|
||||
"dbhost": cfg.Drivers.OwnCloudSQL.DBHost,
|
||||
"dbport": cfg.Drivers.OwnCloudSQL.DBPort,
|
||||
"dbname": cfg.Drivers.OwnCloudSQL.DBName,
|
||||
"idp": cfg.Drivers.OwnCloudSQL.IDP,
|
||||
"nobody": cfg.Drivers.OwnCloudSQL.Nobody,
|
||||
"join_username": cfg.Drivers.OwnCloudSQL.JoinUsername,
|
||||
"join_ownclouduuid": cfg.Drivers.OwnCloudSQL.JoinOwnCloudUUID,
|
||||
"enable_medial_search": cfg.Drivers.OwnCloudSQL.EnableMedialSearch,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
159
vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go
generated
vendored
159
vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go
generated
vendored
@@ -1,159 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package flate
|
||||
|
||||
// Sort sorts data.
|
||||
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
|
||||
// data.Less and data.Swap. The sort is not guaranteed to be stable.
|
||||
func sortByFreq(data []literalNode) {
|
||||
n := len(data)
|
||||
quickSortByFreq(data, 0, n, maxDepth(n))
|
||||
}
|
||||
|
||||
func quickSortByFreq(data []literalNode, a, b, maxDepth int) {
|
||||
for b-a > 12 { // Use ShellSort for slices <= 12 elements
|
||||
if maxDepth == 0 {
|
||||
heapSort(data, a, b)
|
||||
return
|
||||
}
|
||||
maxDepth--
|
||||
mlo, mhi := doPivotByFreq(data, a, b)
|
||||
// Avoiding recursion on the larger subproblem guarantees
|
||||
// a stack depth of at most lg(b-a).
|
||||
if mlo-a < b-mhi {
|
||||
quickSortByFreq(data, a, mlo, maxDepth)
|
||||
a = mhi // i.e., quickSortByFreq(data, mhi, b)
|
||||
} else {
|
||||
quickSortByFreq(data, mhi, b, maxDepth)
|
||||
b = mlo // i.e., quickSortByFreq(data, a, mlo)
|
||||
}
|
||||
}
|
||||
if b-a > 1 {
|
||||
// Do ShellSort pass with gap 6
|
||||
// It could be written in this simplified form cause b-a <= 12
|
||||
for i := a + 6; i < b; i++ {
|
||||
if data[i].freq == data[i-6].freq && data[i].literal < data[i-6].literal || data[i].freq < data[i-6].freq {
|
||||
data[i], data[i-6] = data[i-6], data[i]
|
||||
}
|
||||
}
|
||||
insertionSortByFreq(data, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
func doPivotByFreq(data []literalNode, lo, hi int) (midlo, midhi int) {
|
||||
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
|
||||
if hi-lo > 40 {
|
||||
// Tukey's ``Ninther,'' median of three medians of three.
|
||||
s := (hi - lo) / 8
|
||||
medianOfThreeSortByFreq(data, lo, lo+s, lo+2*s)
|
||||
medianOfThreeSortByFreq(data, m, m-s, m+s)
|
||||
medianOfThreeSortByFreq(data, hi-1, hi-1-s, hi-1-2*s)
|
||||
}
|
||||
medianOfThreeSortByFreq(data, lo, m, hi-1)
|
||||
|
||||
// Invariants are:
|
||||
// data[lo] = pivot (set up by ChoosePivot)
|
||||
// data[lo < i < a] < pivot
|
||||
// data[a <= i < b] <= pivot
|
||||
// data[b <= i < c] unexamined
|
||||
// data[c <= i < hi-1] > pivot
|
||||
// data[hi-1] >= pivot
|
||||
pivot := lo
|
||||
a, c := lo+1, hi-1
|
||||
|
||||
for ; a < c && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ {
|
||||
}
|
||||
b := a
|
||||
for {
|
||||
for ; b < c && (data[pivot].freq == data[b].freq && data[pivot].literal > data[b].literal || data[pivot].freq > data[b].freq); b++ { // data[b] <= pivot
|
||||
}
|
||||
for ; b < c && (data[pivot].freq == data[c-1].freq && data[pivot].literal < data[c-1].literal || data[pivot].freq < data[c-1].freq); c-- { // data[c-1] > pivot
|
||||
}
|
||||
if b >= c {
|
||||
break
|
||||
}
|
||||
// data[b] > pivot; data[c-1] <= pivot
|
||||
data[b], data[c-1] = data[c-1], data[b]
|
||||
b++
|
||||
c--
|
||||
}
|
||||
// If hi-c<3 then there are duplicates (by property of median of nine).
|
||||
// Let's be a bit more conservative, and set border to 5.
|
||||
protect := hi-c < 5
|
||||
if !protect && hi-c < (hi-lo)/4 {
|
||||
// Lets test some points for equality to pivot
|
||||
dups := 0
|
||||
if data[pivot].freq == data[hi-1].freq && data[pivot].literal > data[hi-1].literal || data[pivot].freq > data[hi-1].freq { // data[hi-1] = pivot
|
||||
data[c], data[hi-1] = data[hi-1], data[c]
|
||||
c++
|
||||
dups++
|
||||
}
|
||||
if data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq { // data[b-1] = pivot
|
||||
b--
|
||||
dups++
|
||||
}
|
||||
// m-lo = (hi-lo)/2 > 6
|
||||
// b-lo > (hi-lo)*3/4-1 > 8
|
||||
// ==> m < b ==> data[m] <= pivot
|
||||
if data[m].freq == data[pivot].freq && data[m].literal > data[pivot].literal || data[m].freq > data[pivot].freq { // data[m] = pivot
|
||||
data[m], data[b-1] = data[b-1], data[m]
|
||||
b--
|
||||
dups++
|
||||
}
|
||||
// if at least 2 points are equal to pivot, assume skewed distribution
|
||||
protect = dups > 1
|
||||
}
|
||||
if protect {
|
||||
// Protect against a lot of duplicates
|
||||
// Add invariant:
|
||||
// data[a <= i < b] unexamined
|
||||
// data[b <= i < c] = pivot
|
||||
for {
|
||||
for ; a < b && (data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq); b-- { // data[b] == pivot
|
||||
}
|
||||
for ; a < b && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ { // data[a] < pivot
|
||||
}
|
||||
if a >= b {
|
||||
break
|
||||
}
|
||||
// data[a] == pivot; data[b-1] < pivot
|
||||
data[a], data[b-1] = data[b-1], data[a]
|
||||
a++
|
||||
b--
|
||||
}
|
||||
}
|
||||
// Swap pivot into middle
|
||||
data[pivot], data[b-1] = data[b-1], data[pivot]
|
||||
return b - 1, c
|
||||
}
|
||||
|
||||
// Insertion sort
|
||||
func insertionSortByFreq(data []literalNode, a, b int) {
|
||||
for i := a + 1; i < b; i++ {
|
||||
for j := i; j > a && (data[j].freq == data[j-1].freq && data[j].literal < data[j-1].literal || data[j].freq < data[j-1].freq); j-- {
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// quickSortByFreq, loosely following Bentley and McIlroy,
|
||||
// ``Engineering a Sort Function,'' SP&E November 1993.
|
||||
|
||||
// medianOfThreeSortByFreq moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
|
||||
func medianOfThreeSortByFreq(data []literalNode, m1, m0, m2 int) {
|
||||
// sort 3 elements
|
||||
if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq {
|
||||
data[m1], data[m0] = data[m0], data[m1]
|
||||
}
|
||||
// data[m0] <= data[m1]
|
||||
if data[m2].freq == data[m1].freq && data[m2].literal < data[m1].literal || data[m2].freq < data[m1].freq {
|
||||
data[m2], data[m1] = data[m1], data[m2]
|
||||
// data[m0] <= data[m2] && data[m1] < data[m2]
|
||||
if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq {
|
||||
data[m1], data[m0] = data[m0], data[m1]
|
||||
}
|
||||
}
|
||||
// now data[m0] <= data[m1] <= data[m2]
|
||||
}
|
||||
201
vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go
generated
vendored
201
vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go
generated
vendored
@@ -1,201 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package flate
|
||||
|
||||
// Sort sorts data.
|
||||
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
|
||||
// data.Less and data.Swap. The sort is not guaranteed to be stable.
|
||||
func sortByLiteral(data []literalNode) {
|
||||
n := len(data)
|
||||
quickSort(data, 0, n, maxDepth(n))
|
||||
}
|
||||
|
||||
func quickSort(data []literalNode, a, b, maxDepth int) {
|
||||
for b-a > 12 { // Use ShellSort for slices <= 12 elements
|
||||
if maxDepth == 0 {
|
||||
heapSort(data, a, b)
|
||||
return
|
||||
}
|
||||
maxDepth--
|
||||
mlo, mhi := doPivot(data, a, b)
|
||||
// Avoiding recursion on the larger subproblem guarantees
|
||||
// a stack depth of at most lg(b-a).
|
||||
if mlo-a < b-mhi {
|
||||
quickSort(data, a, mlo, maxDepth)
|
||||
a = mhi // i.e., quickSort(data, mhi, b)
|
||||
} else {
|
||||
quickSort(data, mhi, b, maxDepth)
|
||||
b = mlo // i.e., quickSort(data, a, mlo)
|
||||
}
|
||||
}
|
||||
if b-a > 1 {
|
||||
// Do ShellSort pass with gap 6
|
||||
// It could be written in this simplified form cause b-a <= 12
|
||||
for i := a + 6; i < b; i++ {
|
||||
if data[i].literal < data[i-6].literal {
|
||||
data[i], data[i-6] = data[i-6], data[i]
|
||||
}
|
||||
}
|
||||
insertionSort(data, a, b)
|
||||
}
|
||||
}
|
||||
func heapSort(data []literalNode, a, b int) {
|
||||
first := a
|
||||
lo := 0
|
||||
hi := b - a
|
||||
|
||||
// Build heap with greatest element at top.
|
||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||
siftDown(data, i, hi, first)
|
||||
}
|
||||
|
||||
// Pop elements, largest first, into end of data.
|
||||
for i := hi - 1; i >= 0; i-- {
|
||||
data[first], data[first+i] = data[first+i], data[first]
|
||||
siftDown(data, lo, i, first)
|
||||
}
|
||||
}
|
||||
|
||||
// siftDown implements the heap property on data[lo, hi).
|
||||
// first is an offset into the array where the root of the heap lies.
|
||||
func siftDown(data []literalNode, lo, hi, first int) {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
if child >= hi {
|
||||
break
|
||||
}
|
||||
if child+1 < hi && data[first+child].literal < data[first+child+1].literal {
|
||||
child++
|
||||
}
|
||||
if data[first+root].literal > data[first+child].literal {
|
||||
return
|
||||
}
|
||||
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||
root = child
|
||||
}
|
||||
}
|
||||
func doPivot(data []literalNode, lo, hi int) (midlo, midhi int) {
|
||||
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
|
||||
if hi-lo > 40 {
|
||||
// Tukey's ``Ninther,'' median of three medians of three.
|
||||
s := (hi - lo) / 8
|
||||
medianOfThree(data, lo, lo+s, lo+2*s)
|
||||
medianOfThree(data, m, m-s, m+s)
|
||||
medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
|
||||
}
|
||||
medianOfThree(data, lo, m, hi-1)
|
||||
|
||||
// Invariants are:
|
||||
// data[lo] = pivot (set up by ChoosePivot)
|
||||
// data[lo < i < a] < pivot
|
||||
// data[a <= i < b] <= pivot
|
||||
// data[b <= i < c] unexamined
|
||||
// data[c <= i < hi-1] > pivot
|
||||
// data[hi-1] >= pivot
|
||||
pivot := lo
|
||||
a, c := lo+1, hi-1
|
||||
|
||||
for ; a < c && data[a].literal < data[pivot].literal; a++ {
|
||||
}
|
||||
b := a
|
||||
for {
|
||||
for ; b < c && data[pivot].literal > data[b].literal; b++ { // data[b] <= pivot
|
||||
}
|
||||
for ; b < c && data[pivot].literal < data[c-1].literal; c-- { // data[c-1] > pivot
|
||||
}
|
||||
if b >= c {
|
||||
break
|
||||
}
|
||||
// data[b] > pivot; data[c-1] <= pivot
|
||||
data[b], data[c-1] = data[c-1], data[b]
|
||||
b++
|
||||
c--
|
||||
}
|
||||
// If hi-c<3 then there are duplicates (by property of median of nine).
|
||||
// Let's be a bit more conservative, and set border to 5.
|
||||
protect := hi-c < 5
|
||||
if !protect && hi-c < (hi-lo)/4 {
|
||||
// Lets test some points for equality to pivot
|
||||
dups := 0
|
||||
if data[pivot].literal > data[hi-1].literal { // data[hi-1] = pivot
|
||||
data[c], data[hi-1] = data[hi-1], data[c]
|
||||
c++
|
||||
dups++
|
||||
}
|
||||
if data[b-1].literal > data[pivot].literal { // data[b-1] = pivot
|
||||
b--
|
||||
dups++
|
||||
}
|
||||
// m-lo = (hi-lo)/2 > 6
|
||||
// b-lo > (hi-lo)*3/4-1 > 8
|
||||
// ==> m < b ==> data[m] <= pivot
|
||||
if data[m].literal > data[pivot].literal { // data[m] = pivot
|
||||
data[m], data[b-1] = data[b-1], data[m]
|
||||
b--
|
||||
dups++
|
||||
}
|
||||
// if at least 2 points are equal to pivot, assume skewed distribution
|
||||
protect = dups > 1
|
||||
}
|
||||
if protect {
|
||||
// Protect against a lot of duplicates
|
||||
// Add invariant:
|
||||
// data[a <= i < b] unexamined
|
||||
// data[b <= i < c] = pivot
|
||||
for {
|
||||
for ; a < b && data[b-1].literal > data[pivot].literal; b-- { // data[b] == pivot
|
||||
}
|
||||
for ; a < b && data[a].literal < data[pivot].literal; a++ { // data[a] < pivot
|
||||
}
|
||||
if a >= b {
|
||||
break
|
||||
}
|
||||
// data[a] == pivot; data[b-1] < pivot
|
||||
data[a], data[b-1] = data[b-1], data[a]
|
||||
a++
|
||||
b--
|
||||
}
|
||||
}
|
||||
// Swap pivot into middle
|
||||
data[pivot], data[b-1] = data[b-1], data[pivot]
|
||||
return b - 1, c
|
||||
}
|
||||
|
||||
// Insertion sort
|
||||
func insertionSort(data []literalNode, a, b int) {
|
||||
for i := a + 1; i < b; i++ {
|
||||
for j := i; j > a && data[j].literal < data[j-1].literal; j-- {
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// maxDepth returns a threshold at which quicksort should switch
|
||||
// to heapsort. It returns 2*ceil(lg(n+1)).
|
||||
func maxDepth(n int) int {
|
||||
var depth int
|
||||
for i := n; i > 0; i >>= 1 {
|
||||
depth++
|
||||
}
|
||||
return depth * 2
|
||||
}
|
||||
|
||||
// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
|
||||
func medianOfThree(data []literalNode, m1, m0, m2 int) {
|
||||
// sort 3 elements
|
||||
if data[m1].literal < data[m0].literal {
|
||||
data[m1], data[m0] = data[m0], data[m1]
|
||||
}
|
||||
// data[m0] <= data[m1]
|
||||
if data[m2].literal < data[m1].literal {
|
||||
data[m2], data[m1] = data[m1], data[m2]
|
||||
// data[m0] <= data[m2] && data[m1] < data[m2]
|
||||
if data[m1].literal < data[m0].literal {
|
||||
data[m1], data[m0] = data[m0], data[m1]
|
||||
}
|
||||
}
|
||||
// now data[m0] <= data[m1] <= data[m2]
|
||||
}
|
||||
1
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/loader/loader.go
generated
vendored
1
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/loader/loader.go
generated
vendored
@@ -29,7 +29,6 @@ import (
|
||||
_ "github.com/owncloud/reva/v2/pkg/auth/manager/nextcloud"
|
||||
_ "github.com/owncloud/reva/v2/pkg/auth/manager/ocmshares"
|
||||
_ "github.com/owncloud/reva/v2/pkg/auth/manager/oidc"
|
||||
_ "github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql"
|
||||
_ "github.com/owncloud/reva/v2/pkg/auth/manager/publicshares"
|
||||
_ "github.com/owncloud/reva/v2/pkg/auth/manager/serviceaccounts"
|
||||
// Add your own here
|
||||
|
||||
165
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/accounts/accounts.go
generated
vendored
165
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/accounts/accounts.go
generated
vendored
@@ -1,165 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/owncloud/reva/v2/pkg/appctx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Accounts represents oc10-style Accounts
|
||||
type Accounts struct {
|
||||
driver string
|
||||
db *sql.DB
|
||||
joinUsername, joinUUID, enableMedialSearch bool
|
||||
selectSQL string
|
||||
}
|
||||
|
||||
// NewMysql returns a new accounts instance connecting to a MySQL database
|
||||
func NewMysql(dsn string, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
|
||||
sqldb, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
// FIXME make configurable
|
||||
sqldb.SetConnMaxLifetime(time.Minute * 3)
|
||||
sqldb.SetConnMaxIdleTime(time.Second * 30)
|
||||
sqldb.SetMaxOpenConns(100)
|
||||
sqldb.SetMaxIdleConns(10)
|
||||
|
||||
err = sqldb.Ping()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
return New("mysql", sqldb, joinUsername, joinUUID, enableMedialSearch)
|
||||
}
|
||||
|
||||
// New returns a new accounts instance connecting to the given sql.DB
|
||||
func New(driver string, sqldb *sql.DB, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
|
||||
|
||||
sel := "SELECT id, email, user_id, display_name, quota, last_login, backend, home, state, password"
|
||||
from := `
|
||||
FROM oc_accounts a
|
||||
LEFT JOIN oc_users u
|
||||
ON a.user_id=u.uid
|
||||
`
|
||||
if joinUsername {
|
||||
sel += ", p.configvalue AS username"
|
||||
from += `LEFT JOIN oc_preferences p
|
||||
ON a.user_id=p.userid
|
||||
AND p.appid='core'
|
||||
AND p.configkey='username'`
|
||||
} else {
|
||||
// fallback to user_id as username
|
||||
sel += ", user_id AS username"
|
||||
}
|
||||
if joinUUID {
|
||||
sel += ", p2.configvalue AS ownclouduuid"
|
||||
from += `LEFT JOIN oc_preferences p2
|
||||
ON a.user_id=p2.userid
|
||||
AND p2.appid='core'
|
||||
AND p2.configkey='ownclouduuid'`
|
||||
} else {
|
||||
// fallback to user_id as ownclouduuid
|
||||
sel += ", user_id AS ownclouduuid"
|
||||
}
|
||||
|
||||
return &Accounts{
|
||||
driver: driver,
|
||||
db: sqldb,
|
||||
joinUsername: joinUsername,
|
||||
joinUUID: joinUUID,
|
||||
enableMedialSearch: enableMedialSearch,
|
||||
selectSQL: sel + from,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Account stores information about accounts.
|
||||
type Account struct {
|
||||
ID uint64
|
||||
Email sql.NullString
|
||||
UserID string
|
||||
DisplayName sql.NullString
|
||||
Quota sql.NullString
|
||||
LastLogin int
|
||||
Backend string
|
||||
Home string
|
||||
State int8
|
||||
PasswordHash string // from oc_users
|
||||
Username sql.NullString // optional comes from the oc_preferences
|
||||
OwnCloudUUID sql.NullString // optional comes from the oc_preferences
|
||||
}
|
||||
|
||||
func (as *Accounts) rowToAccount(ctx context.Context, row Scannable) (*Account, error) {
|
||||
a := Account{}
|
||||
if err := row.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.PasswordHash, &a.Username, &a.OwnCloudUUID); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
// Scannable describes the interface providing a Scan method
|
||||
type Scannable interface {
|
||||
Scan(...interface{}) error
|
||||
}
|
||||
|
||||
// GetAccountByLogin fetches an account by mail or username
|
||||
func (as *Accounts) GetAccountByLogin(ctx context.Context, login string) (*Account, error) {
|
||||
var row *sql.Row
|
||||
username := strings.ToLower(login) // usernames are lowercased in owncloud classic
|
||||
if as.joinUsername {
|
||||
row = as.db.QueryRowContext(ctx, as.selectSQL+" WHERE a.email=? OR a.lower_user_id=? OR p.configvalue=?", login, username, login)
|
||||
} else {
|
||||
row = as.db.QueryRowContext(ctx, as.selectSQL+" WHERE a.email=? OR a.lower_user_id=?", login, username)
|
||||
}
|
||||
|
||||
return as.rowToAccount(ctx, row)
|
||||
}
|
||||
|
||||
// GetAccountGroups reads the groups for an account
|
||||
func (as *Accounts) GetAccountGroups(ctx context.Context, uid string) ([]string, error) {
|
||||
rows, err := as.db.QueryContext(ctx, "SELECT gid FROM oc_group_user WHERE uid=?", uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var group string
|
||||
groups := []string{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&group); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
continue
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
BIN
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/accounts/test.sqlite
generated
vendored
BIN
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/accounts/test.sqlite
generated
vendored
Binary file not shown.
192
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/owncloudsql.go
generated
vendored
192
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/owncloudsql.go
generated
vendored
@@ -1,192 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/appctx"
|
||||
"github.com/owncloud/reva/v2/pkg/auth"
|
||||
"github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/accounts"
|
||||
"github.com/owncloud/reva/v2/pkg/auth/manager/registry"
|
||||
"github.com/owncloud/reva/v2/pkg/auth/scope"
|
||||
"github.com/owncloud/reva/v2/pkg/errtypes"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
// Provides mysql drivers
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("owncloudsql", NewMysql)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
c *config
|
||||
db *accounts.Accounts
|
||||
}
|
||||
|
||||
type config struct {
|
||||
DbUsername string `mapstructure:"dbusername"`
|
||||
DbPassword string `mapstructure:"dbpassword"`
|
||||
DbHost string `mapstructure:"dbhost"`
|
||||
DbPort int `mapstructure:"dbport"`
|
||||
DbName string `mapstructure:"dbname"`
|
||||
Idp string `mapstructure:"idp"`
|
||||
Nobody int64 `mapstructure:"nobody"`
|
||||
LegacySalt string `mapstructure:"legacy_salt"`
|
||||
JoinUsername bool `mapstructure:"join_username"`
|
||||
JoinOwnCloudUUID bool `mapstructure:"join_ownclouduuid"`
|
||||
}
|
||||
|
||||
// NewMysql returns a new auth manager connection to an owncloud mysql database
|
||||
func NewMysql(m map[string]interface{}) (auth.Manager, error) {
|
||||
mgr := &manager{}
|
||||
err := mgr.Configure(m)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error creating a new auth manager")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mgr.db, err = accounts.NewMysql(
|
||||
fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", mgr.c.DbUsername, mgr.c.DbPassword, mgr.c.DbHost, mgr.c.DbPort, mgr.c.DbName),
|
||||
mgr.c.JoinUsername,
|
||||
mgr.c.JoinOwnCloudUUID,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
func (m *manager) Configure(ml map[string]interface{}) error {
|
||||
c, err := parseConfig(ml)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Nobody == 0 {
|
||||
c.Nobody = 99
|
||||
}
|
||||
|
||||
m.c = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseConfig(m map[string]interface{}) (*config, error) {
|
||||
c := &config{}
|
||||
if err := mapstructure.Decode(m, &c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *manager) Authenticate(ctx context.Context, login, clientSecret string) (*user.User, map[string]*authpb.Scope, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
// 1. find user by login
|
||||
|
||||
account, err := m.db.GetAccountByLogin(ctx, login)
|
||||
if err != nil {
|
||||
return nil, nil, errtypes.NotFound(login)
|
||||
}
|
||||
// 2. verify the user password
|
||||
if !m.verify(clientSecret, account.PasswordHash) {
|
||||
return nil, nil, errtypes.InvalidCredentials(login)
|
||||
}
|
||||
|
||||
userID := &user.UserId{
|
||||
Idp: m.c.Idp,
|
||||
OpaqueId: account.OwnCloudUUID.String,
|
||||
Type: user.UserType_USER_TYPE_PRIMARY, // TODO: assign the appropriate user type for guest accounts
|
||||
}
|
||||
|
||||
u := &user.User{
|
||||
Id: userID,
|
||||
// TODO add more claims from the StandardClaims, eg EmailVerified and lastlogin
|
||||
Username: account.Username.String,
|
||||
Mail: account.Email.String,
|
||||
DisplayName: account.DisplayName.String,
|
||||
//UidNumber: uidNumber,
|
||||
//GidNumber: gidNumber,
|
||||
}
|
||||
|
||||
if u.Groups, err = m.db.GetAccountGroups(ctx, account.UserID); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var scopes map[string]*authpb.Scope
|
||||
if userID != nil && (userID.Type == user.UserType_USER_TYPE_LIGHTWEIGHT || userID.Type == user.UserType_USER_TYPE_FEDERATED) {
|
||||
scopes, err = scope.AddLightweightAccountScope(authpb.Role_ROLE_OWNER, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
scopes, err = scope.AddOwnerScope(nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
// do not log password hash
|
||||
account.PasswordHash = "***redacted***"
|
||||
log.Debug().Interface("account", account).Interface("user", u).Msg("authenticated user")
|
||||
|
||||
return u, scopes, nil
|
||||
}
|
||||
|
||||
func (m *manager) verify(password, hash string) bool {
|
||||
splitHash := strings.SplitN(hash, "|", 2)
|
||||
switch len(splitHash) {
|
||||
case 2:
|
||||
if splitHash[0] == "1" {
|
||||
return m.verifyHashV1(password, splitHash[1])
|
||||
}
|
||||
case 1:
|
||||
return m.legacyHashVerify(password, hash)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *manager) legacyHashVerify(password, hash string) bool {
|
||||
// TODO rehash $newHash = $this->hash($message);
|
||||
switch len(hash) {
|
||||
case 60: // legacy PHPass hash
|
||||
return nil == bcrypt.CompareHashAndPassword([]byte(hash), []byte(password+m.c.LegacySalt))
|
||||
case 40: // legacy sha1 hash
|
||||
h := sha1.Sum([]byte(password))
|
||||
return hmac.Equal([]byte(hash), []byte(hex.EncodeToString(h[:])))
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (m *manager) verifyHashV1(password, hash string) bool {
|
||||
// TODO implement password_needs_rehash
|
||||
return nil == bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
}
|
||||
1
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/loader/loader.go
generated
vendored
1
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/loader/loader.go
generated
vendored
@@ -23,6 +23,5 @@ import (
|
||||
_ "github.com/owncloud/reva/v2/pkg/publicshare/manager/cs3"
|
||||
_ "github.com/owncloud/reva/v2/pkg/publicshare/manager/json"
|
||||
_ "github.com/owncloud/reva/v2/pkg/publicshare/manager/memory"
|
||||
_ "github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql"
|
||||
// Add your own here
|
||||
)
|
||||
|
||||
226
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql/conversions.go
generated
vendored
226
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql/conversions.go
generated
vendored
@@ -1,226 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/conversions"
|
||||
"github.com/owncloud/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/owncloud/reva/v2/pkg/rgrpc/todo/pool"
|
||||
"github.com/jellydator/ttlcache/v2"
|
||||
)
|
||||
|
||||
// DBShare stores information about user and public shares.
|
||||
type DBShare struct {
|
||||
ID string
|
||||
UIDOwner string
|
||||
UIDInitiator string
|
||||
ItemStorage string
|
||||
FileSource string
|
||||
ItemType string // 'file' or 'folder'
|
||||
ShareWith string
|
||||
Token string
|
||||
Expiration string
|
||||
Permissions int
|
||||
ShareType int
|
||||
ShareName string
|
||||
STime int
|
||||
FileTarget string
|
||||
RejectedBy string
|
||||
State int
|
||||
Parent int
|
||||
}
|
||||
|
||||
// UserConverter describes an interface for converting user ids to names and back
|
||||
type UserConverter interface {
|
||||
UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error)
|
||||
UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error)
|
||||
}
|
||||
|
||||
// GatewayUserConverter converts usernames and ids using the gateway
|
||||
type GatewayUserConverter struct {
|
||||
gwAddr string
|
||||
|
||||
IDCache *ttlcache.Cache
|
||||
NameCache *ttlcache.Cache
|
||||
}
|
||||
|
||||
// NewGatewayUserConverter returns a instance of GatewayUserConverter
|
||||
func NewGatewayUserConverter(gwAddr string) *GatewayUserConverter {
|
||||
IDCache := ttlcache.NewCache()
|
||||
_ = IDCache.SetTTL(30 * time.Second)
|
||||
IDCache.SkipTTLExtensionOnHit(true)
|
||||
NameCache := ttlcache.NewCache()
|
||||
_ = NameCache.SetTTL(30 * time.Second)
|
||||
NameCache.SkipTTLExtensionOnHit(true)
|
||||
|
||||
return &GatewayUserConverter{
|
||||
gwAddr: gwAddr,
|
||||
IDCache: IDCache,
|
||||
NameCache: NameCache,
|
||||
}
|
||||
}
|
||||
|
||||
// UserIDToUserName converts a user ID to an username
|
||||
func (c *GatewayUserConverter) UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) {
|
||||
username, err := c.NameCache.Get(userid.String())
|
||||
if err == nil {
|
||||
return username.(string), nil
|
||||
}
|
||||
|
||||
gwConn, err := pool.GetGatewayServiceClient(c.gwAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
getUserResponse, err := gwConn.GetUser(ctx, &userpb.GetUserRequest{
|
||||
UserId: userid,
|
||||
SkipFetchingUserGroups: true,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if getUserResponse.Status.Code != rpc.Code_CODE_OK {
|
||||
return "", status.NewErrorFromCode(getUserResponse.Status.Code, "gateway")
|
||||
}
|
||||
_ = c.NameCache.Set(userid.String(), getUserResponse.User.Username)
|
||||
return getUserResponse.User.Username, nil
|
||||
}
|
||||
|
||||
// UserNameToUserID converts a username to an user ID
|
||||
func (c *GatewayUserConverter) UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) {
|
||||
id, err := c.IDCache.Get(username)
|
||||
if err == nil {
|
||||
return id.(*userpb.UserId), nil
|
||||
}
|
||||
|
||||
gwConn, err := pool.GetGatewayServiceClient(c.gwAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getUserResponse, err := gwConn.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{
|
||||
Claim: "username",
|
||||
Value: username,
|
||||
SkipFetchingUserGroups: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if getUserResponse.Status.Code != rpc.Code_CODE_OK {
|
||||
return nil, status.NewErrorFromCode(getUserResponse.Status.Code, "gateway")
|
||||
}
|
||||
_ = c.IDCache.Set(username, getUserResponse.User.Id)
|
||||
return getUserResponse.User.Id, nil
|
||||
}
|
||||
|
||||
func resourceTypeToItem(r provider.ResourceType) string {
|
||||
switch r {
|
||||
case provider.ResourceType_RESOURCE_TYPE_FILE:
|
||||
return "file"
|
||||
case provider.ResourceType_RESOURCE_TYPE_CONTAINER:
|
||||
return "folder"
|
||||
case provider.ResourceType_RESOURCE_TYPE_REFERENCE:
|
||||
return "reference"
|
||||
case provider.ResourceType_RESOURCE_TYPE_SYMLINK:
|
||||
return "symlink"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func sharePermToInt(p *provider.ResourcePermissions) int {
|
||||
return int(conversions.RoleFromResourcePermissions(p, true).OCSPermissions())
|
||||
}
|
||||
|
||||
func intTosharePerm(p int) (*provider.ResourcePermissions, error) {
|
||||
perms, err := conversions.NewPermissions(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conversions.RoleFromOCSPermissions(perms, nil).CS3ResourcePermissions(), nil
|
||||
}
|
||||
|
||||
func formatUserID(u *userpb.UserId) string {
|
||||
return u.OpaqueId
|
||||
}
|
||||
|
||||
// ConvertToCS3PublicShare converts a DBShare to a CS3API public share
|
||||
func (m *mgr) ConvertToCS3PublicShare(ctx context.Context, s DBShare) (*link.PublicShare, error) {
|
||||
ts := &typespb.Timestamp{
|
||||
Seconds: uint64(s.STime),
|
||||
}
|
||||
permissions, err := intTosharePerm(s.Permissions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
owner, err := m.userConverter.UserNameToUserID(ctx, s.UIDOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var creator *userpb.UserId
|
||||
if s.UIDOwner == s.UIDInitiator {
|
||||
creator = owner
|
||||
} else {
|
||||
creator, err = m.userConverter.UserNameToUserID(ctx, s.UIDOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
pwd := false
|
||||
if s.ShareWith != "" {
|
||||
pwd = true
|
||||
}
|
||||
var expires *typespb.Timestamp
|
||||
if s.Expiration != "" {
|
||||
t, err := time.Parse("2006-01-02 15:04:05", s.Expiration)
|
||||
if err != nil {
|
||||
t, err = time.Parse("2006-01-02 15:04:05-07:00", s.Expiration)
|
||||
}
|
||||
if err == nil {
|
||||
expires = &typespb.Timestamp{
|
||||
Seconds: uint64(t.Unix()),
|
||||
}
|
||||
}
|
||||
}
|
||||
return &link.PublicShare{
|
||||
Id: &link.PublicShareId{
|
||||
OpaqueId: s.ID,
|
||||
},
|
||||
ResourceId: &provider.ResourceId{
|
||||
SpaceId: s.ItemStorage,
|
||||
OpaqueId: s.FileSource,
|
||||
},
|
||||
Permissions: &link.PublicSharePermissions{Permissions: permissions},
|
||||
Owner: owner,
|
||||
Creator: creator,
|
||||
Token: s.Token,
|
||||
DisplayName: s.ShareName,
|
||||
PasswordProtected: pwd,
|
||||
Expiration: expires,
|
||||
Ctime: ts,
|
||||
Mtime: ts,
|
||||
}, nil
|
||||
}
|
||||
533
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql/owncloudsql.go
generated
vendored
533
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql/owncloudsql.go
generated
vendored
@@ -1,533 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
// Package owncloudsql implements a publiclink share manager backed by an existing ownCloud 10 database
|
||||
//
|
||||
// The SQL queries use `coalesce({column_identifier}, ”) as {column_identifier}` to read an emptystring
|
||||
// instead of null values, which better fits the golang default values.
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/errtypes"
|
||||
"github.com/owncloud/reva/v2/pkg/publicshare"
|
||||
"github.com/owncloud/reva/v2/pkg/publicshare/manager/registry"
|
||||
"github.com/owncloud/reva/v2/pkg/sharedconf"
|
||||
"github.com/owncloud/reva/v2/pkg/utils"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
// Provides mysql drivers
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
const (
|
||||
publicShareType = 3
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("owncloudsql", NewMysql)
|
||||
}
|
||||
|
||||
// Config configures an owncloudsql publicshare manager
|
||||
type Config struct {
|
||||
GatewayAddr string `mapstructure:"gateway_addr"`
|
||||
DbUsername string `mapstructure:"db_username"`
|
||||
DbPassword string `mapstructure:"db_password"`
|
||||
DbHost string `mapstructure:"db_host"`
|
||||
DbPort int `mapstructure:"db_port"`
|
||||
DbName string `mapstructure:"db_name"`
|
||||
EnableExpiredSharesCleanup bool `mapstructure:"enable_expired_shares_cleanup"`
|
||||
SharePasswordHashCost int `mapstructure:"password_hash_cost"`
|
||||
}
|
||||
|
||||
type mgr struct {
|
||||
driver string
|
||||
db *sql.DB
|
||||
c Config
|
||||
userConverter UserConverter
|
||||
}
|
||||
|
||||
// NewMysql returns a new publicshare manager connection to a mysql database
|
||||
func NewMysql(m map[string]interface{}) (publicshare.Manager, error) {
|
||||
c, err := parseConfig(m)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error creating a new manager")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", c.DbUsername, c.DbPassword, c.DbHost, c.DbPort, c.DbName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userConverter := NewGatewayUserConverter(sharedconf.GetGatewaySVC(c.GatewayAddr))
|
||||
|
||||
return New("mysql", db, *c, userConverter)
|
||||
}
|
||||
|
||||
// New returns a new Cache instance connecting to the given sql.DB
|
||||
func New(driver string, db *sql.DB, c Config, userConverter UserConverter) (publicshare.Manager, error) {
|
||||
if c.SharePasswordHashCost == 0 {
|
||||
c.SharePasswordHashCost = bcrypt.DefaultCost
|
||||
}
|
||||
return &mgr{
|
||||
driver: driver,
|
||||
db: db,
|
||||
c: c,
|
||||
userConverter: userConverter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseConfig(m map[string]interface{}) (*Config, error) {
|
||||
c := &Config{}
|
||||
if err := mapstructure.Decode(m, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *mgr) CreatePublicShare(ctx context.Context, u *user.User, rInfo *provider.ResourceInfo, g *link.Grant) (*link.PublicShare, error) {
|
||||
|
||||
tkn := utils.RandString(15)
|
||||
now := time.Now().Unix()
|
||||
|
||||
displayName := tkn
|
||||
if rInfo.ArbitraryMetadata != nil && rInfo.ArbitraryMetadata.Metadata["name"] != "" {
|
||||
displayName = rInfo.ArbitraryMetadata.Metadata["name"]
|
||||
}
|
||||
createdAt := &typespb.Timestamp{
|
||||
Seconds: uint64(now),
|
||||
}
|
||||
|
||||
creator := u.Username
|
||||
owner, err := m.userConverter.UserIDToUserName(ctx, rInfo.Owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
permissions := sharePermToInt(g.Permissions.Permissions)
|
||||
itemType := resourceTypeToItem(rInfo.Type)
|
||||
|
||||
itemSource := rInfo.Id.OpaqueId
|
||||
fileSource, err := strconv.ParseUint(itemSource, 10, 64)
|
||||
if err != nil {
|
||||
// it can be the case that the item source may be a character string
|
||||
// we leave fileSource blank in that case
|
||||
fileSource = 0
|
||||
}
|
||||
|
||||
columns := "share_type,uid_owner,uid_initiator,item_type,item_source,file_source,permissions,stime,token,share_name"
|
||||
placeholders := "?,?,?,?,?,?,?,?,?,?"
|
||||
params := []interface{}{publicShareType, owner, creator, itemType, itemSource, fileSource, permissions, now, tkn, displayName}
|
||||
|
||||
var passwordProtected bool
|
||||
password := g.Password
|
||||
if password != "" {
|
||||
password, err = hashPassword(password, m.c.SharePasswordHashCost)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash share password")
|
||||
}
|
||||
passwordProtected = true
|
||||
|
||||
columns += ",share_with"
|
||||
placeholders += ",?"
|
||||
params = append(params, password)
|
||||
}
|
||||
|
||||
if g.Expiration != nil && g.Expiration.Seconds != 0 {
|
||||
t := time.Unix(int64(g.Expiration.Seconds), 0)
|
||||
columns += ",expiration"
|
||||
placeholders += ",?"
|
||||
params = append(params, t)
|
||||
}
|
||||
|
||||
query := "INSERT INTO oc_share (" + columns + ") VALUES (" + placeholders + ")"
|
||||
stmt, err := m.db.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := stmt.Exec(params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &link.PublicShare{
|
||||
Id: &link.PublicShareId{
|
||||
OpaqueId: strconv.FormatInt(lastID, 10),
|
||||
},
|
||||
Owner: rInfo.GetOwner(),
|
||||
Creator: u.Id,
|
||||
ResourceId: rInfo.Id,
|
||||
Token: tkn,
|
||||
Permissions: g.Permissions,
|
||||
Ctime: createdAt,
|
||||
Mtime: createdAt,
|
||||
PasswordProtected: passwordProtected,
|
||||
Expiration: g.Expiration,
|
||||
DisplayName: displayName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// owncloud 10 prefixes the hash with `1|`
|
||||
func hashPassword(password string, cost int) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), cost)
|
||||
return "1|" + string(bytes), err
|
||||
}
|
||||
|
||||
// UpdatePublicShare updates the expiration date, permissions and Mtime
|
||||
func (m *mgr) UpdatePublicShare(ctx context.Context, u *user.User, req *link.UpdatePublicShareRequest) (*link.PublicShare, error) {
|
||||
query := "update oc_share set "
|
||||
paramsMap := map[string]interface{}{}
|
||||
params := []interface{}{}
|
||||
|
||||
now := time.Now().Unix()
|
||||
uid := u.Username
|
||||
|
||||
switch req.GetUpdate().GetType() {
|
||||
case link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME:
|
||||
paramsMap["share_name"] = req.Update.GetDisplayName()
|
||||
case link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS:
|
||||
paramsMap["permissions"] = sharePermToInt(req.Update.GetGrant().GetPermissions().Permissions)
|
||||
case link.UpdatePublicShareRequest_Update_TYPE_EXPIRATION:
|
||||
paramsMap["expiration"] = time.Unix(int64(req.Update.GetGrant().Expiration.Seconds), 0)
|
||||
case link.UpdatePublicShareRequest_Update_TYPE_PASSWORD:
|
||||
if req.Update.GetGrant().Password == "" {
|
||||
paramsMap["share_with"] = ""
|
||||
} else {
|
||||
h, err := hashPassword(req.Update.GetGrant().Password, m.c.SharePasswordHashCost)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash share password")
|
||||
}
|
||||
paramsMap["share_with"] = h
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid update type: %v", req.GetUpdate().GetType())
|
||||
}
|
||||
|
||||
for k, v := range paramsMap {
|
||||
query += k + "=?"
|
||||
params = append(params, v)
|
||||
}
|
||||
|
||||
switch {
|
||||
case req.Ref.GetId() != nil:
|
||||
query += ",stime=? where id=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, now, req.Ref.GetId().OpaqueId, uid, uid)
|
||||
case req.Ref.GetToken() != "":
|
||||
query += ",stime=? where token=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, now, req.Ref.GetToken(), uid, uid)
|
||||
default:
|
||||
return nil, errtypes.NotFound(req.Ref.String())
|
||||
}
|
||||
|
||||
stmt, err := m.db.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = stmt.Exec(params...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.GetPublicShare(ctx, u, req.Ref, false)
|
||||
}
|
||||
|
||||
func (m *mgr) GetPublicShare(ctx context.Context, u *user.User, ref *link.PublicShareReference, sign bool) (share *link.PublicShare, err error) {
|
||||
|
||||
ps, err := m.getWithPassword(ctx, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if publicshare.IsExpired(&ps.PublicShare) {
|
||||
if err := m.cleanupExpiredShares(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errtypes.NotFound("public share has expired")
|
||||
}
|
||||
|
||||
if ps.PublicShare.PasswordProtected && sign {
|
||||
err = publicshare.AddSignature(&ps.PublicShare, ps.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &ps.PublicShare, nil
|
||||
}
|
||||
|
||||
func (m *mgr) getWithPassword(ctx context.Context, ref *link.PublicShareReference) (*publicshare.WithPassword, error) {
|
||||
switch {
|
||||
case ref.GetToken() != "":
|
||||
return m.getByToken(ctx, ref.GetToken())
|
||||
case ref.GetId().GetOpaqueId() != "":
|
||||
return m.getByID(ctx, ref.GetId().GetOpaqueId())
|
||||
default:
|
||||
return nil, errtypes.BadRequest("neither id nor token given")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mgr) getByToken(ctx context.Context, token string) (*publicshare.WithPassword, error) {
|
||||
s, err := getByToken(m.db, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ps, err := m.ConvertToCS3PublicShare(ctx, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &publicshare.WithPassword{
|
||||
Password: strings.TrimPrefix(s.ShareWith, "1|"),
|
||||
}
|
||||
proto.Merge(&ret.PublicShare, ps)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getByToken(db *sql.DB, token string) (DBShare, error) {
|
||||
s := DBShare{Token: token}
|
||||
query := `SELECT
|
||||
coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator,
|
||||
coalesce(share_with, '') as share_with, coalesce(file_source, '') as file_source,
|
||||
coalesce(item_type, '') as item_type,
|
||||
coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name,
|
||||
s.id, s.stime, s.permissions, fc.storage as storage
|
||||
FROM oc_share s
|
||||
LEFT JOIN oc_filecache fc ON fc.fileid = file_source
|
||||
WHERE share_type=? AND token=?`
|
||||
if err := db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.ItemType, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions, &s.ItemStorage); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return s, errtypes.NotFound(token)
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (m *mgr) getByID(ctx context.Context, id string) (*publicshare.WithPassword, error) {
|
||||
s := DBShare{ID: id}
|
||||
query := `SELECT
|
||||
coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator,
|
||||
coalesce(share_with, '') as share_with, coalesce(file_source, '') as file_source,
|
||||
coalesce(item_type, '') as item_type, coalesce(token,'') as token,
|
||||
coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name,
|
||||
s.stime, s.permissions, fc.storage as storage
|
||||
FROM oc_share s
|
||||
LEFT JOIN oc_filecache fc ON fc.fileid = file_source
|
||||
WHERE share_type=? AND id=?`
|
||||
if err := m.db.QueryRow(query, publicShareType, id).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.ItemType, &s.Token, &s.Expiration, &s.ShareName, &s.STime, &s.Permissions, &s.ItemStorage); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(id)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
ps, err := m.ConvertToCS3PublicShare(ctx, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &publicshare.WithPassword{
|
||||
Password: strings.TrimPrefix(s.ShareWith, "1|"),
|
||||
}
|
||||
proto.Merge(&ret.PublicShare, ps)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (m *mgr) ListPublicShares(ctx context.Context, u *user.User, filters []*link.ListPublicSharesRequest_Filter, sign bool) ([]*link.PublicShare, error) {
|
||||
uid := u.Username
|
||||
// FIXME instead of joining we may want to have to do a stat call ... if we want to store shares from other providers? or just Dump()? and be done with migration?
|
||||
query := `SELECT
|
||||
coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator,
|
||||
coalesce(share_with, '') as share_with, coalesce(file_source, '') as file_source,
|
||||
coalesce(item_type, '') as item_type, coalesce(token,'') as token,
|
||||
coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name,
|
||||
s.id, s.stime, s.permissions, fc.storage as storage
|
||||
FROM oc_share s
|
||||
LEFT JOIN oc_filecache fc ON fc.fileid = file_source
|
||||
WHERE (uid_owner=? or uid_initiator=?)
|
||||
AND (share_type=?)`
|
||||
var resourceFilters, ownerFilters, creatorFilters, storageFilters string
|
||||
var resourceParams, ownerParams, creatorParams, storageParams []interface{}
|
||||
params := []interface{}{uid, uid, publicShareType}
|
||||
|
||||
for _, f := range filters {
|
||||
switch f.Type {
|
||||
case link.ListPublicSharesRequest_Filter_TYPE_RESOURCE_ID:
|
||||
if len(resourceFilters) != 0 {
|
||||
resourceFilters += " OR "
|
||||
}
|
||||
resourceFilters += "item_source=?"
|
||||
resourceParams = append(resourceParams, f.GetResourceId().GetOpaqueId())
|
||||
case link.ListPublicSharesRequest_Filter_TYPE_OWNER:
|
||||
if len(ownerFilters) != 0 {
|
||||
ownerFilters += " OR "
|
||||
}
|
||||
ownerFilters += "(uid_owner=?)"
|
||||
ownerParams = append(ownerParams, formatUserID(f.GetOwner()))
|
||||
case link.ListPublicSharesRequest_Filter_TYPE_CREATOR:
|
||||
if len(creatorFilters) != 0 {
|
||||
creatorFilters += " OR "
|
||||
}
|
||||
creatorFilters += "(uid_initiator=?)"
|
||||
creatorParams = append(creatorParams, formatUserID(f.GetCreator()))
|
||||
case publicshare.StorageIDFilterType:
|
||||
if len(storageFilters) != 0 {
|
||||
storageFilters += " OR "
|
||||
}
|
||||
storageFilters += "(storage=?)"
|
||||
storageParams = append(storageParams, f.GetResourceId().GetStorageId())
|
||||
}
|
||||
}
|
||||
if resourceFilters != "" {
|
||||
query = fmt.Sprintf("%s AND (%s)", query, resourceFilters)
|
||||
params = append(params, resourceParams...)
|
||||
}
|
||||
if ownerFilters != "" {
|
||||
query = fmt.Sprintf("%s AND (%s)", query, ownerFilters)
|
||||
params = append(params, ownerParams...)
|
||||
}
|
||||
if creatorFilters != "" {
|
||||
query = fmt.Sprintf("%s AND (%s)", query, creatorFilters)
|
||||
params = append(params, creatorParams...)
|
||||
}
|
||||
if storageFilters != "" {
|
||||
query = fmt.Sprintf("%s AND (%s)", query, storageFilters)
|
||||
params = append(params, storageParams...)
|
||||
}
|
||||
|
||||
rows, err := m.db.Query(query, params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var s DBShare
|
||||
shares := []*link.PublicShare{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.ItemType, &s.Token, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions, &s.ItemStorage); err != nil {
|
||||
continue
|
||||
}
|
||||
var cs3Share *link.PublicShare
|
||||
if cs3Share, err = m.ConvertToCS3PublicShare(ctx, s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if publicshare.IsExpired(cs3Share) {
|
||||
_ = m.cleanupExpiredShares()
|
||||
} else {
|
||||
if cs3Share.PasswordProtected && sign {
|
||||
if err := publicshare.AddSignature(cs3Share, strings.TrimPrefix(s.ShareWith, "1|")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
shares = append(shares, cs3Share)
|
||||
}
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return shares, nil
|
||||
}
|
||||
|
||||
func (m *mgr) RevokePublicShare(ctx context.Context, u *user.User, ref *link.PublicShareReference) error {
|
||||
uid := u.Username
|
||||
query := "delete from oc_share where "
|
||||
params := []interface{}{}
|
||||
|
||||
switch {
|
||||
case ref.GetId() != nil && ref.GetId().OpaqueId != "":
|
||||
query += "id=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, ref.GetId().OpaqueId, uid, uid)
|
||||
case ref.GetToken() != "":
|
||||
query += "token=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, ref.GetToken(), uid, uid)
|
||||
default:
|
||||
return errtypes.NotFound(ref.String())
|
||||
}
|
||||
|
||||
stmt, err := m.db.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := stmt.Exec(params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowCnt, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowCnt == 0 {
|
||||
return errtypes.NotFound(ref.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mgr) GetPublicShareByToken(ctx context.Context, token string, auth *link.PublicShareAuthentication, sign bool) (*link.PublicShare, error) {
|
||||
ps, err := m.getByToken(ctx, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if publicshare.IsExpired(&ps.PublicShare) {
|
||||
if err := m.cleanupExpiredShares(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errtypes.NotFound("public share has expired")
|
||||
}
|
||||
|
||||
if ps.PublicShare.PasswordProtected {
|
||||
if !publicshare.Authenticate(&ps.PublicShare, ps.Password, auth) {
|
||||
return nil, errtypes.InvalidCredentials("access denied")
|
||||
}
|
||||
}
|
||||
|
||||
return &ps.PublicShare, nil
|
||||
}
|
||||
|
||||
func (m *mgr) cleanupExpiredShares() error {
|
||||
if !m.c.EnableExpiredSharesCleanup {
|
||||
return nil
|
||||
}
|
||||
|
||||
query := "DELETE FROM oc_share WHERE expiration IS NOT NULL AND expiration < ?"
|
||||
params := []interface{}{time.Now().Format("2006-01-02 03:04:05")}
|
||||
|
||||
stmt, err := m.db.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = stmt.Exec(params...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
BIN
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql/test.db
generated
vendored
BIN
vendor/github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql/test.db
generated
vendored
Binary file not shown.
120
vendor/github.com/owncloud/reva/v2/pkg/rgrpc/todo/pool/selector.go
generated
vendored
120
vendor/github.com/owncloud/reva/v2/pkg/rgrpc/todo/pool/selector.go
generated
vendored
@@ -20,6 +20,7 @@ package pool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -45,7 +46,9 @@ import (
|
||||
"github.com/owncloud/reva/v2/pkg/registry"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sercand/kuberesolver/v5"
|
||||
mRegistry "go-micro.dev/v4/registry"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -82,6 +85,27 @@ func GetSelector[T any](k string, id string, f func(cc grpc.ClientConnInterface)
|
||||
return newSelector
|
||||
}
|
||||
|
||||
// cachedClient pairs a grpc client with the connection backing it, so Next
|
||||
// can check the connection's health before handing the client back out.
|
||||
type cachedClient[T any] struct {
|
||||
conn *grpc.ClientConn
|
||||
client T
|
||||
}
|
||||
|
||||
// healthy reports whether the cached connection is safe to hand out again.
|
||||
// A connection only becomes TransientFailure or Shutdown after a real RPC on
|
||||
// it has already failed - a connection that was never used for an RPC stays
|
||||
// Idle, so it is treated as healthy here (there is no way to probe
|
||||
// reachability without using it).
|
||||
func (c cachedClient[T]) healthy() bool {
|
||||
switch c.conn.GetState() {
|
||||
case connectivity.TransientFailure, connectivity.Connecting, connectivity.Shutdown:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
type Selector[T any] struct {
|
||||
id string
|
||||
clientFactory func(cc grpc.ClientConnInterface) T
|
||||
@@ -101,47 +125,99 @@ func (s *Selector[T]) Next(opts ...Option) (T, error) {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
target := s.id
|
||||
// if the target is given as a recognized gRPC URI, skip registry lookup
|
||||
// see https://github.com/grpc/grpc/blob/master/doc/naming.md#name-syntax
|
||||
prefix := strings.SplitN(s.id, ":", 2)[0]
|
||||
switch {
|
||||
case prefix == "dns":
|
||||
fallthrough
|
||||
case prefix == "unix":
|
||||
fallthrough
|
||||
case prefix == "kubernetes":
|
||||
// use target as is and skip registry lookup
|
||||
case prefix == "dns", prefix == "unix", prefix == "kubernetes":
|
||||
// use the id as is and skip registry lookup
|
||||
return s.loadOrNewClient(s.id, allOpts)
|
||||
case options.registry != nil:
|
||||
// use service registry to look up address
|
||||
// use the service registry to look up every known node, and pick
|
||||
// the first one that is ready
|
||||
services, err := options.registry.GetService(s.id)
|
||||
if err != nil {
|
||||
return *new(T), fmt.Errorf("%s: %w", s.id, err)
|
||||
}
|
||||
|
||||
nodeAddress, err := registry.GetNodeAddress(services)
|
||||
if err != nil {
|
||||
return *new(T), fmt.Errorf("%s: %w", s.id, err)
|
||||
}
|
||||
target = nodeAddress
|
||||
targets := shuffledTargets(services)
|
||||
return s.nextHealthy(targets, allOpts)
|
||||
default:
|
||||
// if no registry is available, use the target as is
|
||||
// if no registry is available, use the id as is
|
||||
return s.loadOrNewClient(s.id, allOpts)
|
||||
}
|
||||
}
|
||||
|
||||
// nextHealthy walks targets in the given order. It returns the client for
|
||||
// the first target that is ready. A target with no cache entry is also a
|
||||
// candidate, since Next has no way to know its state without dialing it. If
|
||||
// no target is ready, nextHealthy reuses the first one found, so the caller
|
||||
// gets a real error from a real attempt instead of a fabricated one.
|
||||
func (s *Selector[T]) nextHealthy(targets []string, opts []Option) (T, error) {
|
||||
if len(targets) == 0 {
|
||||
return *new(T), fmt.Errorf("%s: no nodes found in registry", s.id)
|
||||
}
|
||||
var fallback *cachedClient[T]
|
||||
for _, target := range targets {
|
||||
cached, ok := s.clientMap.Load(target)
|
||||
if !ok {
|
||||
return s.newClient(target, opts)
|
||||
}
|
||||
|
||||
cc := cached.(cachedClient[T])
|
||||
if cc.healthy() {
|
||||
return cc.client, nil
|
||||
}
|
||||
if fallback == nil {
|
||||
fallback = &cc
|
||||
}
|
||||
}
|
||||
|
||||
existingClient, ok := s.clientMap.Load(target)
|
||||
if ok {
|
||||
return existingClient.(T), nil
|
||||
}
|
||||
return fallback.client, nil
|
||||
}
|
||||
|
||||
conn, err := NewConn(target, allOpts...)
|
||||
// loadOrNewClient returns the cached client for target, creating a new one if
|
||||
// none exists yet. It does not check health: target was not looked up
|
||||
// through the registry, so there is no alternative node to fall back to.
|
||||
func (s *Selector[T]) loadOrNewClient(target string, opts []Option) (T, error) {
|
||||
if cached, ok := s.clientMap.Load(target); ok {
|
||||
return cached.(cachedClient[T]).client, nil
|
||||
}
|
||||
return s.newClient(target, opts)
|
||||
}
|
||||
|
||||
// newClient creates a new connection to target, wraps it in a client and caches
|
||||
// both under target.
|
||||
func (s *Selector[T]) newClient(target string, opts []Option) (T, error) {
|
||||
conn, err := NewConn(target, opts...)
|
||||
if err != nil {
|
||||
return *new(T), errors.Wrap(err, fmt.Sprintf("could not create connection for %s to %s", s.id, target))
|
||||
}
|
||||
|
||||
newClient := s.clientFactory(conn)
|
||||
s.clientMap.Store(target, newClient)
|
||||
newClient := cachedClient[T]{conn: conn, client: s.clientFactory(conn)}
|
||||
// if another goroutine already created a client for this target, use it instead
|
||||
if existing, loaded := s.clientMap.LoadOrStore(target, newClient); loaded {
|
||||
_ = conn.Close()
|
||||
return existing.(cachedClient[T]).client, nil
|
||||
}
|
||||
|
||||
return newClient, nil
|
||||
return newClient.client, nil
|
||||
}
|
||||
|
||||
// shuffledTargets flattens every node address out of services and returns
|
||||
// them in random order, so repeated calls spread load across all known
|
||||
// nodes instead of always preferring the same one.
|
||||
func shuffledTargets(services []*mRegistry.Service) []string {
|
||||
var targets []string
|
||||
for _, svc := range services {
|
||||
for _, node := range svc.Nodes {
|
||||
targets = append(targets, node.Address)
|
||||
}
|
||||
}
|
||||
rand.Shuffle(len(targets), func(i, j int) {
|
||||
targets[i], targets[j] = targets[j], targets[i]
|
||||
})
|
||||
return targets
|
||||
}
|
||||
|
||||
// GatewaySelector returns a Selector[gateway.GatewayAPIClient].
|
||||
|
||||
1
vendor/github.com/owncloud/reva/v2/pkg/share/manager/loader/loader.go
generated
vendored
1
vendor/github.com/owncloud/reva/v2/pkg/share/manager/loader/loader.go
generated
vendored
@@ -24,6 +24,5 @@ import (
|
||||
_ "github.com/owncloud/reva/v2/pkg/share/manager/json"
|
||||
_ "github.com/owncloud/reva/v2/pkg/share/manager/jsoncs3"
|
||||
_ "github.com/owncloud/reva/v2/pkg/share/manager/memory"
|
||||
_ "github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql"
|
||||
// Add your own here
|
||||
)
|
||||
|
||||
301
vendor/github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql/conversions.go
generated
vendored
301
vendor/github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql/conversions.go
generated
vendored
@@ -1,301 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
userprovider "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/conversions"
|
||||
"github.com/owncloud/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/owncloud/reva/v2/pkg/rgrpc/todo/pool"
|
||||
"github.com/owncloud/reva/v2/pkg/utils"
|
||||
"github.com/jellydator/ttlcache/v2"
|
||||
)
|
||||
|
||||
// DBShare stores information about user and public shares.
|
||||
type DBShare struct {
|
||||
ID string
|
||||
UIDOwner string
|
||||
UIDInitiator string
|
||||
ItemStorage string
|
||||
FileSource string
|
||||
ShareWith string
|
||||
Token string
|
||||
Expiration string
|
||||
Permissions int
|
||||
ShareType int
|
||||
ShareName string
|
||||
STime int
|
||||
FileTarget string
|
||||
RejectedBy string
|
||||
State int
|
||||
Parent int
|
||||
}
|
||||
|
||||
// UserConverter describes an interface for converting user ids to names and back
|
||||
type UserConverter interface {
|
||||
UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error)
|
||||
UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error)
|
||||
GetUser(userid *userpb.UserId) (*userpb.User, error)
|
||||
}
|
||||
|
||||
// GatewayUserConverter converts usernames and ids using the gateway
|
||||
type GatewayUserConverter struct {
|
||||
gwAddr string
|
||||
|
||||
IDCache *ttlcache.Cache
|
||||
NameCache *ttlcache.Cache
|
||||
}
|
||||
|
||||
// NewGatewayUserConverter returns a instance of GatewayUserConverter
|
||||
func NewGatewayUserConverter(gwAddr string) *GatewayUserConverter {
|
||||
IDCache := ttlcache.NewCache()
|
||||
_ = IDCache.SetTTL(30 * time.Second)
|
||||
IDCache.SkipTTLExtensionOnHit(true)
|
||||
NameCache := ttlcache.NewCache()
|
||||
_ = NameCache.SetTTL(30 * time.Second)
|
||||
NameCache.SkipTTLExtensionOnHit(true)
|
||||
|
||||
return &GatewayUserConverter{
|
||||
gwAddr: gwAddr,
|
||||
IDCache: IDCache,
|
||||
NameCache: NameCache,
|
||||
}
|
||||
}
|
||||
|
||||
// UserIDToUserName converts a user ID to an username
|
||||
func (c *GatewayUserConverter) UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) {
|
||||
username, err := c.NameCache.Get(userid.String())
|
||||
if err == nil {
|
||||
return username.(string), nil
|
||||
}
|
||||
|
||||
gwConn, err := pool.GetGatewayServiceClient(c.gwAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
getUserResponse, err := gwConn.GetUser(ctx, &userprovider.GetUserRequest{
|
||||
UserId: userid,
|
||||
SkipFetchingUserGroups: true,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if getUserResponse.Status.Code != rpc.Code_CODE_OK {
|
||||
return "", status.NewErrorFromCode(getUserResponse.Status.Code, "gateway")
|
||||
}
|
||||
_ = c.NameCache.Set(userid.String(), getUserResponse.User.Username)
|
||||
return getUserResponse.User.Username, nil
|
||||
}
|
||||
|
||||
// UserNameToUserID converts a username to an user ID
|
||||
func (c *GatewayUserConverter) UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) {
|
||||
id, err := c.IDCache.Get(username)
|
||||
if err == nil {
|
||||
return id.(*userpb.UserId), nil
|
||||
}
|
||||
|
||||
gwConn, err := pool.GetGatewayServiceClient(c.gwAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getUserResponse, err := gwConn.GetUserByClaim(ctx, &userpb.GetUserByClaimRequest{
|
||||
Claim: "username",
|
||||
Value: username,
|
||||
SkipFetchingUserGroups: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if getUserResponse.Status.Code != rpc.Code_CODE_OK {
|
||||
return nil, status.NewErrorFromCode(getUserResponse.Status.Code, "gateway")
|
||||
}
|
||||
_ = c.IDCache.Set(username, getUserResponse.User.Id)
|
||||
return getUserResponse.User.Id, nil
|
||||
}
|
||||
|
||||
// GetUser gets the user
|
||||
func (c *GatewayUserConverter) GetUser(userid *userpb.UserId) (*userpb.User, error) {
|
||||
gwc, err := pool.GetGatewayServiceClient(c.gwAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utils.GetUser(userid, gwc)
|
||||
}
|
||||
|
||||
func (m *mgr) formatGrantee(ctx context.Context, g *provider.Grantee) (int, string, error) {
|
||||
var granteeType int
|
||||
var formattedID string
|
||||
switch g.Type {
|
||||
case provider.GranteeType_GRANTEE_TYPE_USER:
|
||||
granteeType = 0
|
||||
var err error
|
||||
formattedID, err = m.userConverter.UserIDToUserName(ctx, g.GetUserId())
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
case provider.GranteeType_GRANTEE_TYPE_GROUP:
|
||||
granteeType = 1
|
||||
formattedID = formatGroupID(g.GetGroupId())
|
||||
default:
|
||||
granteeType = -1
|
||||
}
|
||||
return granteeType, formattedID, nil
|
||||
}
|
||||
|
||||
func (m *mgr) extractGrantee(ctx context.Context, t int, g string) (*provider.Grantee, error) {
|
||||
var grantee provider.Grantee
|
||||
switch t {
|
||||
case 0:
|
||||
userid, err := m.userConverter.UserNameToUserID(ctx, g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grantee.Type = provider.GranteeType_GRANTEE_TYPE_USER
|
||||
grantee.Id = &provider.Grantee_UserId{UserId: userid}
|
||||
case 1, 2:
|
||||
grantee.Type = provider.GranteeType_GRANTEE_TYPE_GROUP
|
||||
grantee.Id = &provider.Grantee_GroupId{GroupId: extractGroupID(g)}
|
||||
default:
|
||||
grantee.Type = provider.GranteeType_GRANTEE_TYPE_INVALID
|
||||
}
|
||||
return &grantee, nil
|
||||
}
|
||||
|
||||
func resourceTypeToItem(r provider.ResourceType) string {
|
||||
switch r {
|
||||
case provider.ResourceType_RESOURCE_TYPE_FILE:
|
||||
return "file"
|
||||
case provider.ResourceType_RESOURCE_TYPE_CONTAINER:
|
||||
return "folder"
|
||||
case provider.ResourceType_RESOURCE_TYPE_REFERENCE:
|
||||
return "reference"
|
||||
case provider.ResourceType_RESOURCE_TYPE_SYMLINK:
|
||||
return "symlink"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func sharePermToInt(p *provider.ResourcePermissions) int {
|
||||
return int(conversions.RoleFromResourcePermissions(p, false).OCSPermissions())
|
||||
}
|
||||
|
||||
func intTosharePerm(p int) (*provider.ResourcePermissions, error) {
|
||||
perms, err := conversions.NewPermissions(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conversions.RoleFromOCSPermissions(perms, nil).CS3ResourcePermissions(), nil
|
||||
}
|
||||
|
||||
func intToShareState(g int) collaboration.ShareState {
|
||||
switch g {
|
||||
case 0:
|
||||
return collaboration.ShareState_SHARE_STATE_ACCEPTED
|
||||
case 1:
|
||||
return collaboration.ShareState_SHARE_STATE_PENDING
|
||||
case 2:
|
||||
return collaboration.ShareState_SHARE_STATE_REJECTED
|
||||
default:
|
||||
return collaboration.ShareState_SHARE_STATE_INVALID
|
||||
}
|
||||
}
|
||||
|
||||
func formatUserID(u *userpb.UserId) string {
|
||||
return u.OpaqueId
|
||||
}
|
||||
|
||||
func formatGroupID(u *grouppb.GroupId) string {
|
||||
return u.OpaqueId
|
||||
}
|
||||
|
||||
func extractGroupID(u string) *grouppb.GroupId {
|
||||
return &grouppb.GroupId{OpaqueId: u}
|
||||
}
|
||||
|
||||
func (m *mgr) convertToCS3Share(ctx context.Context, s DBShare, storageMountID string) (*collaboration.Share, error) {
|
||||
ts := &typespb.Timestamp{
|
||||
Seconds: uint64(s.STime),
|
||||
}
|
||||
permissions, err := intTosharePerm(s.Permissions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grantee, err := m.extractGrantee(ctx, s.ShareType, s.ShareWith)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
owner, err := m.userConverter.UserNameToUserID(ctx, s.UIDOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var creator *userpb.UserId
|
||||
if s.UIDOwner == s.UIDInitiator {
|
||||
creator = owner
|
||||
} else {
|
||||
creator, err = m.userConverter.UserNameToUserID(ctx, s.UIDOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &collaboration.Share{
|
||||
Id: &collaboration.ShareId{
|
||||
OpaqueId: s.ID,
|
||||
},
|
||||
ResourceId: &provider.ResourceId{
|
||||
SpaceId: s.ItemStorage,
|
||||
OpaqueId: s.FileSource,
|
||||
},
|
||||
Permissions: &collaboration.SharePermissions{Permissions: permissions},
|
||||
Grantee: grantee,
|
||||
Owner: owner,
|
||||
Creator: creator,
|
||||
Ctime: ts,
|
||||
Mtime: ts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mgr) convertToCS3ReceivedShare(ctx context.Context, s DBShare, storageMountID string) (*collaboration.ReceivedShare, error) {
|
||||
share, err := m.convertToCS3Share(ctx, s, storageMountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var state collaboration.ShareState
|
||||
if s.RejectedBy != "" {
|
||||
state = collaboration.ShareState_SHARE_STATE_REJECTED
|
||||
} else {
|
||||
state = intToShareState(s.State)
|
||||
}
|
||||
return &collaboration.ReceivedShare{
|
||||
Share: share,
|
||||
State: state,
|
||||
MountPoint: &provider.Reference{Path: strings.TrimLeft(s.FileTarget, "/")},
|
||||
}, nil
|
||||
}
|
||||
674
vendor/github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go
generated
vendored
674
vendor/github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go
generated
vendored
@@ -1,674 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
ctxpkg "github.com/owncloud/reva/v2/pkg/ctx"
|
||||
"github.com/owncloud/reva/v2/pkg/errtypes"
|
||||
"github.com/owncloud/reva/v2/pkg/share"
|
||||
"github.com/owncloud/reva/v2/pkg/share/manager/registry"
|
||||
"github.com/owncloud/reva/v2/pkg/utils"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/genproto/protobuf/field_mask"
|
||||
|
||||
// Provides mysql drivers
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
const (
|
||||
shareTypeUser = 0
|
||||
shareTypeGroup = 1
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("owncloudsql", NewMysql)
|
||||
}
|
||||
|
||||
type config struct {
|
||||
GatewayAddr string `mapstructure:"gateway_addr"`
|
||||
StorageMountID string `mapstructure:"storage_mount_id"`
|
||||
DbUsername string `mapstructure:"db_username"`
|
||||
DbPassword string `mapstructure:"db_password"`
|
||||
DbHost string `mapstructure:"db_host"`
|
||||
DbPort int `mapstructure:"db_port"`
|
||||
DbName string `mapstructure:"db_name"`
|
||||
}
|
||||
|
||||
type mgr struct {
|
||||
driver string
|
||||
db *sql.DB
|
||||
storageMountID string
|
||||
userConverter UserConverter
|
||||
}
|
||||
|
||||
// NewMysql returns a new share manager connection to a mysql database
|
||||
func NewMysql(m map[string]interface{}) (share.Manager, error) {
|
||||
c, err := parseConfig(m)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error creating a new manager")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", c.DbUsername, c.DbPassword, c.DbHost, c.DbPort, c.DbName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userConverter := NewGatewayUserConverter(c.GatewayAddr)
|
||||
|
||||
return New("mysql", db, c.StorageMountID, userConverter)
|
||||
}
|
||||
|
||||
// New returns a new Cache instance connecting to the given sql.DB
|
||||
func New(driver string, db *sql.DB, storageMountID string, userConverter UserConverter) (share.Manager, error) {
|
||||
return &mgr{
|
||||
driver: driver,
|
||||
db: db,
|
||||
storageMountID: storageMountID,
|
||||
userConverter: userConverter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseConfig(m map[string]interface{}) (*config, error) {
|
||||
c := &config{}
|
||||
if err := mapstructure.Decode(m, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) {
|
||||
user := ctxpkg.ContextMustGetUser(ctx)
|
||||
|
||||
// do not allow share to myself or the owner if share is for a user
|
||||
// TODO(labkode): should not this be caught already at the gw level?
|
||||
if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER &&
|
||||
(utils.UserEqual(g.Grantee.GetUserId(), user.Id) || utils.UserEqual(g.Grantee.GetUserId(), md.Owner)) {
|
||||
return nil, errtypes.BadRequest("owncloudsql: owner/creator and grantee are the same")
|
||||
}
|
||||
|
||||
// check if share already exists.
|
||||
key := &collaboration.ShareKey{
|
||||
Owner: md.Owner,
|
||||
ResourceId: md.Id,
|
||||
Grantee: g.Grantee,
|
||||
}
|
||||
_, err := m.getByKey(ctx, key)
|
||||
|
||||
// share already exists
|
||||
if err == nil {
|
||||
return nil, errtypes.AlreadyExists(key.String())
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
ts := &typespb.Timestamp{
|
||||
Seconds: uint64(now),
|
||||
}
|
||||
|
||||
owner, err := m.userConverter.UserIDToUserName(ctx, md.Owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shareType, shareWith, err := m.formatGrantee(ctx, g.Grantee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
itemType := resourceTypeToItem(md.Type)
|
||||
targetPath := path.Join("/", path.Base(md.Path))
|
||||
permissions := sharePermToInt(g.Permissions.Permissions)
|
||||
itemSource := md.Id.OpaqueId
|
||||
fileSource, err := strconv.ParseUint(itemSource, 10, 64)
|
||||
if err != nil {
|
||||
// it can be the case that the item source may be a character string
|
||||
// we leave fileSource blank in that case
|
||||
fileSource = 0
|
||||
}
|
||||
|
||||
stmtString := "INSERT INTO oc_share (share_type,uid_owner,uid_initiator,item_type,item_source,file_source,permissions,stime,share_with,file_target) VALUES (?,?,?,?,?,?,?,?,?,?)"
|
||||
stmtValues := []interface{}{shareType, owner, user.Username, itemType, itemSource, fileSource, permissions, now, shareWith, targetPath}
|
||||
|
||||
stmt, err := m.db.Prepare(stmtString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := stmt.ExecContext(ctx, stmtValues...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &collaboration.Share{
|
||||
Id: &collaboration.ShareId{
|
||||
OpaqueId: strconv.FormatInt(lastID, 10),
|
||||
},
|
||||
ResourceId: md.Id,
|
||||
Permissions: g.Permissions,
|
||||
Grantee: g.Grantee,
|
||||
Owner: md.Owner,
|
||||
Creator: user.Id,
|
||||
Ctime: ts,
|
||||
Mtime: ts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) {
|
||||
var s *collaboration.Share
|
||||
var err error
|
||||
switch {
|
||||
case ref.GetId() != nil:
|
||||
s, err = m.getByID(ctx, ref.GetId())
|
||||
case ref.GetKey() != nil:
|
||||
s, err = m.getByKey(ctx, ref.GetKey())
|
||||
default:
|
||||
err = errtypes.NotFound(ref.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) error {
|
||||
uid := ctxpkg.ContextMustGetUser(ctx).Username
|
||||
var query string
|
||||
params := []interface{}{}
|
||||
switch {
|
||||
case ref.GetId() != nil:
|
||||
query = "DELETE FROM oc_share where id=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, ref.GetId().OpaqueId, uid, uid)
|
||||
case ref.GetKey() != nil:
|
||||
key := ref.GetKey()
|
||||
shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
owner := formatUserID(key.Owner)
|
||||
query = "DELETE FROM oc_share WHERE uid_owner=? AND file_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, owner, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid)
|
||||
default:
|
||||
return errtypes.NotFound(ref.String())
|
||||
}
|
||||
|
||||
stmt, err := m.db.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := stmt.ExecContext(ctx, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowCnt, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowCnt == 0 {
|
||||
return errtypes.NotFound(ref.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions, updated *collaboration.Share, fieldMask *field_mask.FieldMask) (*collaboration.Share, error) {
|
||||
permissions := sharePermToInt(p.Permissions)
|
||||
uid := ctxpkg.ContextMustGetUser(ctx).Username
|
||||
|
||||
var query string
|
||||
params := []interface{}{}
|
||||
switch {
|
||||
case ref.GetId() != nil:
|
||||
query = "update oc_share set permissions=?,stime=? where id=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, permissions, time.Now().Unix(), ref.GetId().OpaqueId, uid, uid)
|
||||
case ref.GetKey() != nil:
|
||||
key := ref.GetKey()
|
||||
shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
owner := formatUserID(key.Owner)
|
||||
query = "update oc_share set permissions=?,stime=? where (uid_owner=? or uid_initiator=?) AND file_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)"
|
||||
params = append(params, permissions, time.Now().Unix(), owner, owner, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid)
|
||||
default:
|
||||
return nil, errtypes.NotFound(ref.String())
|
||||
}
|
||||
|
||||
stmt, err := m.db.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = stmt.ExecContext(ctx, params...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.GetShare(ctx, ref)
|
||||
}
|
||||
|
||||
func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ([]*collaboration.Share, error) {
|
||||
uid := ctxpkg.ContextMustGetUser(ctx).Username
|
||||
query := `
|
||||
SELECT
|
||||
coalesce(s.uid_owner, '') as uid_owner, coalesce(s.uid_initiator, '') as uid_initiator,
|
||||
coalesce(s.share_with, '') as share_with, coalesce(s.file_source, '') as file_source,
|
||||
s.file_target, s.id, s.stime, s.permissions, s.share_type, fc.storage as storage
|
||||
FROM oc_share s
|
||||
LEFT JOIN oc_filecache fc ON fc.fileid = file_source
|
||||
WHERE (uid_owner=? or uid_initiator=?)
|
||||
`
|
||||
params := []interface{}{uid, uid}
|
||||
|
||||
var (
|
||||
filterQuery string
|
||||
filterParams []interface{}
|
||||
err error
|
||||
)
|
||||
if len(filters) == 0 {
|
||||
filterQuery += "(share_type=? OR share_type=?)"
|
||||
params = append(params, shareTypeUser)
|
||||
params = append(params, shareTypeGroup)
|
||||
} else {
|
||||
filterQuery, filterParams, err = translateFilters(filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(params, filterParams...)
|
||||
}
|
||||
|
||||
if filterQuery != "" {
|
||||
query = fmt.Sprintf("%s AND (%s)", query, filterQuery)
|
||||
}
|
||||
|
||||
rows, err := m.db.QueryContext(ctx, query, params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var s DBShare
|
||||
shares := []*collaboration.Share{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.FileTarget, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.ItemStorage); err != nil {
|
||||
continue
|
||||
}
|
||||
share, err := m.convertToCS3Share(ctx, s, m.storageMountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shares = append(shares, share)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return shares, nil
|
||||
}
|
||||
|
||||
// we list the shares that are targeted to the user in context or to the user groups.
|
||||
func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userpb.UserId) ([]*collaboration.ReceivedShare, error) {
|
||||
user := ctxpkg.ContextMustGetUser(ctx)
|
||||
if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE {
|
||||
u, err := m.userConverter.GetUser(forUser)
|
||||
if err != nil {
|
||||
return nil, errtypes.BadRequest("user not found")
|
||||
}
|
||||
user = u
|
||||
}
|
||||
uid := user.Username
|
||||
|
||||
params := []interface{}{uid, uid, uid}
|
||||
for _, v := range user.Groups {
|
||||
params = append(params, v)
|
||||
}
|
||||
|
||||
homeConcat := ""
|
||||
if m.driver == "mysql" { // mysql concat
|
||||
homeConcat = "storages.id = CONCAT('home::', s.uid_owner)"
|
||||
} else { // sqlite3 concat
|
||||
homeConcat = "storages.id = 'home::' || s.uid_owner"
|
||||
}
|
||||
userSelect := ""
|
||||
if len(user.Groups) > 0 {
|
||||
userSelect = "AND ((share_type != 1 AND share_with=?) OR (share_type = 1 AND share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))"
|
||||
} else {
|
||||
userSelect = "AND (share_type != 1 AND share_with=?)"
|
||||
}
|
||||
query := `
|
||||
WITH results AS
|
||||
(
|
||||
SELECT s.*, storages.numeric_id FROM oc_share s
|
||||
LEFT JOIN oc_storages storages ON ` + homeConcat + `
|
||||
WHERE (uid_owner != ? AND uid_initiator != ?) ` + userSelect + `
|
||||
)
|
||||
SELECT COALESCE(r.uid_owner, '') AS uid_owner, COALESCE(r.uid_initiator, '') AS uid_initiator, COALESCE(r.share_with, '')
|
||||
AS share_with, COALESCE(r.file_source, '') AS file_source, COALESCE(r2.file_target, r.file_target), r.id, r.stime, r.permissions, r.share_type, COALESCE(r2.accepted, r.accepted),
|
||||
r.numeric_id, COALESCE(r.parent, -1) AS parent FROM results r LEFT JOIN results r2 ON r.id = r2.parent WHERE r.parent IS NULL`
|
||||
|
||||
filterQuery, filterParams, err := translateFilters(filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(params, filterParams...)
|
||||
|
||||
if filterQuery != "" {
|
||||
query = fmt.Sprintf("%s AND (%s)", query, filterQuery)
|
||||
}
|
||||
query += ";"
|
||||
rows, err := m.db.QueryContext(ctx, query, params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var s DBShare
|
||||
shares := []*collaboration.ReceivedShare{}
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.FileTarget, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State, &s.ItemStorage, &s.Parent); err != nil {
|
||||
continue
|
||||
}
|
||||
share, err := m.convertToCS3ReceivedShare(ctx, s, m.storageMountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shares = append(shares, share)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return shares, nil
|
||||
}
|
||||
|
||||
func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) {
|
||||
var s *collaboration.ReceivedShare
|
||||
var err error
|
||||
switch {
|
||||
case ref.GetId() != nil:
|
||||
s, err = m.getReceivedByID(ctx, ref.GetId())
|
||||
case ref.GetKey() != nil:
|
||||
s, err = m.getReceivedByKey(ctx, ref.GetKey())
|
||||
default:
|
||||
err = errtypes.NotFound(ref.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
||||
}
|
||||
|
||||
func (m *mgr) UpdateReceivedShare(ctx context.Context, receivedShare *collaboration.ReceivedShare, fieldMask *field_mask.FieldMask, _ *userpb.UserId) (*collaboration.ReceivedShare, error) {
|
||||
// TODO: How to inject the uid when a UserId is set? override it in the ctx? Add parameter to GetReceivedShare?
|
||||
rs, err := m.GetReceivedShare(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: receivedShare.Share.Id}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields := []string{}
|
||||
params := []interface{}{}
|
||||
for i := range fieldMask.Paths {
|
||||
switch fieldMask.Paths[i] {
|
||||
case "state":
|
||||
rs.State = receivedShare.State
|
||||
fields = append(fields, "accepted=?")
|
||||
switch rs.State {
|
||||
case collaboration.ShareState_SHARE_STATE_REJECTED:
|
||||
params = append(params, 2)
|
||||
case collaboration.ShareState_SHARE_STATE_ACCEPTED:
|
||||
params = append(params, 0)
|
||||
}
|
||||
case "mount_point":
|
||||
fields = append(fields, "file_target=?")
|
||||
rs.MountPoint = receivedShare.MountPoint
|
||||
params = append(params, rs.MountPoint.Path)
|
||||
case "hidden":
|
||||
continue
|
||||
default:
|
||||
return nil, errtypes.NotSupported("updating " + fieldMask.Paths[i] + " is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
return nil, fmt.Errorf("no valid field provided in the fieldmask")
|
||||
}
|
||||
|
||||
updateReceivedShare := func(column string) error {
|
||||
query := "update oc_share set "
|
||||
query += strings.Join(fields, ",")
|
||||
query += fmt.Sprintf(" where %s=?", column)
|
||||
params := append(params, rs.Share.Id.OpaqueId)
|
||||
|
||||
stmt, err := m.db.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := stmt.ExecContext(ctx, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected < 1 {
|
||||
return fmt.Errorf("No rows updated")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = updateReceivedShare("parent") // Try to update the child state in case of group shares first
|
||||
if err != nil {
|
||||
err = updateReceivedShare("id")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func (m *mgr) getByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.Share, error) {
|
||||
uid := ctxpkg.ContextMustGetUser(ctx).Username
|
||||
s := 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(file_source, '') as file_source, file_target, stime, permissions, share_type FROM oc_share WHERE id=? AND (uid_owner=? or uid_initiator=?)"
|
||||
if err := m.db.QueryRowContext(ctx, query, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.FileTarget, &s.STime, &s.Permissions, &s.ShareType); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(id.OpaqueId)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return m.convertToCS3Share(ctx, s, m.storageMountID)
|
||||
}
|
||||
|
||||
func (m *mgr) getByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) {
|
||||
owner, err := m.userConverter.UserIDToUserName(ctx, key.Owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uid := ctxpkg.ContextMustGetUser(ctx).Username
|
||||
|
||||
s := DBShare{}
|
||||
shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(file_source, '') as file_source, file_target, id, stime, permissions, share_type FROM oc_share WHERE uid_owner=? AND file_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)"
|
||||
if err = m.db.QueryRowContext(ctx, query, owner, key.ResourceId.StorageId, shareType, shareWith, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.FileTarget, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(key.String())
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return m.convertToCS3Share(ctx, s, m.storageMountID)
|
||||
}
|
||||
|
||||
func (m *mgr) getReceivedByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.ReceivedShare, error) {
|
||||
user := ctxpkg.ContextMustGetUser(ctx)
|
||||
uid := user.Username
|
||||
|
||||
params := []interface{}{id.OpaqueId, id.OpaqueId, uid}
|
||||
for _, v := range user.Groups {
|
||||
params = append(params, v)
|
||||
}
|
||||
|
||||
homeConcat := ""
|
||||
if m.driver == "mysql" { // mysql concat
|
||||
homeConcat = "storages.id = CONCAT('home::', s.uid_owner)"
|
||||
} else { // sqlite3 concat
|
||||
homeConcat = "storages.id = 'home::' || s.uid_owner"
|
||||
}
|
||||
userSelect := ""
|
||||
if len(user.Groups) > 0 {
|
||||
userSelect = "AND ((share_type != 1 AND share_with=?) OR (share_type = 1 AND share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + ")))"
|
||||
} else {
|
||||
userSelect = "AND (share_type != 1 AND share_with=?)"
|
||||
}
|
||||
|
||||
query := `
|
||||
WITH results AS
|
||||
(
|
||||
SELECT s.*, storages.numeric_id
|
||||
FROM oc_share s
|
||||
LEFT JOIN oc_storages storages ON ` + homeConcat + `
|
||||
WHERE s.id=? OR s.parent=? ` + userSelect + `
|
||||
)
|
||||
SELECT COALESCE(r.uid_owner, '') AS uid_owner, COALESCE(r.uid_initiator, '') AS uid_initiator, COALESCE(r.share_with, '')
|
||||
AS share_with, COALESCE(r.file_source, '') AS file_source, COALESCE(r2.file_target, r.file_target), r.id, r.stime, r.permissions, r.share_type, COALESCE(r2.accepted, r.accepted),
|
||||
r.numeric_id, COALESCE(r.parent, -1) AS parent
|
||||
FROM results r
|
||||
LEFT JOIN results r2 ON r.id = r2.parent
|
||||
WHERE r.parent IS NULL;
|
||||
`
|
||||
|
||||
s := DBShare{}
|
||||
if err := m.db.QueryRowContext(ctx, query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.FileTarget, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State, &s.ItemStorage, &s.Parent); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(id.OpaqueId)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return m.convertToCS3ReceivedShare(ctx, s, m.storageMountID)
|
||||
}
|
||||
|
||||
func (m *mgr) getReceivedByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.ReceivedShare, error) {
|
||||
user := ctxpkg.ContextMustGetUser(ctx)
|
||||
uid := user.Username
|
||||
|
||||
shareType, shareWith, err := m.formatGrantee(ctx, key.Grantee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := []interface{}{uid, formatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, shareWith}
|
||||
for _, v := range user.Groups {
|
||||
params = append(params, v)
|
||||
}
|
||||
|
||||
s := DBShare{}
|
||||
query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(file_source, '') as file_source, file_target, ts.id, stime, permissions, share_type, accepted FROM oc_share ts WHERE uid_owner=? AND file_source=? AND share_type=? AND share_with=? "
|
||||
if len(user.Groups) > 0 {
|
||||
query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))"
|
||||
} else {
|
||||
query += "AND (share_with=?)"
|
||||
}
|
||||
|
||||
if err := m.db.QueryRowContext(ctx, query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.FileSource, &s.FileTarget, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(key.String())
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return m.convertToCS3ReceivedShare(ctx, s, m.storageMountID)
|
||||
}
|
||||
|
||||
func granteeTypeToShareType(granteeType provider.GranteeType) int {
|
||||
switch granteeType {
|
||||
case provider.GranteeType_GRANTEE_TYPE_USER:
|
||||
return shareTypeUser
|
||||
case provider.GranteeType_GRANTEE_TYPE_GROUP:
|
||||
return shareTypeGroup
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// translateFilters translates the filters to sql queries
|
||||
func translateFilters(filters []*collaboration.Filter) (string, []interface{}, error) {
|
||||
var (
|
||||
filterQuery string
|
||||
params []interface{}
|
||||
)
|
||||
|
||||
groupedFilters := share.GroupFiltersByType(filters)
|
||||
// If multiple filters of the same type are passed to this function, they need to be combined with the `OR` operator.
|
||||
// That is why the filters got grouped by type.
|
||||
// For every given filter type, iterate over the filters and if there are more than one combine them.
|
||||
// Combine the different filter types using `AND`
|
||||
var filterCounter = 0
|
||||
for filterType, filters := range groupedFilters {
|
||||
switch filterType {
|
||||
case collaboration.Filter_TYPE_RESOURCE_ID:
|
||||
filterQuery += "("
|
||||
for i, f := range filters {
|
||||
filterQuery += "file_source=?"
|
||||
params = append(params, f.GetResourceId().OpaqueId)
|
||||
|
||||
if i != len(filters)-1 {
|
||||
filterQuery += " OR "
|
||||
}
|
||||
}
|
||||
filterQuery += ")"
|
||||
case collaboration.Filter_TYPE_GRANTEE_TYPE:
|
||||
filterQuery += "("
|
||||
for i, f := range filters {
|
||||
filterQuery += "r.share_type=?"
|
||||
params = append(params, granteeTypeToShareType(f.GetGranteeType()))
|
||||
|
||||
if i != len(filters)-1 {
|
||||
filterQuery += " OR "
|
||||
}
|
||||
}
|
||||
filterQuery += ")"
|
||||
case collaboration.Filter_TYPE_EXCLUDE_DENIALS:
|
||||
// TODO this may change once the mapping of permission to share types is completed (cf. pkg/cbox/utils/conversions.go)
|
||||
filterQuery += "r.permissions > 0"
|
||||
default:
|
||||
return "", nil, fmt.Errorf("filter type is not supported")
|
||||
}
|
||||
if filterCounter != len(groupedFilters)-1 {
|
||||
filterQuery += " AND "
|
||||
}
|
||||
filterCounter++
|
||||
}
|
||||
return filterQuery, params, nil
|
||||
}
|
||||
BIN
vendor/github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql/test.db
generated
vendored
BIN
vendor/github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql/test.db
generated
vendored
Binary file not shown.
1
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/loader/loader.go
generated
vendored
1
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/loader/loader.go
generated
vendored
@@ -32,7 +32,6 @@ import (
|
||||
_ "github.com/owncloud/reva/v2/pkg/storage/fs/localhome"
|
||||
_ "github.com/owncloud/reva/v2/pkg/storage/fs/nextcloud"
|
||||
_ "github.com/owncloud/reva/v2/pkg/storage/fs/ocis"
|
||||
_ "github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql"
|
||||
_ "github.com/owncloud/reva/v2/pkg/storage/fs/s3"
|
||||
_ "github.com/owncloud/reva/v2/pkg/storage/fs/s3ng"
|
||||
// Add your own here
|
||||
|
||||
761
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/filecache/filecache.go
generated
vendored
761
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/filecache/filecache.go
generated
vendored
@@ -1,761 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/conversions"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
// Provides mysql drivers
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
// Cache represents a oc10-style file cache
|
||||
type Cache struct {
|
||||
driver string
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// Storage represents a storage entry in the database
|
||||
type Storage struct {
|
||||
ID string
|
||||
NumericID int
|
||||
}
|
||||
|
||||
// File represents an entry of the file cache
|
||||
type File struct {
|
||||
ID int
|
||||
Storage int
|
||||
Parent int
|
||||
MimePart int
|
||||
MimeType int
|
||||
MimeTypeString string
|
||||
Size int
|
||||
MTime int
|
||||
StorageMTime int
|
||||
UnencryptedSize int
|
||||
Permissions int
|
||||
Encrypted bool
|
||||
Path string
|
||||
Name string
|
||||
Etag string
|
||||
Checksum string
|
||||
}
|
||||
|
||||
// TrashItem represents a trash item of the file cache
|
||||
type TrashItem struct {
|
||||
ID int
|
||||
Name string
|
||||
User string
|
||||
Path string
|
||||
Timestamp int
|
||||
}
|
||||
|
||||
// Scannable describes the interface providing a Scan method
|
||||
type Scannable interface {
|
||||
Scan(...interface{}) error
|
||||
}
|
||||
|
||||
// NewMysql returns a new Cache instance connecting to a MySQL database
|
||||
func NewMysql(dsn string) (*Cache, error) {
|
||||
sqldb, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
// FIXME make configurable
|
||||
sqldb.SetConnMaxLifetime(time.Minute * 3)
|
||||
sqldb.SetConnMaxIdleTime(time.Second * 30)
|
||||
sqldb.SetMaxOpenConns(100)
|
||||
sqldb.SetMaxIdleConns(10)
|
||||
|
||||
err = sqldb.Ping()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
return New("mysql", sqldb)
|
||||
}
|
||||
|
||||
// New returns a new Cache instance connecting to the given sql.DB
|
||||
func New(driver string, sqldb *sql.DB) (*Cache, error) {
|
||||
return &Cache{
|
||||
driver: driver,
|
||||
db: sqldb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListStorages returns the list of numeric ids of all storages
|
||||
// Optionally only home storages are considered
|
||||
func (c *Cache) ListStorages(ctx context.Context, onlyHome bool) ([]*Storage, error) {
|
||||
query := ""
|
||||
if onlyHome {
|
||||
mountPointConcat := ""
|
||||
if c.driver == "mysql" {
|
||||
mountPointConcat = "m.mount_point = CONCAT('/', m.user_id, '/')"
|
||||
} else { // sqlite3
|
||||
mountPointConcat = "m.mount_point = '/' || m.user_id || '/'"
|
||||
}
|
||||
|
||||
query = "SELECT s.id, s.numeric_id FROM oc_storages s JOIN oc_mounts m ON s.numeric_id = m.storage_id WHERE " + mountPointConcat
|
||||
} else {
|
||||
query = "SELECT id, numeric_id FROM oc_storages"
|
||||
}
|
||||
rows, err := c.db.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
storages := []*Storage{}
|
||||
for rows.Next() {
|
||||
storage := &Storage{}
|
||||
err := rows.Scan(&storage.ID, &storage.NumericID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storages = append(storages, storage)
|
||||
}
|
||||
return storages, nil
|
||||
}
|
||||
|
||||
// GetStorage returns the storage with the given numeric id
|
||||
func (c *Cache) GetStorage(ctx context.Context, numeridID interface{}) (*Storage, error) {
|
||||
numericID, err := toIntID(numeridID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row := c.db.QueryRowContext(ctx, "SELECT id, numeric_id FROM oc_storages WHERE numeric_id = ?", numericID)
|
||||
s := &Storage{}
|
||||
switch err := row.Scan(&s.ID, &s.NumericID); err {
|
||||
case nil:
|
||||
return s, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// GetNumericStorageID returns the database id for the given storage
|
||||
func (c *Cache) GetNumericStorageID(ctx context.Context, id string) (int, error) {
|
||||
row := c.db.QueryRowContext(ctx, "SELECT numeric_id FROM oc_storages WHERE id = ?", id)
|
||||
var nid int
|
||||
switch err := row.Scan(&nid); err {
|
||||
case nil:
|
||||
return nid, nil
|
||||
default:
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
// CreateStorage creates a new storage and returns its numeric id
|
||||
func (c *Cache) CreateStorage(ctx context.Context, id string) (int, error) {
|
||||
tx, err := c.db.Begin()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
stmt, err := tx.PrepareContext(ctx, "INSERT INTO oc_storages(id) VALUES(?)")
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
res, err := stmt.ExecContext(ctx, id)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
insertedID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"path": "",
|
||||
"etag": "",
|
||||
"mimetype": "httpd/unix-directory",
|
||||
}
|
||||
_, err = c.doInsertOrUpdate(ctx, tx, int(insertedID), data, true)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return int(insertedID), err
|
||||
}
|
||||
|
||||
// GetStorageOwner returns the username of the owner of the given storage
|
||||
func (c *Cache) GetStorageOwner(ctx context.Context, numericID interface{}) (string, error) {
|
||||
numericID, err := toIntID(numericID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
row := c.db.QueryRowContext(ctx, "SELECT id FROM oc_storages WHERE numeric_id = ?", numericID)
|
||||
var id string
|
||||
switch err := row.Scan(&id); err {
|
||||
case nil:
|
||||
return strings.TrimPrefix(id, "home::"), nil
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// GetStorageOwnerByFileID returns the username of the owner of the given entry
|
||||
func (c *Cache) GetStorageOwnerByFileID(ctx context.Context, numericID interface{}) (string, error) {
|
||||
numericID, err := toIntID(numericID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
row := c.db.QueryRowContext(ctx, "SELECT id FROM oc_storages storages, oc_filecache cache WHERE storages.numeric_id = cache.storage AND cache.fileid = ?", numericID)
|
||||
var id string
|
||||
switch err := row.Scan(&id); err {
|
||||
case nil:
|
||||
return strings.TrimPrefix(id, "home::"), nil
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) rowToFile(row Scannable) (*File, error) {
|
||||
var fileid, storage, parent, mimetype, mimepart, size, mtime, storageMtime, encrypted, unencryptedSize int
|
||||
var permissions sql.NullInt32
|
||||
var path, name, etag, checksum, mimetypestring sql.NullString
|
||||
err := row.Scan(&fileid, &storage, &path, &parent, &permissions, &mimetype, &mimepart, &mimetypestring, &size, &mtime, &storageMtime, &encrypted, &unencryptedSize, &name, &etag, &checksum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &File{
|
||||
ID: fileid,
|
||||
Storage: storage,
|
||||
Path: path.String,
|
||||
Parent: parent,
|
||||
Permissions: int(permissions.Int32),
|
||||
MimeType: mimetype,
|
||||
MimeTypeString: mimetypestring.String,
|
||||
MimePart: mimepart,
|
||||
Size: size,
|
||||
MTime: mtime,
|
||||
StorageMTime: storageMtime,
|
||||
Encrypted: encrypted == 1,
|
||||
UnencryptedSize: unencryptedSize,
|
||||
Name: name.String,
|
||||
Etag: etag.String,
|
||||
Checksum: checksum.String,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get returns the cache entry for the specified storage/path
|
||||
func (c *Cache) Get(ctx context.Context, s interface{}, p string) (*File, error) {
|
||||
storageID, err := toIntID(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
phashBytes := md5.Sum([]byte(p))
|
||||
phash := hex.EncodeToString(phashBytes[:])
|
||||
|
||||
row := c.db.QueryRowContext(ctx, `
|
||||
SELECT
|
||||
fc.fileid, fc.storage, fc.path, fc.parent, fc.permissions, fc.mimetype, fc.mimepart,
|
||||
mt.mimetype, fc.size, fc.mtime, fc.storage_mtime, fc.encrypted, fc.unencrypted_size,
|
||||
fc.name, fc.etag, fc.checksum
|
||||
FROM oc_filecache fc
|
||||
LEFT JOIN oc_mimetypes mt ON fc.mimetype = mt.id
|
||||
WHERE path_hash = ? AND storage = ?`, phash, storageID)
|
||||
return c.rowToFile(row)
|
||||
}
|
||||
|
||||
// Path returns the path for the specified entry
|
||||
func (c *Cache) Path(ctx context.Context, id interface{}) (string, error) {
|
||||
id, err := toIntID(id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
row := c.db.QueryRowContext(ctx, "SELECT path FROM oc_filecache WHERE fileid = ?", id)
|
||||
var path string
|
||||
err = row.Scan(&path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// List returns the list of entries below the given path
|
||||
func (c *Cache) List(ctx context.Context, storage interface{}, p string) ([]*File, error) {
|
||||
storageID, err := toIntID(storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rows *sql.Rows
|
||||
phash := fmt.Sprintf("%x", md5.Sum([]byte(strings.Trim(p, "/"))))
|
||||
rows, err = c.db.QueryContext(ctx, `
|
||||
SELECT
|
||||
fc.fileid, fc.storage, fc.path, fc.parent, fc.permissions, fc.mimetype, fc.mimepart,
|
||||
mt.mimetype, fc.size, fc.mtime, fc.storage_mtime, fc.encrypted, fc.unencrypted_size,
|
||||
fc.name, fc.etag, fc.checksum
|
||||
FROM oc_filecache fc
|
||||
LEFT JOIN oc_mimetypes mt ON fc.mimetype = mt.id
|
||||
WHERE storage = ? AND parent = (SELECT fileid FROM oc_filecache WHERE storage = ? AND path_hash=?) AND name IS NOT NULL
|
||||
`, storageID, storageID, phash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
entries := []*File{}
|
||||
for rows.Next() {
|
||||
entry, err := c.rowToFile(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// Permissions returns the permissions for the specified storage/path
|
||||
func (c *Cache) Permissions(ctx context.Context, storage interface{}, p string) (*provider.ResourcePermissions, error) {
|
||||
entry, err := c.Get(ctx, storage, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
perms, err := conversions.NewPermissions(entry.Permissions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conversions.RoleFromOCSPermissions(perms, nil).CS3ResourcePermissions(), nil
|
||||
}
|
||||
|
||||
// InsertOrUpdate creates or updates a cache entry
|
||||
func (c *Cache) InsertOrUpdate(ctx context.Context, storage interface{}, data map[string]interface{}, allowEmptyParent bool) (int, error) {
|
||||
tx, err := c.db.Begin()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
id, err := c.doInsertOrUpdate(ctx, tx, storage, data, allowEmptyParent)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (c *Cache) doInsertOrUpdate(ctx context.Context, tx *sql.Tx, storage interface{}, data map[string]interface{}, allowEmptyParent bool) (int, error) {
|
||||
storageID, err := toIntID(storage)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
columns := []string{"storage"}
|
||||
placeholders := []string{"?"}
|
||||
values := []interface{}{storage}
|
||||
|
||||
for _, key := range []string{"path", "mimetype", "etag"} {
|
||||
if _, exists := data[key]; !exists {
|
||||
return -1, fmt.Errorf("missing required data")
|
||||
}
|
||||
}
|
||||
|
||||
path := data["path"].(string)
|
||||
data["name"] = filepath.Base(path)
|
||||
if data["name"] == "." {
|
||||
data["name"] = ""
|
||||
}
|
||||
|
||||
parentPath := strings.TrimRight(filepath.Dir(path), "/")
|
||||
if parentPath == "." {
|
||||
parentPath = ""
|
||||
}
|
||||
if path == "" {
|
||||
data["parent"] = -1
|
||||
} else {
|
||||
parent, err := c.Get(ctx, storageID, parentPath)
|
||||
if err == nil {
|
||||
data["parent"] = parent.ID
|
||||
} else {
|
||||
if allowEmptyParent {
|
||||
data["parent"] = -1
|
||||
} else {
|
||||
return -1, fmt.Errorf("could not find parent %s, %s, %v, %w", parentPath, path, parent, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, exists := data["checksum"]; !exists {
|
||||
data["checksum"] = ""
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
switch k {
|
||||
case "path":
|
||||
phashBytes := md5.Sum([]byte(v.(string)))
|
||||
phash := hex.EncodeToString(phashBytes[:])
|
||||
columns = append(columns, "path_hash")
|
||||
values = append(values, phash)
|
||||
placeholders = append(placeholders, "?")
|
||||
case "storage_mtime":
|
||||
if _, exists := data["mtime"]; !exists {
|
||||
columns = append(columns, "mtime")
|
||||
values = append(values, v)
|
||||
placeholders = append(placeholders, "?")
|
||||
}
|
||||
case "mimetype":
|
||||
parts := strings.Split(v.(string), "/")
|
||||
columns = append(columns, "mimetype")
|
||||
values = append(values, v)
|
||||
placeholders = append(placeholders, "(SELECT id FROM oc_mimetypes WHERE mimetype=?)")
|
||||
columns = append(columns, "mimepart")
|
||||
values = append(values, parts[0])
|
||||
placeholders = append(placeholders, "(SELECT id FROM oc_mimetypes WHERE mimetype=?)")
|
||||
continue
|
||||
}
|
||||
|
||||
columns = append(columns, k)
|
||||
values = append(values, v)
|
||||
placeholders = append(placeholders, "?")
|
||||
}
|
||||
|
||||
err = c.insertMimetype(ctx, tx, data["mimetype"].(string))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
query := "INSERT INTO oc_filecache( " + strings.Join(columns, ", ") + ") VALUES(" + strings.Join(placeholders, ",") + ")"
|
||||
|
||||
updates := []string{}
|
||||
for i, column := range columns {
|
||||
if column != "path" && column != "path_hash" && column != "storage" {
|
||||
updates = append(updates, column+"="+placeholders[i])
|
||||
values = append(values, values[i])
|
||||
}
|
||||
}
|
||||
if c.driver == "mysql" { // mysql upsert
|
||||
query += " ON DUPLICATE KEY UPDATE "
|
||||
} else { // sqlite3 upsert
|
||||
query += " ON CONFLICT(storage,path_hash) DO UPDATE SET "
|
||||
}
|
||||
query += strings.Join(updates, ",")
|
||||
|
||||
stmt, err := tx.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
res, err := stmt.ExecContext(ctx, values...)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("could not store filecache item")
|
||||
return -1, err
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return int(id), nil
|
||||
}
|
||||
|
||||
// Copy creates a copy of the specified entry at the target path
|
||||
func (c *Cache) Copy(ctx context.Context, storage interface{}, sourcePath, targetPath string) (int, error) {
|
||||
storageID, err := toIntID(storage)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
source, err := c.Get(ctx, storageID, sourcePath)
|
||||
if err != nil {
|
||||
return -1, errors.Wrap(err, "could not find source")
|
||||
}
|
||||
|
||||
row := c.db.QueryRowContext(ctx, "SELECT mimetype FROM oc_mimetypes WHERE id=?", source.MimeType)
|
||||
var mimetype string
|
||||
err = row.Scan(&mimetype)
|
||||
if err != nil {
|
||||
return -1, errors.Wrap(err, "could not find source mimetype")
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"path": targetPath,
|
||||
"checksum": source.Checksum,
|
||||
"mimetype": mimetype,
|
||||
"permissions": source.Permissions,
|
||||
"etag": source.Etag,
|
||||
"size": source.Size,
|
||||
"mtime": source.MTime,
|
||||
"storage_mtime": source.StorageMTime,
|
||||
"encrypted": source.Encrypted,
|
||||
"unencrypted_size": source.UnencryptedSize,
|
||||
}
|
||||
return c.InsertOrUpdate(ctx, storage, data, false)
|
||||
}
|
||||
|
||||
// Move moves the specified entry to the target path
|
||||
func (c *Cache) Move(ctx context.Context, storage interface{}, sourcePath, targetPath string) error {
|
||||
storageID, err := toIntID(storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
source, err := c.Get(ctx, storageID, sourcePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not find source")
|
||||
}
|
||||
newParentPath := strings.TrimRight(filepath.Dir(targetPath), "/")
|
||||
newParent, err := c.Get(ctx, storageID, newParentPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not find new parent")
|
||||
}
|
||||
|
||||
tx, err := c.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
stmt, err := tx.Prepare("UPDATE oc_filecache SET parent=?, path=?, name=?, path_hash=? WHERE storage = ? AND fileid=?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
phashBytes := md5.Sum([]byte(targetPath))
|
||||
_, err = stmt.ExecContext(ctx, newParent.ID, targetPath, filepath.Base(targetPath), hex.EncodeToString(phashBytes[:]), storageID, source.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
childRows, err := tx.QueryContext(ctx, "SELECT fileid, path FROM oc_filecache WHERE parent = ?", source.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer childRows.Close()
|
||||
children := map[int]string{}
|
||||
for childRows.Next() {
|
||||
var (
|
||||
id int
|
||||
path string
|
||||
)
|
||||
err = childRows.Scan(&id, &path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
children[id] = path
|
||||
}
|
||||
for id, path := range children {
|
||||
path = strings.ReplaceAll(path, sourcePath, targetPath)
|
||||
phashBytes = md5.Sum([]byte(path))
|
||||
_, err = stmt.ExecContext(ctx, source.ID, path, filepath.Base(path), hex.EncodeToString(phashBytes[:]), storageID, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// Purge removes the specified storage/path from the cache without putting it into the trash
|
||||
func (c *Cache) Purge(ctx context.Context, storage interface{}, path string) error {
|
||||
storageID, err := toIntID(storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
phashBytes := md5.Sum([]byte(path))
|
||||
phash := hex.EncodeToString(phashBytes[:])
|
||||
_, err = c.db.ExecContext(ctx, "DELETE FROM oc_filecache WHERE storage = ? and path_hash = ?", storageID, phash)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete removes the specified storage/path from the cache
|
||||
func (c *Cache) Delete(ctx context.Context, storage interface{}, user, path, trashPath string) error {
|
||||
err := c.Move(ctx, storage, path, trashPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(.*)\.d(\d+)$`)
|
||||
parts := re.FindStringSubmatch(filepath.Base(trashPath))
|
||||
|
||||
query := "INSERT INTO oc_files_trash(user,id,timestamp,location) VALUES(?,?,?,?)"
|
||||
stmt, err := c.db.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relativeLocation, err := filepath.Rel("files/", filepath.Dir(path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stmt.ExecContext(ctx, user, filepath.Base(parts[1]), parts[2], relativeLocation)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("could not store filecache item")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRecycleItem returns the specified recycle item
|
||||
func (c *Cache) GetRecycleItem(ctx context.Context, user, path string, timestamp int) (*TrashItem, error) {
|
||||
row := c.db.QueryRowContext(ctx, "SELECT auto_id, id, location FROM oc_files_trash WHERE id = ? AND user = ? AND timestamp = ?", path, user, timestamp)
|
||||
var autoID int
|
||||
var id, location string
|
||||
err := row.Scan(&autoID, &id, &location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &TrashItem{
|
||||
ID: autoID,
|
||||
Name: id,
|
||||
User: user,
|
||||
Path: location,
|
||||
Timestamp: timestamp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EmptyRecycle clears the recycle bin for the given user
|
||||
func (c *Cache) EmptyRecycle(ctx context.Context, user string) error {
|
||||
_, err := c.db.ExecContext(ctx, "DELETE FROM oc_files_trash WHERE user = ?", user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storage, err := c.GetNumericStorageID(ctx, "home::"+user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.db.ExecContext(ctx, "DELETE FROM oc_filecache WHERE storage = ? AND PATH LIKE ?", storage, "files_trashbin/%")
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteRecycleItem deletes the specified item from the trash
|
||||
func (c *Cache) DeleteRecycleItem(ctx context.Context, user, path string, timestamp int) error {
|
||||
_, err := c.db.ExecContext(ctx, "DELETE FROM oc_files_trash WHERE id = ? AND user = ? AND timestamp = ?", path, user, timestamp)
|
||||
return err
|
||||
}
|
||||
|
||||
// PurgeRecycleItem deletes the specified item from the filecache and the trash
|
||||
func (c *Cache) PurgeRecycleItem(ctx context.Context, user, path string, timestamp int, isVersionFile bool) error {
|
||||
row := c.db.QueryRowContext(ctx, "SELECT auto_id, location FROM oc_files_trash WHERE id = ? AND user = ? AND timestamp = ?", path, user, timestamp)
|
||||
var autoID int
|
||||
var location string
|
||||
err := row.Scan(&autoID, &location)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.db.ExecContext(ctx, "DELETE FROM oc_files_trash WHERE auto_id=?", autoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storage, err := c.GetNumericStorageID(ctx, "home::"+user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trashType := "files"
|
||||
if isVersionFile {
|
||||
trashType = "versions"
|
||||
}
|
||||
item, err := c.Get(ctx, storage, filepath.Join("files_trashbin", trashType, path+".d"+strconv.Itoa(timestamp)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.db.ExecContext(ctx, "DELETE FROM oc_filecache WHERE fileid=? OR parent=?", item.ID, item.ID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SetEtag set a new etag for the specified item
|
||||
func (c *Cache) SetEtag(ctx context.Context, storage interface{}, path, etag string) error {
|
||||
storageID, err := toIntID(storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
source, err := c.Get(ctx, storageID, path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not find source")
|
||||
}
|
||||
stmt, err := c.db.PrepareContext(ctx, "UPDATE oc_filecache SET etag=? WHERE storage = ? AND fileid=?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stmt.ExecContext(ctx, etag, storageID, source.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cache) insertMimetype(ctx context.Context, tx *sql.Tx, mimetype string) error {
|
||||
insertPart := func(v string) error {
|
||||
stmt, err := tx.PrepareContext(ctx, "INSERT INTO oc_mimetypes(mimetype) VALUES(?)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stmt.ExecContext(ctx, v)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "UNIQUE") || strings.Contains(err.Error(), "Error 1062") {
|
||||
return nil // Already exists
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(mimetype, "/")
|
||||
err := insertPart(parts[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return insertPart(mimetype)
|
||||
}
|
||||
|
||||
func toIntID(rid interface{}) (int, error) {
|
||||
switch t := rid.(type) {
|
||||
case int:
|
||||
return t, nil
|
||||
case string:
|
||||
return strconv.Atoi(t)
|
||||
default:
|
||||
return -1, fmt.Errorf("invalid type")
|
||||
}
|
||||
}
|
||||
BIN
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/filecache/test.db
generated
vendored
BIN
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/filecache/test.db
generated
vendored
Binary file not shown.
2149
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql.go
generated
vendored
2149
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql.go
generated
vendored
File diff suppressed because it is too large
Load Diff
87
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql_unix.go
generated
vendored
87
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql_unix.go
generated
vendored
@@ -1,87 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/appctx"
|
||||
)
|
||||
|
||||
// TODO(jfd) get rid of the differences between unix and windows. the inode and dev should never be used for the etag because it interferes with backups
|
||||
|
||||
// calcEtag will create an etag based on the md5 of
|
||||
// - mtime,
|
||||
// - inode (if available),
|
||||
// - device (if available) and
|
||||
// - size.
|
||||
// errors are logged, but an etag will still be returned
|
||||
func calcEtag(ctx context.Context, fi os.FileInfo) string {
|
||||
log := appctx.GetLogger(ctx)
|
||||
h := md5.New()
|
||||
err := binary.Write(h, binary.BigEndian, fi.ModTime().UnixNano())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error writing mtime")
|
||||
}
|
||||
stat, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if ok {
|
||||
// take device and inode into account
|
||||
err = binary.Write(h, binary.BigEndian, stat.Ino)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error writing inode")
|
||||
}
|
||||
err = binary.Write(h, binary.BigEndian, stat.Dev)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error writing device")
|
||||
}
|
||||
}
|
||||
err = binary.Write(h, binary.BigEndian, fi.Size())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error writing size")
|
||||
}
|
||||
etag := fmt.Sprintf("%x", h.Sum(nil))
|
||||
return strings.Trim(etag, "\"")
|
||||
}
|
||||
|
||||
func (fs *owncloudsqlfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, uint64, error) {
|
||||
// TODO quota of which storage space?
|
||||
// we could use the logged in user, but when a user has access to multiple storages this falls short
|
||||
// for now return quota of root
|
||||
stat := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(fs.toInternalPath(ctx, "/"), &stat)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
// Total data blocks in filesystem
|
||||
total := stat.Blocks * uint64(stat.Bsize)
|
||||
// Free blocks available to unprivileged user
|
||||
used := (stat.Blocks - uint64(stat.Bavail)) * uint64(stat.Bsize) //nolint:unconvert
|
||||
remaining := total - used
|
||||
return total, used, remaining, nil
|
||||
}
|
||||
76
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql_windows.go
generated
vendored
76
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql_windows.go
generated
vendored
@@ -1,76 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/appctx"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// calcEtag will create an etag based on the md5 of
|
||||
// - mtime,
|
||||
// - inode (if available),
|
||||
// - device (if available) and
|
||||
// - size.
|
||||
// errors are logged, but an etag will still be returned
|
||||
func calcEtag(ctx context.Context, fi os.FileInfo) string {
|
||||
log := appctx.GetLogger(ctx)
|
||||
h := md5.New()
|
||||
err := binary.Write(h, binary.BigEndian, fi.ModTime().UnixNano())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error writing mtime")
|
||||
}
|
||||
// device and inode have no meaning on windows
|
||||
err = binary.Write(h, binary.BigEndian, fi.Size())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error writing size")
|
||||
}
|
||||
etag := fmt.Sprintf(`"%x"`, h.Sum(nil))
|
||||
return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\""))
|
||||
}
|
||||
|
||||
func (fs *owncloudsqlfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, uint64, error) {
|
||||
// TODO quota of which storage space?
|
||||
// we could use the logged in user, but when a user has access to multiple storages this falls short
|
||||
// for now return quota of root
|
||||
var free, total, avail uint64
|
||||
|
||||
pathPtr, err := windows.UTF16PtrFromString(fs.toInternalPath(ctx, "/"))
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
err = windows.GetDiskFreeSpaceEx(pathPtr, &avail, &total, &free)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
used := total - free
|
||||
return total, used, free, nil
|
||||
}
|
||||
193
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/spaces.go
generated
vendored
193
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/spaces.go
generated
vendored
@@ -1,193 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
ctxpkg "github.com/owncloud/reva/v2/pkg/ctx"
|
||||
"github.com/owncloud/reva/v2/pkg/errtypes"
|
||||
"github.com/owncloud/reva/v2/pkg/storage"
|
||||
"github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/filecache"
|
||||
"github.com/owncloud/reva/v2/pkg/storagespace"
|
||||
"github.com/owncloud/reva/v2/pkg/utils"
|
||||
)
|
||||
|
||||
// ListStorageSpaces lists storage spaces according to the provided filters
|
||||
func (fs *owncloudsqlfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, unrestricted bool) ([]*provider.StorageSpace, error) {
|
||||
var (
|
||||
spaceID = "*"
|
||||
// uid *userpb.UserId
|
||||
)
|
||||
|
||||
filteringUnsupportedSpaceTypes := false
|
||||
|
||||
for i := range filter {
|
||||
switch filter[i].Type {
|
||||
case provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE:
|
||||
t := filter[i].GetSpaceType()
|
||||
filteringUnsupportedSpaceTypes = (t != "personal" && !strings.HasPrefix(t, "+"))
|
||||
case provider.ListStorageSpacesRequest_Filter_TYPE_ID:
|
||||
_, spaceID, _, _ = storagespace.SplitID(filter[i].GetId().OpaqueId)
|
||||
case provider.ListStorageSpacesRequest_Filter_TYPE_USER:
|
||||
// uid = filter[i].GetUser()
|
||||
}
|
||||
}
|
||||
if filteringUnsupportedSpaceTypes {
|
||||
// owncloudsql only supports personal spaces, no need to search for something else
|
||||
return []*provider.StorageSpace{}, nil
|
||||
}
|
||||
|
||||
spaces := []*provider.StorageSpace{}
|
||||
if spaceID == "*" {
|
||||
u, ok := ctxpkg.ContextGetUser(ctx)
|
||||
if !ok {
|
||||
return nil, errtypes.UserRequired("error getting user from context")
|
||||
}
|
||||
// if uid != nil && utils.UserIDEqual(uid, u.Id) {
|
||||
// return nil, errtypes.PermissionDenied("cannot access space of other user?")
|
||||
// }
|
||||
space, err := fs.getPersonalSpace(ctx, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spaces = append(spaces, space)
|
||||
} else {
|
||||
id, err := strconv.Atoi(spaceID)
|
||||
if err != nil {
|
||||
// non-numeric space id -> this request is not for us
|
||||
return []*provider.StorageSpace{}, nil
|
||||
}
|
||||
space, err := fs.getSpaceByNumericID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spaces = append(spaces, space)
|
||||
}
|
||||
return spaces, nil
|
||||
}
|
||||
|
||||
// CreateStorageSpace creates a storage space
|
||||
func (fs *owncloudsqlfs) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) {
|
||||
return nil, errtypes.NotSupported("unimplemented: CreateStorageSpace")
|
||||
}
|
||||
|
||||
// UpdateStorageSpace updates a storage space
|
||||
func (fs *owncloudsqlfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) {
|
||||
return nil, errtypes.NotSupported("update storage space")
|
||||
}
|
||||
|
||||
// DeleteStorageSpace deletes a storage space
|
||||
func (fs *owncloudsqlfs) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*storage.DeleteStorageSpaceResult, error) {
|
||||
return nil, errtypes.NotSupported("delete storage space")
|
||||
}
|
||||
|
||||
// Note: currently unused but will be used later
|
||||
// func (fs *owncloudsqlfs) listAllPersonalSpaces(ctx context.Context) ([]*provider.StorageSpace, error) {
|
||||
// storages, err := fs.filecache.ListStorages(true)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// spaces := []*provider.StorageSpace{}
|
||||
// for _, storage := range storages {
|
||||
// space, err := fs.storageToSpace(ctx, storage)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// spaces = append(spaces, space)
|
||||
// }
|
||||
// return spaces, nil
|
||||
// }
|
||||
|
||||
func (fs *owncloudsqlfs) getPersonalSpace(ctx context.Context, owner *userpb.User) (*provider.StorageSpace, error) {
|
||||
storageID, err := fs.filecache.GetNumericStorageID(ctx, "home::"+owner.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storage, err := fs.filecache.GetStorage(ctx, storageID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err := fs.filecache.Get(ctx, storage.NumericID, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
space := &provider.StorageSpace{
|
||||
Id: &provider.StorageSpaceId{OpaqueId: strconv.Itoa(storage.NumericID)},
|
||||
Root: &provider.ResourceId{
|
||||
// return ownclouds numeric storage id as the space id!
|
||||
SpaceId: strconv.Itoa(storage.NumericID),
|
||||
OpaqueId: strconv.Itoa(root.ID),
|
||||
},
|
||||
Name: owner.Username,
|
||||
SpaceType: "personal",
|
||||
Mtime: &types.Timestamp{Seconds: uint64(root.MTime)},
|
||||
Owner: owner,
|
||||
}
|
||||
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "spaceAlias", "personal/"+owner.Username)
|
||||
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "etag", fmt.Sprintf(`"%s"`, root.Etag))
|
||||
return space, nil
|
||||
}
|
||||
|
||||
func (fs *owncloudsqlfs) getSpaceByNumericID(ctx context.Context, spaceID int) (*provider.StorageSpace, error) {
|
||||
storage, err := fs.filecache.GetStorage(ctx, spaceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !strings.HasPrefix(storage.ID, "home::") {
|
||||
return nil, fmt.Errorf("only personal spaces are supported")
|
||||
}
|
||||
|
||||
return fs.storageToSpace(ctx, storage)
|
||||
}
|
||||
|
||||
func (fs *owncloudsqlfs) storageToSpace(ctx context.Context, storage *filecache.Storage) (*provider.StorageSpace, error) {
|
||||
root, err := fs.filecache.Get(ctx, storage.NumericID, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ownerName := strings.TrimPrefix(storage.ID, "home::")
|
||||
owner, err := fs.getUser(ctx, ownerName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
space := &provider.StorageSpace{
|
||||
Id: &provider.StorageSpaceId{OpaqueId: strconv.Itoa(storage.NumericID)},
|
||||
Root: &provider.ResourceId{
|
||||
// return ownclouds numeric storage id as the space id!
|
||||
SpaceId: strconv.Itoa(storage.NumericID),
|
||||
OpaqueId: strconv.Itoa(root.ID),
|
||||
},
|
||||
Name: owner.Username,
|
||||
SpaceType: "personal",
|
||||
Mtime: &types.Timestamp{Seconds: uint64(root.MTime)},
|
||||
Owner: owner,
|
||||
}
|
||||
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "spaceAlias", "personal/"+owner.Username)
|
||||
space.Opaque = utils.AppendPlainToOpaque(space.Opaque, "etag", fmt.Sprintf(`"%s"`, root.Etag))
|
||||
return space, nil
|
||||
}
|
||||
43
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/upload.go
generated
vendored
43
vendor/github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/upload.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/errtypes"
|
||||
"github.com/owncloud/reva/v2/pkg/storage"
|
||||
)
|
||||
|
||||
func (fs *owncloudsqlfs) MarkProcessing(ctx context.Context, ref *provider.Reference, processing bool, sessionID string) error {
|
||||
return errtypes.NotSupported("op not supported")
|
||||
}
|
||||
|
||||
func (fs *owncloudsqlfs) CommitUpload(_ context.Context, _ *provider.Reference, _ string, _ storage.UploadSource) error {
|
||||
return errtypes.NotSupported("op not supported")
|
||||
}
|
||||
|
||||
func (fs *owncloudsqlfs) PrepareUpload(_ context.Context, _ *provider.Reference, _ string, info storage.UploadInfo) (*storage.PrepareUploadResult, error) {
|
||||
return &storage.PrepareUploadResult{VersionCreated: info.NodeExisted}, nil
|
||||
}
|
||||
|
||||
func (fs *owncloudsqlfs) RollbackUpload(_ context.Context, _ *provider.Reference, _ string, _ storage.RollbackInfo) error {
|
||||
return nil
|
||||
}
|
||||
1
vendor/github.com/owncloud/reva/v2/pkg/user/manager/loader/loader.go
generated
vendored
1
vendor/github.com/owncloud/reva/v2/pkg/user/manager/loader/loader.go
generated
vendored
@@ -25,6 +25,5 @@ import (
|
||||
_ "github.com/owncloud/reva/v2/pkg/user/manager/ldap"
|
||||
_ "github.com/owncloud/reva/v2/pkg/user/manager/memory"
|
||||
_ "github.com/owncloud/reva/v2/pkg/user/manager/nextcloud"
|
||||
_ "github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql"
|
||||
// Add your own here
|
||||
)
|
||||
|
||||
228
vendor/github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/accounts/accounts.go
generated
vendored
228
vendor/github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/accounts/accounts.go
generated
vendored
@@ -1,228 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/owncloud/reva/v2/pkg/appctx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Accounts represents oc10-style Accounts
|
||||
type Accounts struct {
|
||||
driver string
|
||||
db *sql.DB
|
||||
joinUsername, joinUUID, enableMedialSearch bool
|
||||
selectSQL string
|
||||
}
|
||||
|
||||
// NewMysql returns a new Cache instance connecting to a MySQL database
|
||||
func NewMysql(dsn string, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
|
||||
sqldb, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
// FIXME make configurable
|
||||
sqldb.SetConnMaxLifetime(time.Minute * 3)
|
||||
sqldb.SetConnMaxIdleTime(time.Second * 30)
|
||||
sqldb.SetMaxOpenConns(100)
|
||||
sqldb.SetMaxIdleConns(10)
|
||||
|
||||
err = sqldb.Ping()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
return New("mysql", sqldb, joinUsername, joinUUID, enableMedialSearch)
|
||||
}
|
||||
|
||||
// New returns a new Cache instance connecting to the given sql.DB
|
||||
func New(driver string, sqldb *sql.DB, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
|
||||
|
||||
sel := "SELECT id, email, user_id, display_name, quota, last_login, backend, home, state"
|
||||
from := `
|
||||
FROM oc_accounts a
|
||||
`
|
||||
if joinUsername {
|
||||
sel += ", p.configvalue AS username"
|
||||
from += `LEFT JOIN oc_preferences p
|
||||
ON a.user_id=p.userid
|
||||
AND p.appid='core'
|
||||
AND p.configkey='username'`
|
||||
} else {
|
||||
// fallback to user_id as username
|
||||
sel += ", user_id AS username"
|
||||
}
|
||||
if joinUUID {
|
||||
sel += ", p2.configvalue AS ownclouduuid"
|
||||
from += `LEFT JOIN oc_preferences p2
|
||||
ON a.user_id=p2.userid
|
||||
AND p2.appid='core'
|
||||
AND p2.configkey='ownclouduuid'`
|
||||
} else {
|
||||
// fallback to user_id as ownclouduuid
|
||||
sel += ", user_id AS ownclouduuid"
|
||||
}
|
||||
|
||||
return &Accounts{
|
||||
driver: driver,
|
||||
db: sqldb,
|
||||
joinUsername: joinUsername,
|
||||
joinUUID: joinUUID,
|
||||
enableMedialSearch: enableMedialSearch,
|
||||
selectSQL: sel + from,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Account stores information about accounts.
|
||||
type Account struct {
|
||||
ID uint64
|
||||
Email sql.NullString
|
||||
UserID string
|
||||
DisplayName sql.NullString
|
||||
Quota sql.NullString
|
||||
LastLogin int
|
||||
Backend string
|
||||
Home string
|
||||
State int8
|
||||
Username sql.NullString // optional comes from the oc_preferences
|
||||
OwnCloudUUID sql.NullString // optional comes from the oc_preferences
|
||||
}
|
||||
|
||||
func (as *Accounts) rowToAccount(ctx context.Context, row Scannable) (*Account, error) {
|
||||
a := Account{}
|
||||
if err := row.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.Username, &a.OwnCloudUUID); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
// Scannable describes the interface providing a Scan method
|
||||
type Scannable interface {
|
||||
Scan(...interface{}) error
|
||||
}
|
||||
|
||||
// GetAccountByClaim fetches an account by mail, username or userid
|
||||
func (as *Accounts) GetAccountByClaim(ctx context.Context, claim, value string) (*Account, error) {
|
||||
// TODO align supported claims with rest driver and the others, maybe refactor into common mapping
|
||||
var row *sql.Row
|
||||
var where string
|
||||
switch claim {
|
||||
case "mail":
|
||||
where = "WHERE a.email=?"
|
||||
// case "uid":
|
||||
// claim = m.c.Schema.UIDNumber
|
||||
// case "gid":
|
||||
// claim = m.c.Schema.GIDNumber
|
||||
case "username":
|
||||
if as.joinUsername {
|
||||
where = "WHERE p.configvalue=?"
|
||||
} else {
|
||||
// use user_id as username
|
||||
where = "WHERE a.user_id=?"
|
||||
}
|
||||
case "userid":
|
||||
if as.joinUUID {
|
||||
where = "WHERE p2.configvalue=?"
|
||||
} else {
|
||||
// use user_id as uuid
|
||||
where = "WHERE a.user_id=?"
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("owncloudsql: invalid field " + claim)
|
||||
}
|
||||
|
||||
row = as.db.QueryRowContext(ctx, as.selectSQL+where, value)
|
||||
|
||||
return as.rowToAccount(ctx, row)
|
||||
}
|
||||
|
||||
func sanitizeWildcards(q string) string {
|
||||
return strings.ReplaceAll(strings.ReplaceAll(q, "%", `\%`), "_", `\_`)
|
||||
}
|
||||
|
||||
// FindAccounts searches userid, displayname and email using the given query. The Wildcard caracters % and _ are escaped.
|
||||
func (as *Accounts) FindAccounts(ctx context.Context, query string) ([]Account, error) {
|
||||
if as.enableMedialSearch {
|
||||
query = "%" + sanitizeWildcards(query) + "%"
|
||||
}
|
||||
// TODO join oc_account_terms
|
||||
where := "WHERE a.user_id LIKE ? OR a.display_name LIKE ? OR a.email LIKE ?"
|
||||
args := []interface{}{query, query, query}
|
||||
|
||||
if as.joinUsername {
|
||||
where += " OR p.configvalue LIKE ?"
|
||||
args = append(args, query)
|
||||
}
|
||||
if as.joinUUID {
|
||||
where += " OR p2.configvalue LIKE ?"
|
||||
args = append(args, query)
|
||||
}
|
||||
|
||||
rows, err := as.db.QueryContext(ctx, as.selectSQL+where, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
accounts := []Account{}
|
||||
for rows.Next() {
|
||||
a := Account{}
|
||||
if err := rows.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.Username, &a.OwnCloudUUID); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
continue
|
||||
}
|
||||
accounts = append(accounts, a)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// GetAccountGroups lasts the groups for an account
|
||||
func (as *Accounts) GetAccountGroups(ctx context.Context, uid string) ([]string, error) {
|
||||
rows, err := as.db.QueryContext(ctx, "SELECT gid FROM oc_group_user WHERE uid=?", uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
groups := []string{}
|
||||
for rows.Next() {
|
||||
var group string
|
||||
if err := rows.Scan(&group); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
continue
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
BIN
vendor/github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/accounts/test.sqlite
generated
vendored
BIN
vendor/github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/accounts/test.sqlite
generated
vendored
Binary file not shown.
189
vendor/github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go
generated
vendored
189
vendor/github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go
generated
vendored
@@ -1,189 +0,0 @@
|
||||
// Copyright 2018-2021 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/owncloud/reva/v2/pkg/appctx"
|
||||
"github.com/owncloud/reva/v2/pkg/errtypes"
|
||||
"github.com/owncloud/reva/v2/pkg/user"
|
||||
"github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/accounts"
|
||||
"github.com/owncloud/reva/v2/pkg/user/manager/registry"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
// Provides mysql drivers
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("owncloudsql", NewMysql)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
c *config
|
||||
db *accounts.Accounts
|
||||
}
|
||||
|
||||
type config struct {
|
||||
DbUsername string `mapstructure:"dbusername"`
|
||||
DbPassword string `mapstructure:"dbpassword"`
|
||||
DbHost string `mapstructure:"dbhost"`
|
||||
DbPort int `mapstructure:"dbport"`
|
||||
DbName string `mapstructure:"dbname"`
|
||||
Idp string `mapstructure:"idp"`
|
||||
Nobody int64 `mapstructure:"nobody"`
|
||||
JoinUsername bool `mapstructure:"join_username"`
|
||||
JoinOwnCloudUUID bool `mapstructure:"join_ownclouduuid"`
|
||||
EnableMedialSearch bool `mapstructure:"enable_medial_search"`
|
||||
}
|
||||
|
||||
// NewMysql returns a new user manager connection to an owncloud mysql database
|
||||
func NewMysql(m map[string]interface{}) (user.Manager, error) {
|
||||
mgr := &manager{}
|
||||
err := mgr.Configure(m)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error creating a new manager")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mgr.db, err = accounts.NewMysql(
|
||||
fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", mgr.c.DbUsername, mgr.c.DbPassword, mgr.c.DbHost, mgr.c.DbPort, mgr.c.DbName),
|
||||
mgr.c.JoinUsername,
|
||||
mgr.c.JoinOwnCloudUUID,
|
||||
mgr.c.EnableMedialSearch,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
func (m *manager) Configure(ml map[string]interface{}) error {
|
||||
c, err := parseConfig(ml)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Nobody == 0 {
|
||||
c.Nobody = 99
|
||||
}
|
||||
|
||||
m.c = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseConfig(m map[string]interface{}) (*config, error) {
|
||||
c := &config{}
|
||||
if err := mapstructure.Decode(m, &c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
// search via the user_id
|
||||
a, err := m.db.GetAccountByClaim(ctx, "userid", uid.OpaqueId)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(uid.OpaqueId)
|
||||
}
|
||||
return m.convertToCS3User(ctx, a, skipFetchingGroups)
|
||||
}
|
||||
|
||||
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
a, err := m.db.GetAccountByClaim(ctx, claim, value)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(claim + "=" + value)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.convertToCS3User(ctx, a, skipFetchingGroups)
|
||||
}
|
||||
|
||||
func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) {
|
||||
|
||||
accounts, err := m.db.FindAccounts(ctx, query)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound("no users found for " + query)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]*userpb.User, 0, len(accounts))
|
||||
for i := range accounts {
|
||||
u, err := m.convertToCS3User(ctx, &accounts[i], skipFetchingGroups)
|
||||
if err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Interface("account", accounts[i]).Msg("could not convert account, skipping")
|
||||
continue
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) {
|
||||
groups, err := m.db.GetAccountGroups(ctx, uid.OpaqueId)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound("no groups found for uid " + uid.OpaqueId)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (m *manager) convertToCS3User(ctx context.Context, a *accounts.Account, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
u := &userpb.User{
|
||||
Id: &userpb.UserId{
|
||||
Idp: m.c.Idp,
|
||||
OpaqueId: a.OwnCloudUUID.String,
|
||||
Type: userpb.UserType_USER_TYPE_PRIMARY,
|
||||
},
|
||||
Username: a.Username.String,
|
||||
Mail: a.Email.String,
|
||||
DisplayName: a.DisplayName.String,
|
||||
//Groups: groups,
|
||||
GidNumber: m.c.Nobody,
|
||||
UidNumber: m.c.Nobody,
|
||||
}
|
||||
// https://github.com/owncloud/reva/pull/4135
|
||||
// fall back to userid
|
||||
if u.Id.OpaqueId == "" {
|
||||
u.Id.OpaqueId = a.UserID
|
||||
}
|
||||
if u.Username == "" {
|
||||
u.Username = u.Id.OpaqueId
|
||||
}
|
||||
if u.DisplayName == "" {
|
||||
u.DisplayName = u.Id.OpaqueId
|
||||
}
|
||||
|
||||
if !skipFetchingGroups {
|
||||
var err error
|
||||
if u.Groups, err = m.GetUserGroups(ctx, u.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
126
vendor/github.com/prometheus/procfs/internal/util/parse.go
generated
vendored
126
vendor/github.com/prometheus/procfs/internal/util/parse.go
generated
vendored
@@ -1,126 +0,0 @@
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseUint32s parses a slice of strings into a slice of uint32s.
|
||||
func ParseUint32s(ss []string) ([]uint32, error) {
|
||||
us := make([]uint32, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
u, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
us = append(us, uint32(u))
|
||||
}
|
||||
|
||||
return us, nil
|
||||
}
|
||||
|
||||
// ParseUint64s parses a slice of strings into a slice of uint64s.
|
||||
func ParseUint64s(ss []string) ([]uint64, error) {
|
||||
us := make([]uint64, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
u, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
us = append(us, u)
|
||||
}
|
||||
|
||||
return us, nil
|
||||
}
|
||||
|
||||
// ParsePInt64s parses a slice of strings into a slice of int64 pointers.
|
||||
func ParsePInt64s(ss []string) ([]*int64, error) {
|
||||
us := make([]*int64, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
u, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
us = append(us, &u)
|
||||
}
|
||||
|
||||
return us, nil
|
||||
}
|
||||
|
||||
// Parses a uint64 from given hex in string.
|
||||
func ParseHexUint64s(ss []string) ([]*uint64, error) {
|
||||
us := make([]*uint64, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
u, err := strconv.ParseUint(s, 16, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
us = append(us, &u)
|
||||
}
|
||||
|
||||
return us, nil
|
||||
}
|
||||
|
||||
// ReadUintFromFile reads a file and attempts to parse a uint64 from it.
|
||||
func ReadUintFromFile(path string) (uint64, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
|
||||
}
|
||||
|
||||
// ReadIntFromFile reads a file and attempts to parse a int64 from it.
|
||||
func ReadIntFromFile(path string) (int64, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
|
||||
}
|
||||
|
||||
// ParseBool parses a string into a boolean pointer.
|
||||
func ParseBool(b string) *bool {
|
||||
var truth bool
|
||||
switch b {
|
||||
case "enabled":
|
||||
truth = true
|
||||
case "disabled":
|
||||
truth = false
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return &truth
|
||||
}
|
||||
|
||||
// ReadHexFromFile reads a file and attempts to parse a uint64 from a hexadecimal format 0xXX.
|
||||
func ReadHexFromFile(path string) (uint64, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
hexString := strings.TrimSpace(string(data))
|
||||
if !strings.HasPrefix(hexString, "0x") {
|
||||
return 0, errors.New("invalid format: hex string does not start with '0x'")
|
||||
}
|
||||
return strconv.ParseUint(hexString[2:], 16, 64)
|
||||
}
|
||||
37
vendor/github.com/prometheus/procfs/internal/util/readfile.go
generated
vendored
37
vendor/github.com/prometheus/procfs/internal/util/readfile.go
generated
vendored
@@ -1,37 +0,0 @@
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ReadFileNoStat uses io.ReadAll to read contents of entire file.
|
||||
// This is similar to os.ReadFile but without the call to os.Stat, because
|
||||
// many files in /proc and /sys report incorrect file sizes (either 0 or 4096).
|
||||
// Reads a max file size of 1024kB. For files larger than this, a scanner
|
||||
// should be used.
|
||||
func ReadFileNoStat(filename string) ([]byte, error) {
|
||||
const maxBufferSize = 1024 * 1024
|
||||
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
reader := io.LimitReader(f, maxBufferSize)
|
||||
return io.ReadAll(reader)
|
||||
}
|
||||
68
vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go
generated
vendored
68
vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go
generated
vendored
@@ -1,68 +0,0 @@
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build (linux || darwin) && !appengine
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// SysReadFile is a simplified os.ReadFile that invokes syscall.Read directly.
|
||||
// https://github.com/prometheus/node_exporter/pull/728/files
|
||||
//
|
||||
// Note that this function will not read files larger than 128 bytes.
|
||||
func SysReadFile(file string) (string, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// On some machines, hwmon drivers are broken and return EAGAIN. This causes
|
||||
// Go's os.ReadFile implementation to poll forever.
|
||||
//
|
||||
// Since we either want to read data or bail immediately, do the simplest
|
||||
// possible read using syscall directly.
|
||||
const sysFileBufferSize = 128
|
||||
b := make([]byte, sysFileBufferSize)
|
||||
n, err := syscall.Read(int(f.Fd()), b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bytes.TrimSpace(b[:n])), nil
|
||||
}
|
||||
|
||||
// SysReadUintFromFile reads a file using SysReadFile and attempts to parse a uint64 from it.
|
||||
func SysReadUintFromFile(path string) (uint64, error) {
|
||||
data, err := SysReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
|
||||
}
|
||||
|
||||
// SysReadIntFromFile reads a file using SysReadFile and attempts to parse a int64 from it.
|
||||
func SysReadIntFromFile(path string) (int64, error) {
|
||||
data, err := SysReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
|
||||
}
|
||||
26
vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go
generated
vendored
26
vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build (linux && appengine) || (!linux && !darwin)
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SysReadFile is here implemented as a noop for builds that do not support
|
||||
// the read syscall. For example Windows, or Linux on Google App Engine.
|
||||
func SysReadFile(file string) (string, error) {
|
||||
return "", fmt.Errorf("not supported on this platform")
|
||||
}
|
||||
91
vendor/github.com/prometheus/procfs/internal/util/valueparser.go
generated
vendored
91
vendor/github.com/prometheus/procfs/internal/util/valueparser.go
generated
vendored
@@ -1,91 +0,0 @@
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TODO(mdlayher): util packages are an anti-pattern and this should be moved
|
||||
// somewhere else that is more focused in the future.
|
||||
|
||||
// A ValueParser enables parsing a single string into a variety of data types
|
||||
// in a concise and safe way. The Err method must be invoked after invoking
|
||||
// any other methods to ensure a value was successfully parsed.
|
||||
type ValueParser struct {
|
||||
v string
|
||||
err error
|
||||
}
|
||||
|
||||
// NewValueParser creates a ValueParser using the input string.
|
||||
func NewValueParser(v string) *ValueParser {
|
||||
return &ValueParser{v: v}
|
||||
}
|
||||
|
||||
// Int interprets the underlying value as an int and returns that value.
|
||||
func (vp *ValueParser) Int() int { return int(vp.int64()) }
|
||||
|
||||
// PInt64 interprets the underlying value as an int64 and returns a pointer to
|
||||
// that value.
|
||||
func (vp *ValueParser) PInt64() *int64 {
|
||||
if vp.err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := vp.int64()
|
||||
return &v
|
||||
}
|
||||
|
||||
// int64 interprets the underlying value as an int64 and returns that value.
|
||||
// TODO: export if/when necessary.
|
||||
func (vp *ValueParser) int64() int64 {
|
||||
if vp.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// A base value of zero makes ParseInt infer the correct base using the
|
||||
// string's prefix, if any.
|
||||
const base = 0
|
||||
v, err := strconv.ParseInt(vp.v, base, 64)
|
||||
if err != nil {
|
||||
vp.err = err
|
||||
return 0
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// PUInt64 interprets the underlying value as an uint64 and returns a pointer to
|
||||
// that value.
|
||||
func (vp *ValueParser) PUInt64() *uint64 {
|
||||
if vp.err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// A base value of zero makes ParseInt infer the correct base using the
|
||||
// string's prefix, if any.
|
||||
const base = 0
|
||||
v, err := strconv.ParseUint(vp.v, base, 64)
|
||||
if err != nil {
|
||||
vp.err = err
|
||||
return nil
|
||||
}
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
// Err returns the last error, if any, encountered by the ValueParser.
|
||||
func (vp *ValueParser) Err() error {
|
||||
return vp.err
|
||||
}
|
||||
10
vendor/modules.txt
vendored
10
vendor/modules.txt
vendored
@@ -1321,7 +1321,7 @@ github.com/orcaman/concurrent-map
|
||||
# github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245
|
||||
## explicit; go 1.18
|
||||
github.com/owncloud/libre-graph-api-go
|
||||
# github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899
|
||||
# github.com/owncloud/reva/v2 v2.0.0-20261019155447-90c24c1b3575
|
||||
## explicit; go 1.26.0
|
||||
github.com/owncloud/reva/v2/cmd/revad/internal/grace
|
||||
github.com/owncloud/reva/v2/cmd/revad/runtime
|
||||
@@ -1439,8 +1439,6 @@ github.com/owncloud/reva/v2/pkg/auth/manager/machine
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/nextcloud
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/ocmshares
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/oidc
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/owncloudsql/accounts
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/publicshares
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/registry
|
||||
github.com/owncloud/reva/v2/pkg/auth/manager/serviceaccounts
|
||||
@@ -1546,7 +1544,6 @@ github.com/owncloud/reva/v2/pkg/publicshare/manager/json/persistence/file
|
||||
github.com/owncloud/reva/v2/pkg/publicshare/manager/json/persistence/memory
|
||||
github.com/owncloud/reva/v2/pkg/publicshare/manager/loader
|
||||
github.com/owncloud/reva/v2/pkg/publicshare/manager/memory
|
||||
github.com/owncloud/reva/v2/pkg/publicshare/manager/owncloudsql
|
||||
github.com/owncloud/reva/v2/pkg/publicshare/manager/registry
|
||||
github.com/owncloud/reva/v2/pkg/registry
|
||||
github.com/owncloud/reva/v2/pkg/rgrpc
|
||||
@@ -1578,7 +1575,6 @@ github.com/owncloud/reva/v2/pkg/share/manager/jsoncs3/sharecache
|
||||
github.com/owncloud/reva/v2/pkg/share/manager/jsoncs3/shareid
|
||||
github.com/owncloud/reva/v2/pkg/share/manager/loader
|
||||
github.com/owncloud/reva/v2/pkg/share/manager/memory
|
||||
github.com/owncloud/reva/v2/pkg/share/manager/owncloudsql
|
||||
github.com/owncloud/reva/v2/pkg/share/manager/registry
|
||||
github.com/owncloud/reva/v2/pkg/sharedconf
|
||||
github.com/owncloud/reva/v2/pkg/siteacc
|
||||
@@ -1621,8 +1617,6 @@ github.com/owncloud/reva/v2/pkg/storage/fs/localhome
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/nextcloud
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/ocis
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/ocis/blobstore
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/owncloudsql/filecache
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/posix
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/posix/blobstore
|
||||
github.com/owncloud/reva/v2/pkg/storage/fs/posix/lookup
|
||||
@@ -1691,8 +1685,6 @@ github.com/owncloud/reva/v2/pkg/user/manager/ldap
|
||||
github.com/owncloud/reva/v2/pkg/user/manager/loader
|
||||
github.com/owncloud/reva/v2/pkg/user/manager/memory
|
||||
github.com/owncloud/reva/v2/pkg/user/manager/nextcloud
|
||||
github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql
|
||||
github.com/owncloud/reva/v2/pkg/user/manager/owncloudsql/accounts
|
||||
github.com/owncloud/reva/v2/pkg/user/manager/registry
|
||||
github.com/owncloud/reva/v2/pkg/utils
|
||||
github.com/owncloud/reva/v2/pkg/utils/cfg
|
||||
|
||||
Reference in New Issue
Block a user