mirror of
https://github.com/rclone/rclone.git
synced 2025-01-08 12:34:53 +02:00
seafile: renew library password - fixes #6662
Passwords for encrypted libraries are kept in memory in the server and flushed after an hour. This MR fixes an issue when the library password expires after 1 hour.
This commit is contained in:
parent
f08bb5bf66
commit
f31ab6d178
54
backend/seafile/renew.go
Normal file
54
backend/seafile/renew.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package seafile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rclone/rclone/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Renew allows tokens to be renewed on expiry.
|
||||||
|
type Renew struct {
|
||||||
|
ts *time.Ticker // timer indicating when it's time to renew the token
|
||||||
|
run func() error // the callback to do the renewal
|
||||||
|
done chan interface{} // channel to end the go routine
|
||||||
|
shutdown *sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRenew creates a new Renew struct and starts a background process
|
||||||
|
// which renews the token whenever it expires. It uses the run() call
|
||||||
|
// to do the renewal.
|
||||||
|
func NewRenew(every time.Duration, run func() error) *Renew {
|
||||||
|
r := &Renew{
|
||||||
|
ts: time.NewTicker(every),
|
||||||
|
run: run,
|
||||||
|
done: make(chan interface{}),
|
||||||
|
shutdown: &sync.Once{},
|
||||||
|
}
|
||||||
|
go r.renewOnExpiry()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Renew) renewOnExpiry() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-r.ts.C:
|
||||||
|
err := r.run()
|
||||||
|
if err != nil {
|
||||||
|
fs.Errorf(nil, "error while refreshing decryption token: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-r.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown stops the ticker and no more renewal will take place.
|
||||||
|
func (r *Renew) Shutdown() {
|
||||||
|
// closing a channel can only be done once
|
||||||
|
r.shutdown.Do(func() {
|
||||||
|
r.ts.Stop()
|
||||||
|
close(r.done)
|
||||||
|
})
|
||||||
|
}
|
35
backend/seafile/renew_test.go
Normal file
35
backend/seafile/renew_test.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package seafile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShouldAllowShutdownTwice(t *testing.T) {
|
||||||
|
renew := NewRenew(time.Hour, func() error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
renew.Shutdown()
|
||||||
|
renew.Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenewal(t *testing.T) {
|
||||||
|
var count int64
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2) // run the renewal twice
|
||||||
|
renew := NewRenew(time.Millisecond, func() error {
|
||||||
|
atomic.AddInt64(&count, 1)
|
||||||
|
wg.Done()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
renew.Shutdown()
|
||||||
|
|
||||||
|
// it is technically possible that a third renewal gets triggered between Wait() and Shutdown()
|
||||||
|
assert.GreaterOrEqual(t, atomic.LoadInt64(&count), int64(2))
|
||||||
|
}
|
@ -143,6 +143,7 @@ type Fs struct {
|
|||||||
createDirMutex sync.Mutex // Protect creation of directories
|
createDirMutex sync.Mutex // Protect creation of directories
|
||||||
useOldDirectoryAPI bool // Use the old API v2 if seafile < 7
|
useOldDirectoryAPI bool // Use the old API v2 if seafile < 7
|
||||||
moveDirNotAvailable bool // Version < 7.0 don't have an API to move a directory
|
moveDirNotAvailable bool // Version < 7.0 don't have an API to move a directory
|
||||||
|
renew *Renew // Renew an encrypted library token
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
@ -268,6 +269,11 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
|
|||||||
}
|
}
|
||||||
// And remove the public link feature
|
// And remove the public link feature
|
||||||
f.features.PublicLink = nil
|
f.features.PublicLink = nil
|
||||||
|
|
||||||
|
// renew the library password every 45 minutes
|
||||||
|
f.renew = NewRenew(45*time.Minute, func() error {
|
||||||
|
return f.authorizeLibrary(context.Background(), libraryID)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Deactivate the cleaner feature since there's no library selected
|
// Deactivate the cleaner feature since there's no library selected
|
||||||
@ -383,6 +389,15 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf
|
|||||||
return nil, fmt.Errorf("unknown state %q", config.State)
|
return nil, fmt.Errorf("unknown state %q", config.State)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown the Fs
|
||||||
|
func (f *Fs) Shutdown(ctx context.Context) error {
|
||||||
|
if f.renew == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f.renew.Shutdown()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// sets the AuthorizationToken up
|
// sets the AuthorizationToken up
|
||||||
func (f *Fs) setAuthorizationToken(token string) {
|
func (f *Fs) setAuthorizationToken(token string) {
|
||||||
f.srv.SetHeader("Authorization", "Token "+token)
|
f.srv.SetHeader("Authorization", "Token "+token)
|
||||||
@ -1331,6 +1346,7 @@ var (
|
|||||||
_ fs.PutStreamer = &Fs{}
|
_ fs.PutStreamer = &Fs{}
|
||||||
_ fs.PublicLinker = &Fs{}
|
_ fs.PublicLinker = &Fs{}
|
||||||
_ fs.UserInfoer = &Fs{}
|
_ fs.UserInfoer = &Fs{}
|
||||||
|
_ fs.Shutdowner = &Fs{}
|
||||||
_ fs.Object = &Object{}
|
_ fs.Object = &Object{}
|
||||||
_ fs.IDer = &Object{}
|
_ fs.IDer = &Object{}
|
||||||
)
|
)
|
||||||
|
@ -8,9 +8,10 @@ versionIntroduced: "v1.52"
|
|||||||
|
|
||||||
This is a backend for the [Seafile](https://www.seafile.com/) storage service:
|
This is a backend for the [Seafile](https://www.seafile.com/) storage service:
|
||||||
- It works with both the free community edition or the professional edition.
|
- It works with both the free community edition or the professional edition.
|
||||||
- Seafile versions 6.x and 7.x are all supported.
|
- Seafile versions 6.x, 7.x, 8.x and 9.x are all supported.
|
||||||
- Encrypted libraries are also supported.
|
- Encrypted libraries are also supported.
|
||||||
- It supports 2FA enabled users
|
- It supports 2FA enabled users
|
||||||
|
- Using a Library API Token is **not** supported
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@ -256,14 +257,17 @@ that has already been shared, you will get the exact same link.
|
|||||||
|
|
||||||
### Compatibility
|
### Compatibility
|
||||||
|
|
||||||
It has been actively tested using the [seafile docker image](https://github.com/haiwen/seafile-docker) of these versions:
|
It has been actively developed using the [seafile docker image](https://github.com/haiwen/seafile-docker) of these versions:
|
||||||
- 6.3.4 community edition
|
- 6.3.4 community edition
|
||||||
- 7.0.5 community edition
|
- 7.0.5 community edition
|
||||||
- 7.1.3 community edition
|
- 7.1.3 community edition
|
||||||
|
- 9.0.10 community edition
|
||||||
|
|
||||||
Versions below 6.0 are not supported.
|
Versions below 6.0 are not supported.
|
||||||
Versions between 6.0 and 6.3 haven't been tested and might not work properly.
|
Versions between 6.0 and 6.3 haven't been tested and might not work properly.
|
||||||
|
|
||||||
|
Each new version of `rclone` is automatically tested against the [latest docker image](https://hub.docker.com/r/seafileltd/seafile-mc/) of the seafile community server.
|
||||||
|
|
||||||
{{< rem autogenerated options start" - DO NOT EDIT - instead edit fs.RegInfo in backend/seafile/seafile.go then run make backenddocs" >}}
|
{{< rem autogenerated options start" - DO NOT EDIT - instead edit fs.RegInfo in backend/seafile/seafile.go then run make backenddocs" >}}
|
||||||
### Standard options
|
### Standard options
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
version: '2.0'
|
version: '2.0'
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: mariadb:10.1
|
image: mariadb:10.5
|
||||||
environment:
|
environment:
|
||||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||||
- MYSQL_LOG_CONSOLE=true
|
- MYSQL_LOG_CONSOLE=true
|
||||||
@ -9,7 +9,7 @@ services:
|
|||||||
- ${SEAFILE_TEST_DATA}/${NAME}/seafile-mysql/db:/var/lib/mysql
|
- ${SEAFILE_TEST_DATA}/${NAME}/seafile-mysql/db:/var/lib/mysql
|
||||||
|
|
||||||
memcached:
|
memcached:
|
||||||
image: memcached:1.5.6
|
image: memcached:1.6.9
|
||||||
entrypoint: memcached -m 256
|
entrypoint: memcached -m 256
|
||||||
|
|
||||||
seafile:
|
seafile:
|
||||||
|
Loading…
Reference in New Issue
Block a user