Bump reva
This commit is contained in:
@@ -60,10 +60,10 @@ require (
|
|||||||
github.com/oklog/run v1.1.0
|
github.com/oklog/run v1.1.0
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/ginkgo/v2 v2.23.0
|
github.com/onsi/ginkgo/v2 v2.23.1
|
||||||
github.com/onsi/gomega v1.36.2
|
github.com/onsi/gomega v1.36.2
|
||||||
github.com/open-policy-agent/opa v1.2.0
|
github.com/open-policy-agent/opa v1.2.0
|
||||||
github.com/opencloud-eu/reva/v2 v2.28.1-0.20250319144557-ae6d4d54cb01
|
github.com/opencloud-eu/reva/v2 v2.28.1-0.20250320105919-be91238e6b11
|
||||||
github.com/orcaman/concurrent-map v1.0.0
|
github.com/orcaman/concurrent-map v1.0.0
|
||||||
github.com/owncloud/libre-graph-api-go v1.0.5-0.20240829135935-80dc00d6f5ea
|
github.com/owncloud/libre-graph-api-go v1.0.5-0.20240829135935-80dc00d6f5ea
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
@@ -287,6 +287,7 @@ require (
|
|||||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||||
github.com/sercand/kuberesolver/v5 v5.1.1 // indirect
|
github.com/sercand/kuberesolver/v5 v5.1.1 // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
|
github.com/sethvargo/go-diceware v0.5.0 // indirect
|
||||||
github.com/sethvargo/go-password v0.3.1 // indirect
|
github.com/sethvargo/go-password v0.3.1 // indirect
|
||||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
|
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
|
||||||
|
|||||||
@@ -852,8 +852,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ=
|
github.com/onsi/ginkgo/v2 v2.23.1 h1:Ox0cOPv/t8RzKJUfDo9ZKtRvBOJY369sFJnl00CjqwY=
|
||||||
github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
github.com/onsi/ginkgo/v2 v2.23.1/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
@@ -861,8 +861,8 @@ github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
|||||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||||
github.com/open-policy-agent/opa v1.2.0 h1:88NDVCM0of1eO6Z4AFeL3utTEtMuwloFmWWU7dRV1z0=
|
github.com/open-policy-agent/opa v1.2.0 h1:88NDVCM0of1eO6Z4AFeL3utTEtMuwloFmWWU7dRV1z0=
|
||||||
github.com/open-policy-agent/opa v1.2.0/go.mod h1:30euUmOvuBoebRCcJ7DMF42bRBOPznvt0ACUMYDUGVY=
|
github.com/open-policy-agent/opa v1.2.0/go.mod h1:30euUmOvuBoebRCcJ7DMF42bRBOPznvt0ACUMYDUGVY=
|
||||||
github.com/opencloud-eu/reva/v2 v2.28.1-0.20250319144557-ae6d4d54cb01 h1:XhxHB2APcLCFaYFP1AEXwPSld6aUyK4PgGkT+AvxVGI=
|
github.com/opencloud-eu/reva/v2 v2.28.1-0.20250320105919-be91238e6b11 h1:MjfgrhEs73BezOXQZUgEtNTZsmXDVixFpGzZljR5lrk=
|
||||||
github.com/opencloud-eu/reva/v2 v2.28.1-0.20250319144557-ae6d4d54cb01/go.mod h1:IWorhegiAG25pT0L3tbXeTqP70a8ALJNBOXa4t4QG14=
|
github.com/opencloud-eu/reva/v2 v2.28.1-0.20250320105919-be91238e6b11/go.mod h1:iK0tNdLgqK0zBi0l7Q4uWSn9GPUbYtNxz3YAMfYvYNg=
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
@@ -1000,6 +1000,8 @@ github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aep
|
|||||||
github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ=
|
github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
|
github.com/sethvargo/go-diceware v0.5.0 h1:exrQ7GpaBo00GqRVM1N8ChXSsi3oS7tjQiIehsD+yR0=
|
||||||
|
github.com/sethvargo/go-diceware v0.5.0/go.mod h1:Lg1SyPS7yQO6BBgTN5r4f2MUDkqGfLWsOjHPY0kA8iw=
|
||||||
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
|
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
|
||||||
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
|
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
|
||||||
github.com/shamaton/msgpack/v2 v2.2.3 h1:uDOHmxQySlvlUYfQwdjxyybAOzjlQsD1Vjy+4jmO9NM=
|
github.com/shamaton/msgpack/v2 v2.2.3 h1:uDOHmxQySlvlUYfQwdjxyybAOzjlQsD1Vjy+4jmO9NM=
|
||||||
|
|||||||
+15
@@ -1,3 +1,18 @@
|
|||||||
|
## 2.23.1
|
||||||
|
|
||||||
|
## 🚨 For users on MacOS 🚨
|
||||||
|
|
||||||
|
A long-standing Ginkgo performance issue on MacOS seems to be due to mac's antimalware XProtect. You can follow the instructions [here](https://onsi.github.io/ginkgo/#if-you-are-running-on-macos) to disable it in your terminal. Doing so sped up Ginkgo's own test suite from 1m8s to 47s.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
Ginkgo's CLI is now a bit clearer if you pass flags in incorrectly:
|
||||||
|
|
||||||
|
- make it clearer that you need to pass a filename to the various profile flags, not an absolute directory [a0e52ff]
|
||||||
|
- emit an error and exit if the ginkgo invocation includes flags after positional arguments [b799d8d]
|
||||||
|
|
||||||
|
This might cause existing CI builds to fail. If so then it's likely that your CI build was misconfigured and should be corrected. Open an issue if you need help.
|
||||||
|
|
||||||
## 2.23.0
|
## 2.23.0
|
||||||
|
|
||||||
Ginkgo 2.23.0 adds a handful of methods to `GinkgoT()` to make it compatible with the `testing.TB` interface in Go 1.24. `GinkgoT().Context()`, in particular, is a useful shorthand for generating a new context that will clean itself up in a `DeferCleanup()`. This has subtle behavior differences from the golang implementation but should make sense in a Ginkgo... um... context.
|
Ginkgo 2.23.0 adds a handful of methods to `GinkgoT()` to make it compatible with the `testing.TB` interface in Go 1.24. `GinkgoT().Context()`, in particular, is a useful shorthand for generating a new context that will clean itself up in a `DeferCleanup()`. This has subtle behavior differences from the golang implementation but should make sense in a Ginkgo... um... context.
|
||||||
|
|||||||
+5
-1
@@ -24,7 +24,11 @@ func (c Command) Run(args []string, additionalArgs []string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
AbortWithUsage(err.Error())
|
AbortWithUsage(err.Error())
|
||||||
}
|
}
|
||||||
|
for _, arg := range args {
|
||||||
|
if strings.HasPrefix(arg, "-") {
|
||||||
|
AbortWith("Malformed arguments - make sure all flags appear {{bold}}after{{/}} the Ginkgo subcommand and {{bold}}before{{/}} your list of packages.\n{{gray}}e.g. 'ginkgo run -p my_package' is valid `ginkgo -p run my_package` is not.{{/}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
c.Command(args, additionalArgs)
|
c.Command(args, additionalArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-3
@@ -257,8 +257,12 @@ var FlagSections = GinkgoFlagSections{
|
|||||||
{Key: "filter", Style: "{{cyan}}", Heading: "Filtering Tests"},
|
{Key: "filter", Style: "{{cyan}}", Heading: "Filtering Tests"},
|
||||||
{Key: "failure", Style: "{{red}}", Heading: "Failure Handling"},
|
{Key: "failure", Style: "{{red}}", Heading: "Failure Handling"},
|
||||||
{Key: "output", Style: "{{magenta}}", Heading: "Controlling Output Formatting"},
|
{Key: "output", Style: "{{magenta}}", Heading: "Controlling Output Formatting"},
|
||||||
{Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis"},
|
{Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis",
|
||||||
{Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis"},
|
Description: "When generating a cover files, please pass a filename {{bold}}not{{/}} a path. To specify a different directory use {{magenta}}--output-dir{{/}}.",
|
||||||
|
},
|
||||||
|
{Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis",
|
||||||
|
Description: "When generating profile files, please pass filenames {{bold}}not{{/}} a path. Ginkgo will generate a profile file with the given name in the package's directory. To specify a different directory use {{magenta}}--output-dir{{/}}.",
|
||||||
|
},
|
||||||
{Key: "debug", Style: "{{blue}}", Heading: "Debugging Tests",
|
{Key: "debug", Style: "{{blue}}", Heading: "Debugging Tests",
|
||||||
Description: "In addition to these flags, Ginkgo supports a few debugging environment variables. To change the parallel server protocol set {{blue}}GINKGO_PARALLEL_PROTOCOL{{/}} to {{bold}}HTTP{{/}}. To avoid pruning callstacks set {{blue}}GINKGO_PRUNE_STACK{{/}} to {{bold}}FALSE{{/}}."},
|
Description: "In addition to these flags, Ginkgo supports a few debugging environment variables. To change the parallel server protocol set {{blue}}GINKGO_PARALLEL_PROTOCOL{{/}} to {{bold}}HTTP{{/}}. To avoid pruning callstacks set {{blue}}GINKGO_PRUNE_STACK{{/}} to {{bold}}FALSE{{/}}."},
|
||||||
{Key: "watch", Style: "{{light-yellow}}", Heading: "Controlling Ginkgo Watch"},
|
{Key: "watch", Style: "{{light-yellow}}", Heading: "Controlling Ginkgo Watch"},
|
||||||
@@ -572,7 +576,7 @@ var GoBuildFlags = GinkgoFlags{
|
|||||||
// GoRunFlags provides flags for the Ginkgo CLI run, and watch commands that capture go's run-time flags. These are passed to the compiled test binary by the ginkgo CLI
|
// GoRunFlags provides flags for the Ginkgo CLI run, and watch commands that capture go's run-time flags. These are passed to the compiled test binary by the ginkgo CLI
|
||||||
var GoRunFlags = GinkgoFlags{
|
var GoRunFlags = GinkgoFlags{
|
||||||
{KeyPath: "Go.CoverProfile", Name: "coverprofile", UsageArgument: "file", SectionKey: "code-and-coverage-analysis",
|
{KeyPath: "Go.CoverProfile", Name: "coverprofile", UsageArgument: "file", SectionKey: "code-and-coverage-analysis",
|
||||||
Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover.`},
|
Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover. Must be passed a filename, not a path. Use output-dir to control the location of the output.`},
|
||||||
{KeyPath: "Go.BlockProfile", Name: "blockprofile", UsageArgument: "file", SectionKey: "performance-analysis",
|
{KeyPath: "Go.BlockProfile", Name: "blockprofile", UsageArgument: "file", SectionKey: "performance-analysis",
|
||||||
Usage: `Write a goroutine blocking profile to the specified file when all tests are complete. Preserves test binary.`},
|
Usage: `Write a goroutine blocking profile to the specified file when all tests are complete. Preserves test binary.`},
|
||||||
{KeyPath: "Go.BlockProfileRate", Name: "blockprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis",
|
{KeyPath: "Go.BlockProfileRate", Name: "blockprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis",
|
||||||
@@ -600,6 +604,22 @@ func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsCo
|
|||||||
errors = append(errors, GinkgoErrors.BothRepeatAndUntilItFails())
|
errors = append(errors, GinkgoErrors.BothRepeatAndUntilItFails())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.ContainsRune(goFlagsConfig.CoverProfile, os.PathSeparator) {
|
||||||
|
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--coverprofile", goFlagsConfig.CoverProfile))
|
||||||
|
}
|
||||||
|
if strings.ContainsRune(goFlagsConfig.CPUProfile, os.PathSeparator) {
|
||||||
|
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--cpuprofile", goFlagsConfig.CPUProfile))
|
||||||
|
}
|
||||||
|
if strings.ContainsRune(goFlagsConfig.MemProfile, os.PathSeparator) {
|
||||||
|
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--memprofile", goFlagsConfig.MemProfile))
|
||||||
|
}
|
||||||
|
if strings.ContainsRune(goFlagsConfig.BlockProfile, os.PathSeparator) {
|
||||||
|
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--blockprofile", goFlagsConfig.BlockProfile))
|
||||||
|
}
|
||||||
|
if strings.ContainsRune(goFlagsConfig.MutexProfile, os.PathSeparator) {
|
||||||
|
errors = append(errors, GinkgoErrors.ExpectFilenameNotPath("--mutexprofile", goFlagsConfig.MutexProfile))
|
||||||
|
}
|
||||||
|
|
||||||
//initialize the output directory
|
//initialize the output directory
|
||||||
if cliConfig.OutputDir != "" {
|
if cliConfig.OutputDir != "" {
|
||||||
err := os.MkdirAll(cliConfig.OutputDir, 0777)
|
err := os.MkdirAll(cliConfig.OutputDir, 0777)
|
||||||
|
|||||||
+7
@@ -629,6 +629,13 @@ func (g ginkgoErrors) BothRepeatAndUntilItFails() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g ginkgoErrors) ExpectFilenameNotPath(flag string, path string) error {
|
||||||
|
return GinkgoError{
|
||||||
|
Heading: fmt.Sprintf("%s expects a filename but was given a path: %s", flag, path),
|
||||||
|
Message: fmt.Sprintf("%s takes a filename, not a path. Use --output-dir to specify a directory to collect all test outputs.", flag),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Stack-Trace parsing errors */
|
/* Stack-Trace parsing errors */
|
||||||
|
|
||||||
func (g ginkgoErrors) FailedToParseStackTrace(message string) error {
|
func (g ginkgoErrors) FailedToParseStackTrace(message string) error {
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
const VERSION = "2.23.0"
|
const VERSION = "2.23.1"
|
||||||
|
|||||||
+494
@@ -0,0 +1,494 @@
|
|||||||
|
package jsoncs3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alexedwards/argon2id"
|
||||||
|
apppb "github.com/cs3org/go-cs3apis/cs3/auth/applications/v1beta1"
|
||||||
|
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
|
||||||
|
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||||
|
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/opencloud-eu/reva/v2/pkg/appauth"
|
||||||
|
"github.com/opencloud-eu/reva/v2/pkg/appauth/manager/registry"
|
||||||
|
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||||
|
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||||
|
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||||
|
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/metadata"
|
||||||
|
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sethvargo/go-diceware/diceware"
|
||||||
|
"github.com/sethvargo/go-password/password"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PasswordGenerator interface {
|
||||||
|
GeneratePassword() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("jsoncs3", New)
|
||||||
|
}
|
||||||
|
|
||||||
|
type manager struct {
|
||||||
|
sync.RWMutex // for lazy initialization
|
||||||
|
mds metadata.Storage
|
||||||
|
generator PasswordGenerator
|
||||||
|
initialized bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
ProviderAddr string `mapstructure:"provider_addr"`
|
||||||
|
ServiceUserID string `mapstructure:"service_user_id"`
|
||||||
|
ServiceUserIdp string `mapstructure:"service_user_idp"`
|
||||||
|
MachineAuthAPIKey string `mapstructure:"machine_auth_apikey"`
|
||||||
|
Generator string `mapstructure:"password_generator"`
|
||||||
|
GeneratorConfig map[string]any `mapstructure:"generator_config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type updaterFunc func(map[string]*apppb.AppPassword) (map[string]*apppb.AppPassword, error)
|
||||||
|
|
||||||
|
const tracerName = "jsoncs3"
|
||||||
|
|
||||||
|
func New(m map[string]any) (appauth.Manager, error) {
|
||||||
|
c := &config{}
|
||||||
|
if err := mapstructure.Decode(m, c); err != nil {
|
||||||
|
err = errors.Wrap(err, "error creating a new manager")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ProviderAddr == "" {
|
||||||
|
return nil, fmt.Errorf("appauth jsoncs3 manager: provider_addr not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ServiceUserID == "" {
|
||||||
|
return nil, fmt.Errorf("appauth jsoncs3 manager: service_user_id not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ServiceUserIdp == "" {
|
||||||
|
return nil, fmt.Errorf("appauth jsoncs3 manager: service_user_idp not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.MachineAuthAPIKey == "" {
|
||||||
|
return nil, fmt.Errorf("appauth jsoncs3 manager: machine_auth_apikey not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Generator == "" {
|
||||||
|
c.Generator = "diceware"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pwgen PasswordGenerator
|
||||||
|
var err error
|
||||||
|
switch c.Generator {
|
||||||
|
case "diceware":
|
||||||
|
pwgen, err = NewDicewareGenerator(c.GeneratorConfig)
|
||||||
|
case "random":
|
||||||
|
pwgen, err = NewRandGenerator(c.GeneratorConfig)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("appauth jsoncs3 manager: unknown generator: %s", c.Generator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("appauth jsoncs3 manager: failed initialize password generator: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs3, err := metadata.NewCS3Storage(c.ProviderAddr, c.ProviderAddr, c.ServiceUserID, c.ServiceUserIdp, c.MachineAuthAPIKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewWithOptions(cs3, pwgen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithOptions(mds metadata.Storage, generator PasswordGenerator) (*manager, error) {
|
||||||
|
return &manager{
|
||||||
|
mds: mds,
|
||||||
|
generator: generator,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateAppPassword creates a password with specified scope to be used by
|
||||||
|
// third-party applications.
|
||||||
|
func (m *manager) GenerateAppPassword(ctx context.Context, scope map[string]*authpb.Scope, label string, expiration *typespb.Timestamp) (*apppb.AppPassword, error) {
|
||||||
|
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GenerateAppPassword")
|
||||||
|
defer span.End()
|
||||||
|
if err := m.initialize(ctx); err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
token, err := m.generator.GeneratePassword()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error creating new token")
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenHashed, err := argon2id.CreateHash(token, argon2id.DefaultParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error creating new token")
|
||||||
|
}
|
||||||
|
|
||||||
|
var userID *userpb.UserId
|
||||||
|
if user, ok := ctxpkg.ContextGetUser(ctx); ok {
|
||||||
|
userID = user.GetId()
|
||||||
|
} else {
|
||||||
|
return nil, errtypes.BadRequest("no user in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
cTime := &typespb.Timestamp{Seconds: uint64(time.Now().Unix())}
|
||||||
|
|
||||||
|
// For persisting we use the hashed password, since we don't
|
||||||
|
// want to store it in cleartext
|
||||||
|
appPass := &apppb.AppPassword{
|
||||||
|
Password: tokenHashed,
|
||||||
|
TokenScope: scope,
|
||||||
|
Label: label,
|
||||||
|
Expiration: expiration,
|
||||||
|
Ctime: cTime,
|
||||||
|
Utime: cTime,
|
||||||
|
User: userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
id := uuid.New().String()
|
||||||
|
|
||||||
|
err = m.updateWithRetry(ctx, 5, true, userID, func(a map[string]*apppb.AppPassword) (map[string]*apppb.AppPassword, error) {
|
||||||
|
a[id] = appPass
|
||||||
|
return a, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we need to resplace the hash with the cleartext password again since
|
||||||
|
// the requestor needs to know the cleartext value.
|
||||||
|
appPass.Password = token
|
||||||
|
|
||||||
|
return appPass, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAppPasswords lists the application passwords created by a user.
|
||||||
|
func (m *manager) ListAppPasswords(ctx context.Context) ([]*apppb.AppPassword, error) {
|
||||||
|
log := appctx.GetLogger(ctx)
|
||||||
|
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "ListAppPasswords")
|
||||||
|
defer span.End()
|
||||||
|
if err := m.initialize(ctx); err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var userID *userpb.UserId
|
||||||
|
if user, ok := ctxpkg.ContextGetUser(ctx); ok {
|
||||||
|
userID = user.GetId()
|
||||||
|
} else {
|
||||||
|
return nil, errtypes.BadRequest("no user in context")
|
||||||
|
}
|
||||||
|
_, userAppPasswords, err := m.getUserAppPasswords(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(errtypes.NotFound); ok {
|
||||||
|
return []*apppb.AppPassword{}, nil
|
||||||
|
}
|
||||||
|
log.Error().Err(err).Msg("getUserAppPasswords failed")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userAppPasswordSlice := make([]*apppb.AppPassword, 0, len(userAppPasswords))
|
||||||
|
|
||||||
|
for _, p := range userAppPasswords {
|
||||||
|
userAppPasswordSlice = append(userAppPasswordSlice, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return userAppPasswordSlice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidateAppPassword invalidates a generated password.
|
||||||
|
func (m *manager) InvalidateAppPassword(ctx context.Context, secret string) error {
|
||||||
|
log := appctx.GetLogger(ctx)
|
||||||
|
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "InvalidateAppPassword")
|
||||||
|
defer span.End()
|
||||||
|
if err := m.initialize(ctx); err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var userID *userpb.UserId
|
||||||
|
if user, ok := ctxpkg.ContextGetUser(ctx); ok {
|
||||||
|
userID = user.GetId()
|
||||||
|
} else {
|
||||||
|
return errtypes.BadRequest("no user in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
updater := func(a map[string]*apppb.AppPassword) (map[string]*apppb.AppPassword, error) {
|
||||||
|
for key, pw := range a {
|
||||||
|
ok, err := argon2id.ComparePasswordAndHash(secret, pw.Password)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
log.Debug().Err(err).Msg("Error comparing password and hash")
|
||||||
|
case ok:
|
||||||
|
delete(a, key)
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a, errtypes.NotFound("password not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.updateWithRetry(ctx, 5, false, userID, updater)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("getUserAppPasswords failed")
|
||||||
|
return errtypes.NotFound("password not found")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAppPassword retrieves the password information by the combination of username and password.
|
||||||
|
func (m *manager) GetAppPassword(ctx context.Context, user *userpb.UserId, secret string) (*apppb.AppPassword, error) {
|
||||||
|
log := appctx.GetLogger(ctx)
|
||||||
|
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetAppPassword")
|
||||||
|
defer span.End()
|
||||||
|
if err := m.initialize(ctx); err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errUpdateSkipped := errors.New("update skipped")
|
||||||
|
|
||||||
|
var matchedPw *apppb.AppPassword
|
||||||
|
updater := func(a map[string]*apppb.AppPassword) (map[string]*apppb.AppPassword, error) {
|
||||||
|
matchedPw = nil
|
||||||
|
for id, pw := range a {
|
||||||
|
ok, err := argon2id.ComparePasswordAndHash(secret, pw.Password)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
log.Debug().Err(err).Msg("Error comparing password and hash")
|
||||||
|
case ok:
|
||||||
|
// password found
|
||||||
|
if pw.Expiration != nil && pw.Expiration.Seconds != 0 && uint64(time.Now().Unix()) > pw.Expiration.Seconds {
|
||||||
|
log.Debug().Str("AppPasswordId", id).Msg("password expired")
|
||||||
|
return nil, errtypes.NotFound("password not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
matchedPw = pw
|
||||||
|
// password not expired
|
||||||
|
// Updating the Utime will cause an Upload for every single GetAppPassword request. We are limiting this to one
|
||||||
|
// update per 5 minutes otherwise this backend will become unusable.
|
||||||
|
if time.Since(utils.TSToTime(pw.Utime)) > 5*time.Minute {
|
||||||
|
a[id].Utime = utils.TSNow()
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
return a, errUpdateSkipped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errtypes.NotFound("password not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.updateWithRetry(ctx, 5, false, user, updater)
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
fallthrough
|
||||||
|
case errors.Is(err, errUpdateSkipped):
|
||||||
|
return matchedPw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errtypes.NotFound("password not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) initialize(ctx context.Context) error {
|
||||||
|
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "initialize")
|
||||||
|
defer span.End()
|
||||||
|
if m.initialized {
|
||||||
|
span.SetStatus(codes.Ok, "already initialized")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if m.initialized { // check if initialization happened while grabbing the lock
|
||||||
|
span.SetStatus(codes.Ok, "initialized while grabbing lock")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.Background()
|
||||||
|
err := m.mds.Init(ctx, "jsoncs3-appauth-data")
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) updateWithRetry(ctx context.Context, retries int, createIfNotFound bool, userid *userpb.UserId, updater updaterFunc) error {
|
||||||
|
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "initialize")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
retry := true
|
||||||
|
var (
|
||||||
|
etag string
|
||||||
|
userAppPasswords map[string]*apppb.AppPassword
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// retry for the specified number of times, then error out
|
||||||
|
for i := 0; i < retries && retry; i++ {
|
||||||
|
etag, userAppPasswords, err = m.getUserAppPasswords(ctx, userid)
|
||||||
|
switch err.(type) {
|
||||||
|
case nil:
|
||||||
|
// empty
|
||||||
|
case errtypes.NotFound:
|
||||||
|
if createIfNotFound {
|
||||||
|
userAppPasswords = map[string]*apppb.AppPassword{}
|
||||||
|
} else {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, "downloading app tokens failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, "downloading app tokens failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userAppPasswords, err = updater(userAppPasswords)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.updateUserAppPassword(ctx, userid, userAppPasswords, etag)
|
||||||
|
switch err.(type) {
|
||||||
|
case nil:
|
||||||
|
retry = false
|
||||||
|
case errtypes.PreconditionFailed:
|
||||||
|
retry = true
|
||||||
|
default:
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if retry {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, "updating app tokens failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) updateUserAppPassword(ctx context.Context, userid *userpb.UserId, appPasswords map[string]*apppb.AppPassword, ifMatchEtag string) error {
|
||||||
|
log := appctx.GetLogger(ctx)
|
||||||
|
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "getUserAppPasswords")
|
||||||
|
jsonPath := userAppTokenJSONPath(userid)
|
||||||
|
|
||||||
|
pwBytes, err := json.Marshal(appPasswords)
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ur := metadata.UploadRequest{
|
||||||
|
Path: jsonPath,
|
||||||
|
Content: pwBytes,
|
||||||
|
IfMatchEtag: ifMatchEtag,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no etag, make sure to only upload if the file wasn't craeted yet
|
||||||
|
if ifMatchEtag == "" {
|
||||||
|
ur.IfNoneMatch = []string{"*"}
|
||||||
|
}
|
||||||
|
_, err = m.mds.Upload(ctx, ur)
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
log.Debug().Err(err).Msg("persisting provider cache failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) getUserAppPasswords(ctx context.Context, userid *userpb.UserId) (string, map[string]*apppb.AppPassword, error) {
|
||||||
|
log := appctx.GetLogger(ctx)
|
||||||
|
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "getUserAppPasswords")
|
||||||
|
jsonPath := userAppTokenJSONPath(userid)
|
||||||
|
dlreq := metadata.DownloadRequest{
|
||||||
|
Path: jsonPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
var userAppPasswords = map[string]*apppb.AppPassword{}
|
||||||
|
dlres, err := m.mds.Download(ctx, dlreq)
|
||||||
|
switch err.(type) {
|
||||||
|
case nil:
|
||||||
|
err = json.Unmarshal(dlres.Content, &userAppPasswords)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("unmarshaling app tokens failed")
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
case errtypes.NotFound:
|
||||||
|
return "", nil, errtypes.NotFound("password not found")
|
||||||
|
default:
|
||||||
|
span.RecordError(err)
|
||||||
|
span.SetStatus(codes.Error, "downloading app tokens failed")
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return dlres.Etag, userAppPasswords, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func userAppTokenJSONPath(userID *userpb.UserId) string {
|
||||||
|
return userID.GetOpaqueId() + ".json"
|
||||||
|
}
|
||||||
|
|
||||||
|
type randomPassword struct {
|
||||||
|
Strength int `mapstructure:"token_strength"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRandGenerator(config map[string]any) (*randomPassword, error) {
|
||||||
|
r := &randomPassword{}
|
||||||
|
if err := mapstructure.Decode(config, r); err != nil {
|
||||||
|
err = errors.Wrap(err, "error configuring password generator")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.Strength <= 0 {
|
||||||
|
r.Strength = 11
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r randomPassword) GeneratePassword() (string, error) {
|
||||||
|
token, err := password.Generate(r.Strength, r.Strength/2, 0, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "error creating new token")
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type dicewarePassword struct {
|
||||||
|
NumWords int `mapstructure:"number_of_words"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDicewareGenerator(config map[string]any) (*dicewarePassword, error) {
|
||||||
|
d := &dicewarePassword{}
|
||||||
|
if err := mapstructure.Decode(config, d); err != nil {
|
||||||
|
err = errors.Wrap(err, "error creating a new manager")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if d.NumWords <= 0 {
|
||||||
|
d.NumWords = 6
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dicewarePassword) GeneratePassword() (string, error) {
|
||||||
|
token, err := diceware.Generate(d.NumWords)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "error creating new token")
|
||||||
|
}
|
||||||
|
return strings.Join(token, " "), nil
|
||||||
|
}
|
||||||
+1
@@ -21,5 +21,6 @@ package loader
|
|||||||
import (
|
import (
|
||||||
// Load core application auth manager drivers.
|
// Load core application auth manager drivers.
|
||||||
_ "github.com/opencloud-eu/reva/v2/pkg/appauth/manager/json"
|
_ "github.com/opencloud-eu/reva/v2/pkg/appauth/manager/json"
|
||||||
|
_ "github.com/opencloud-eu/reva/v2/pkg/appauth/manager/jsoncs3"
|
||||||
// Add your own here
|
// Add your own here
|
||||||
)
|
)
|
||||||
|
|||||||
+5
@@ -276,6 +276,11 @@ func (lu *Lookup) InternalRoot() string {
|
|||||||
return lu.Options.Root
|
return lu.Options.Root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InternalSpaceRoot returns the internal path for a space
|
||||||
|
func (lu *Lookup) InternalSpaceRoot(spaceID string) string {
|
||||||
|
return lu.InternalPath(spaceID, spaceID)
|
||||||
|
}
|
||||||
|
|
||||||
// InternalPath returns the internal path for a given ID
|
// InternalPath returns the internal path for a given ID
|
||||||
func (lu *Lookup) InternalPath(spaceID, nodeID string) string {
|
func (lu *Lookup) InternalPath(spaceID, nodeID string) string {
|
||||||
if strings.Contains(nodeID, node.RevisionIDDelimiter) || strings.HasSuffix(nodeID, node.CurrentIDDelimiter) {
|
if strings.Contains(nodeID, node.RevisionIDDelimiter) || strings.HasSuffix(nodeID, node.CurrentIDDelimiter) {
|
||||||
|
|||||||
Generated
Vendored
+5
@@ -263,6 +263,11 @@ func (lu *Lookup) InternalRoot() string {
|
|||||||
return lu.Options.Root
|
return lu.Options.Root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InternalSpaceRoot returns the internal path for a space
|
||||||
|
func (lu *Lookup) InternalSpaceRoot(spaceID string) string {
|
||||||
|
return filepath.Join(lu.Options.Root, "spaces", Pathify(spaceID, 1, 2))
|
||||||
|
}
|
||||||
|
|
||||||
// InternalPath returns the internal path for a given ID
|
// InternalPath returns the internal path for a given ID
|
||||||
func (lu *Lookup) InternalPath(spaceID, nodeID string) string {
|
func (lu *Lookup) InternalPath(spaceID, nodeID string) string {
|
||||||
return filepath.Join(lu.Options.Root, "spaces", Pathify(spaceID, 1, 2), "nodes", Pathify(nodeID, 4, 2))
|
return filepath.Join(lu.Options.Root, "spaces", Pathify(spaceID, 1, 2), "nodes", Pathify(nodeID, 4, 2))
|
||||||
|
|||||||
+1
@@ -156,6 +156,7 @@ type PathLookup interface {
|
|||||||
GenerateSpaceID(spaceType string, owner *userpb.User) (string, error)
|
GenerateSpaceID(spaceType string, owner *userpb.User) (string, error)
|
||||||
|
|
||||||
InternalRoot() string
|
InternalRoot() string
|
||||||
|
InternalSpaceRoot(spaceID string) string
|
||||||
InternalPath(spaceID, nodeID string) string
|
InternalPath(spaceID, nodeID string) string
|
||||||
VersionPath(spaceID, nodeID, version string) string
|
VersionPath(spaceID, nodeID, version string) string
|
||||||
Path(ctx context.Context, n *Node, hasPermission PermissionFunc) (path string, err error)
|
Path(ctx context.Context, n *Node, hasPermission PermissionFunc) (path string, err error)
|
||||||
|
|||||||
+5
-2
@@ -786,8 +786,11 @@ func (fs *Decomposedfs) DeleteStorageSpace(ctx context.Context, req *provider.De
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove space metadata
|
// remove space metadata
|
||||||
if err := os.RemoveAll(root); err != nil {
|
spaceRoot := fs.lu.InternalSpaceRoot(spaceID)
|
||||||
return err
|
if spaceRoot != "" {
|
||||||
|
if err := os.RemoveAll(fs.lu.InternalSpaceRoot(spaceID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalidate id in cache
|
// invalidate id in cache
|
||||||
|
|||||||
Generated
Vendored
+24
-1
@@ -30,6 +30,7 @@ import (
|
|||||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rogpeppe/go-internal/lockedfile"
|
"github.com/rogpeppe/go-internal/lockedfile"
|
||||||
|
"github.com/shamaton/msgpack/v2"
|
||||||
|
|
||||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||||
@@ -62,7 +63,29 @@ func (tp *Tree) CreateRevision(ctx context.Context, n *node.Node, version string
|
|||||||
vf, err := os.OpenFile(versionPath, os.O_CREATE|os.O_EXCL, 0600)
|
vf, err := os.OpenFile(versionPath, os.O_CREATE|os.O_EXCL, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsExist(err) {
|
if os.IsExist(err) {
|
||||||
err := os.Remove(versionPath)
|
revisionNode := node.NewBaseNode(n.SpaceID, n.ID+node.RevisionIDDelimiter+version, tp.lookup)
|
||||||
|
revisionPath := tp.lookup.MetadataBackend().MetadataPath(revisionNode)
|
||||||
|
b, err := os.ReadFile(revisionPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string][]byte{}
|
||||||
|
if err := msgpack.Unmarshal(b, &m); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
bid := m["user.oc.blobid"]
|
||||||
|
if string(bid) != "" {
|
||||||
|
if err := tp.DeleteBlob(&node.Node{
|
||||||
|
BaseNode: *revisionNode,
|
||||||
|
BlobID: string(bid),
|
||||||
|
}); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(versionPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright 2017 Seth Vargo <seth@sethvargo.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
package diceware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sides is the number of sides on a die.
|
||||||
|
var sides = big.NewInt(6)
|
||||||
|
|
||||||
|
var _ DicewareGenerator = (*Generator)(nil)
|
||||||
|
|
||||||
|
// Generator is the stateful generator which can be used to customize the word
|
||||||
|
// list and other generation options.
|
||||||
|
type Generator struct {
|
||||||
|
wordList WordList
|
||||||
|
randReader io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeneratorInput is used as input to the NewGenerator function.
|
||||||
|
type GeneratorInput struct {
|
||||||
|
// WordList is the word list to use. There are built-in word lists like
|
||||||
|
// WordListEffBig (default), WordListEffSmall, and WordListOriginal. You can
|
||||||
|
// also bring your own word list by implementing the WordList interface.
|
||||||
|
WordList WordList
|
||||||
|
|
||||||
|
// RandReader is an optional reader to use in place of the default
|
||||||
|
// (crypto/rand.Reader), which can be used to generate repeatable sets of
|
||||||
|
// words
|
||||||
|
RandReader io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenerator creates a new Generator from the specified configuration. If no
|
||||||
|
// input is given, all the default values are used. This function is safe for
|
||||||
|
// concurrent use.
|
||||||
|
func NewGenerator(i *GeneratorInput) (*Generator, error) {
|
||||||
|
if i == nil {
|
||||||
|
i = new(GeneratorInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.WordList == nil {
|
||||||
|
i.WordList = WordListEffLarge()
|
||||||
|
}
|
||||||
|
|
||||||
|
gen := &Generator{
|
||||||
|
wordList: i.WordList,
|
||||||
|
randReader: i.RandReader,
|
||||||
|
}
|
||||||
|
|
||||||
|
if gen.randReader == nil {
|
||||||
|
gen.randReader = rand.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
return gen, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate generates a collection of diceware words, specified by the numWords
|
||||||
|
// parameter.
|
||||||
|
//
|
||||||
|
// The algorithm is fast, but it's not designed to be performant, favoring
|
||||||
|
// entropy over speed.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent use, but there is a possibility of
|
||||||
|
// concurrent invocations generating overlapping words. To generate multiple
|
||||||
|
// non-overlapping words, use a single invocation of the function and split the
|
||||||
|
// resulting string list.
|
||||||
|
func (g *Generator) Generate(numWords int) ([]string, error) {
|
||||||
|
if typ, ok := g.wordList.(WordListNumWordser); ok {
|
||||||
|
if l := typ.NumWords(); numWords > l {
|
||||||
|
return nil, fmt.Errorf("number of requested words (%d) cannot exceed the size of the wordlist (%d)",
|
||||||
|
numWords, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list := make([]string, 0, numWords)
|
||||||
|
seen := make(map[string]struct{}, numWords)
|
||||||
|
|
||||||
|
for i := 0; i < numWords; i++ {
|
||||||
|
n, err := g.RollWord(g.wordList.Digits())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
word := g.wordList.WordAt(n)
|
||||||
|
if _, ok := seen[word]; ok {
|
||||||
|
i--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, word)
|
||||||
|
seen[word] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustGenerate is the same as Generate, but panics on error.
|
||||||
|
func (g *Generator) MustGenerate(numWords int) []string {
|
||||||
|
list, err := g.Generate(numWords)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate - see Generator.Generate for usage.
|
||||||
|
func Generate(numWords int) ([]string, error) {
|
||||||
|
gen, err := NewGenerator(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return gen.Generate(numWords)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustGenerate - see Generator.MustGenerate for usage.
|
||||||
|
func MustGenerate(numWords int) []string {
|
||||||
|
gen, err := NewGenerator(nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return gen.MustGenerate(numWords)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateWithWordList generates a list of the given number of words from the
|
||||||
|
// given word list.
|
||||||
|
func GenerateWithWordList(numWords int, wordList WordList) ([]string, error) {
|
||||||
|
gen, err := NewGenerator(&GeneratorInput{
|
||||||
|
WordList: wordList,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return gen.Generate(numWords)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WordAt retrieves the word at the given index from EFF's large wordlist.
|
||||||
|
//
|
||||||
|
// Deprecated: Use WordList.WordAt instead.
|
||||||
|
func WordAt(i int) string {
|
||||||
|
return WordListEffLarge().WordAt(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RollDie rolls a single 6-sided die and returns a value between [1,6].
|
||||||
|
//
|
||||||
|
// Internally this creates a new Generator with a nil configuration and calls
|
||||||
|
// Generator.RollDie.
|
||||||
|
func RollDie() (int, error) {
|
||||||
|
gen, err := NewGenerator(nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return gen.RollDie()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RollWord rolls and aggregates dice to represent one word in the list. The
|
||||||
|
// result is the index of the word in the list.
|
||||||
|
//
|
||||||
|
// Internally this creates a new Generator with a nil configuration and calls
|
||||||
|
// Generator.RollWord.
|
||||||
|
func RollWord(d int) (int, error) {
|
||||||
|
gen, err := NewGenerator(nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return gen.RollWord(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RollDie rolls a single 6-sided die and returns a value between [1,6].
|
||||||
|
func (g *Generator) RollDie() (int, error) {
|
||||||
|
r, err := rand.Int(g.randReader, sides)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to generate a random number: %w", err)
|
||||||
|
}
|
||||||
|
return int(r.Int64()) + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RollWord rolls and aggregates dice to represent one word in the list. The
|
||||||
|
// result is the index of the word in the list.
|
||||||
|
func (g *Generator) RollWord(d int) (int, error) {
|
||||||
|
var final int
|
||||||
|
|
||||||
|
for i := d; i > 0; i-- {
|
||||||
|
res, err := g.RollDie()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
final += res * int(math.Pow(10, float64(i-1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return final, nil
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package diceware
|
||||||
|
|
||||||
|
type DicewareGenerator interface {
|
||||||
|
Generate(int) ([]string, error)
|
||||||
|
MustGenerate(int) []string
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
package diceware
|
||||||
|
|
||||||
|
var _ DicewareGenerator = (*mockGenerator)(nil)
|
||||||
|
|
||||||
|
type mockGenerator struct {
|
||||||
|
result []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockGenerator creates a new generator that satisfies the DicewareGenerator
|
||||||
|
// interface. If an error is provided, the error is returned. If a result if
|
||||||
|
// provided, the result is always returned, regardless of what parameters are
|
||||||
|
// passed into the Generate or MustGenerate methods.
|
||||||
|
//
|
||||||
|
// This function is most useful for tests where you want to have predicable
|
||||||
|
// results for a transitive resource that depends on go-diceware.
|
||||||
|
func NewMockGenerator(result []string, err error) *mockGenerator {
|
||||||
|
return &mockGenerator{
|
||||||
|
result: result,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate returns the mocked result or error.
|
||||||
|
func (g *mockGenerator) Generate(int) ([]string, error) {
|
||||||
|
if g.err != nil {
|
||||||
|
return nil, g.err
|
||||||
|
}
|
||||||
|
return g.result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustGenerate returns the mocked result or panics if an error was given.
|
||||||
|
func (g *mockGenerator) MustGenerate(int) []string {
|
||||||
|
if g.err != nil {
|
||||||
|
panic(g.err)
|
||||||
|
}
|
||||||
|
return g.result
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package diceware
|
||||||
|
|
||||||
|
// WordList is an interface that must be implemented to be considered a word
|
||||||
|
// list for use in the diceware algorithm. This interface can be implemented by
|
||||||
|
// other libraries.
|
||||||
|
type WordList interface {
|
||||||
|
// Digits is the number of digits for indexes in the word list. This
|
||||||
|
// determines the number of dice rolls.
|
||||||
|
Digits() int
|
||||||
|
|
||||||
|
// WordAt returns the word at the given integer in the word list.
|
||||||
|
WordAt(int) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// WordListNumWordser is an auxiliary interface that returns the number of words
|
||||||
|
// in the list. This is a separate interface for backwards compatibility.
|
||||||
|
type WordListNumWordser interface {
|
||||||
|
// NumWords returns the total number of words in the list.
|
||||||
|
NumWords() int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ WordList = (*wordListInternal)(nil)
|
||||||
|
_ WordListNumWordser = (*wordListInternal)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type wordListInternal struct {
|
||||||
|
digits int
|
||||||
|
words map[int]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wordListInternal) Digits() int {
|
||||||
|
return w.digits
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wordListInternal) WordAt(i int) string {
|
||||||
|
return w.words[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wordListInternal) NumWords() int {
|
||||||
|
return len(w.words)
|
||||||
|
}
|
||||||
+7794
File diff suppressed because it is too large
Load Diff
+1314
File diff suppressed because it is too large
Load Diff
+7789
File diff suppressed because it is too large
Load Diff
Vendored
+6
-2
@@ -1057,7 +1057,7 @@ github.com/onsi/ginkgo/reporters/stenographer
|
|||||||
github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable
|
github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable
|
||||||
github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty
|
github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty
|
||||||
github.com/onsi/ginkgo/types
|
github.com/onsi/ginkgo/types
|
||||||
# github.com/onsi/ginkgo/v2 v2.23.0
|
# github.com/onsi/ginkgo/v2 v2.23.1
|
||||||
## explicit; go 1.23.0
|
## explicit; go 1.23.0
|
||||||
github.com/onsi/ginkgo/v2
|
github.com/onsi/ginkgo/v2
|
||||||
github.com/onsi/ginkgo/v2/config
|
github.com/onsi/ginkgo/v2/config
|
||||||
@@ -1191,7 +1191,7 @@ github.com/open-policy-agent/opa/v1/types
|
|||||||
github.com/open-policy-agent/opa/v1/util
|
github.com/open-policy-agent/opa/v1/util
|
||||||
github.com/open-policy-agent/opa/v1/util/decoding
|
github.com/open-policy-agent/opa/v1/util/decoding
|
||||||
github.com/open-policy-agent/opa/v1/version
|
github.com/open-policy-agent/opa/v1/version
|
||||||
# github.com/opencloud-eu/reva/v2 v2.28.1-0.20250319144557-ae6d4d54cb01
|
# github.com/opencloud-eu/reva/v2 v2.28.1-0.20250320105919-be91238e6b11
|
||||||
## explicit; go 1.24.1
|
## explicit; go 1.24.1
|
||||||
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
|
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
|
||||||
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
|
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
|
||||||
@@ -1295,6 +1295,7 @@ github.com/opencloud-eu/reva/v2/pkg/app/registry/registry
|
|||||||
github.com/opencloud-eu/reva/v2/pkg/app/registry/static
|
github.com/opencloud-eu/reva/v2/pkg/app/registry/static
|
||||||
github.com/opencloud-eu/reva/v2/pkg/appauth
|
github.com/opencloud-eu/reva/v2/pkg/appauth
|
||||||
github.com/opencloud-eu/reva/v2/pkg/appauth/manager/json
|
github.com/opencloud-eu/reva/v2/pkg/appauth/manager/json
|
||||||
|
github.com/opencloud-eu/reva/v2/pkg/appauth/manager/jsoncs3
|
||||||
github.com/opencloud-eu/reva/v2/pkg/appauth/manager/loader
|
github.com/opencloud-eu/reva/v2/pkg/appauth/manager/loader
|
||||||
github.com/opencloud-eu/reva/v2/pkg/appauth/manager/registry
|
github.com/opencloud-eu/reva/v2/pkg/appauth/manager/registry
|
||||||
github.com/opencloud-eu/reva/v2/pkg/appctx
|
github.com/opencloud-eu/reva/v2/pkg/appctx
|
||||||
@@ -1763,6 +1764,9 @@ github.com/sercand/kuberesolver/v5
|
|||||||
# github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
# github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/sergi/go-diff/diffmatchpatch
|
github.com/sergi/go-diff/diffmatchpatch
|
||||||
|
# github.com/sethvargo/go-diceware v0.5.0
|
||||||
|
## explicit; go 1.22
|
||||||
|
github.com/sethvargo/go-diceware/diceware
|
||||||
# github.com/sethvargo/go-password v0.3.1
|
# github.com/sethvargo/go-password v0.3.1
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
github.com/sethvargo/go-password/password
|
github.com/sethvargo/go-password/password
|
||||||
|
|||||||
Reference in New Issue
Block a user