rc: methods marked as AuthRequired need auth unless --rc-no-auth
Methods which can read or mutate external storage will require authorisation - enforce this. This can be overidden by `--rc-no-auth`.
This commit is contained in:
parent
181267e20e
commit
fa0a9653d2
8 changed files with 102 additions and 4 deletions
|
@ -106,6 +106,7 @@ type Server struct {
|
|||
httpServer *http.Server
|
||||
basicPassHashed string
|
||||
useSSL bool // if server is configured for SSL/TLS
|
||||
usingAuth bool // set if authentication is configured
|
||||
}
|
||||
|
||||
// singleUserProvider provides the encrypted password for a single user
|
||||
|
@ -143,6 +144,7 @@ func NewServer(handler http.Handler, opt *Options) *Server {
|
|||
}
|
||||
authenticator := auth.NewBasicAuthenticator(s.Opt.Realm, secretProvider)
|
||||
handler = auth.JustCheck(authenticator, handler.ServeHTTP)
|
||||
s.usingAuth = true
|
||||
}
|
||||
|
||||
s.useSSL = s.Opt.SslKey != ""
|
||||
|
@ -255,3 +257,8 @@ func (s *Server) URL() string {
|
|||
}
|
||||
return fmt.Sprintf("%s://%s/", proto, addr)
|
||||
}
|
||||
|
||||
// UsingAuth returns true if authentication is required
|
||||
func (s *Server) UsingAuth() bool {
|
||||
return s.usingAuth
|
||||
}
|
||||
|
|
|
@ -81,6 +81,19 @@ implementing browser based GUIs for rclone functions.
|
|||
|
||||
Default Off.
|
||||
|
||||
### --rc-no-auth
|
||||
|
||||
By default rclone will require authorisation to have been set up on
|
||||
the rc interface in order to use any methods which access any rclone
|
||||
remotes. Eg `operations/list` is denied as it involved creating a
|
||||
remote as is `sync/copy`.
|
||||
|
||||
If this is set then no authorisation will be required on the server to
|
||||
use these methods. The alternative is to use `--rc-user` and
|
||||
`--rc-pass` and use these credentials in the request.
|
||||
|
||||
Default Off.
|
||||
|
||||
## Accessing the remote control via the rclone rc command
|
||||
|
||||
Rclone itself implements the remote control protocol in its `rclone
|
||||
|
|
|
@ -17,6 +17,16 @@ func init() {
|
|||
Help: `
|
||||
This echoes the input parameters to the output parameters for testing
|
||||
purposes. It can be used to check that rclone is still alive and to
|
||||
check that parameter passing is working properly.`,
|
||||
})
|
||||
Add(Call{
|
||||
Path: "rc/noopauth",
|
||||
AuthRequired: true,
|
||||
Fn: rcNoop,
|
||||
Title: "Echo the input to the output parameters requiring auth",
|
||||
Help: `
|
||||
This echoes the input parameters to the output parameters for testing
|
||||
purposes. It can be used to check that rclone is still alive and to
|
||||
check that parameter passing is working properly.`,
|
||||
})
|
||||
Add(Call{
|
||||
|
|
|
@ -21,6 +21,7 @@ type Options struct {
|
|||
Enabled bool // set to enable the server
|
||||
Serve bool // set to serve files from remotes
|
||||
Files string // set to enable serving files locally
|
||||
NoAuth bool // set to disable auth checks on AuthRequired methods
|
||||
}
|
||||
|
||||
// DefaultOpt is the default values used for Options
|
||||
|
|
|
@ -19,5 +19,6 @@ func AddFlags(flagSet *pflag.FlagSet) {
|
|||
flags.BoolVarP(flagSet, &Opt.Enabled, "rc", "", false, "Enable the remote control server.")
|
||||
flags.StringVarP(flagSet, &Opt.Files, "rc-files", "", "", "Path to local files to serve on the HTTP server.")
|
||||
flags.BoolVarP(flagSet, &Opt.Serve, "rc-serve", "", false, "Enable the serving of remote objects.")
|
||||
flags.BoolVarP(flagSet, &Opt.NoAuth, "rc-no-auth", "", false, "Don't require auth for certain methods.")
|
||||
httpflags.AddFlagsPrefix(flagSet, "rc-", &Opt.HTTPOptions)
|
||||
}
|
||||
|
|
|
@ -159,6 +159,12 @@ func (s *Server) handlePost(w http.ResponseWriter, r *http.Request, path string)
|
|||
return
|
||||
}
|
||||
|
||||
// Check to see if it requires authorisation
|
||||
if !s.opt.NoAuth && call.AuthRequired && !s.UsingAuth() {
|
||||
writeError(path, in, w, errors.Errorf("authentication must be set up on the rc server to use %q or the --rc-no-auth flag must be in use", path), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Check to see if it is async or not
|
||||
isAsync, err := in.GetBool("_async")
|
||||
if rc.NotErrParamNotFound(err) {
|
||||
|
|
|
@ -537,6 +537,65 @@ func TestNoServe(t *testing.T) {
|
|||
testServer(t, tests, &opt)
|
||||
}
|
||||
|
||||
func TestAuthRequired(t *testing.T) {
|
||||
tests := []testRun{{
|
||||
Name: "auth",
|
||||
URL: "rc/noopauth",
|
||||
Method: "POST",
|
||||
Body: `{}`,
|
||||
ContentType: "application/javascript",
|
||||
Status: http.StatusForbidden,
|
||||
Expected: `{
|
||||
"error": "authentication must be set up on the rc server to use \"rc/noopauth\" or the --rc-no-auth flag must be in use",
|
||||
"input": {},
|
||||
"path": "rc/noopauth",
|
||||
"status": 403
|
||||
}
|
||||
`,
|
||||
}}
|
||||
opt := newTestOpt()
|
||||
opt.Serve = false
|
||||
opt.Files = ""
|
||||
opt.NoAuth = false
|
||||
testServer(t, tests, &opt)
|
||||
}
|
||||
|
||||
func TestNoAuth(t *testing.T) {
|
||||
tests := []testRun{{
|
||||
Name: "auth",
|
||||
URL: "rc/noopauth",
|
||||
Method: "POST",
|
||||
Body: `{}`,
|
||||
ContentType: "application/javascript",
|
||||
Status: http.StatusOK,
|
||||
Expected: "{}\n",
|
||||
}}
|
||||
opt := newTestOpt()
|
||||
opt.Serve = false
|
||||
opt.Files = ""
|
||||
opt.NoAuth = true
|
||||
testServer(t, tests, &opt)
|
||||
}
|
||||
|
||||
func TestWithUserPass(t *testing.T) {
|
||||
tests := []testRun{{
|
||||
Name: "auth",
|
||||
URL: "rc/noopauth",
|
||||
Method: "POST",
|
||||
Body: `{}`,
|
||||
ContentType: "application/javascript",
|
||||
Status: http.StatusOK,
|
||||
Expected: "{}\n",
|
||||
}}
|
||||
opt := newTestOpt()
|
||||
opt.Serve = false
|
||||
opt.Files = ""
|
||||
opt.NoAuth = false
|
||||
opt.HTTPOptions.BasicUser = "user"
|
||||
opt.HTTPOptions.BasicPass = "pass"
|
||||
testServer(t, tests, &opt)
|
||||
}
|
||||
|
||||
func TestRCAsync(t *testing.T) {
|
||||
tests := []testRun{{
|
||||
Name: "ok",
|
||||
|
|
|
@ -16,10 +16,11 @@ type Func func(in Params) (out Params, err error)
|
|||
// Call defines info about a remote control function and is used in
|
||||
// the Add function to create new entry points.
|
||||
type Call struct {
|
||||
Path string // path to activate this RC
|
||||
Fn Func `json:"-"` // function to call
|
||||
Title string // help for the function
|
||||
Help string // multi-line markdown formatted help
|
||||
Path string // path to activate this RC
|
||||
Fn Func `json:"-"` // function to call
|
||||
Title string // help for the function
|
||||
AuthRequired bool // if set then this call requires authorisation to be set
|
||||
Help string // multi-line markdown formatted help
|
||||
}
|
||||
|
||||
// Registry holds the list of all the registered remote control functions
|
||||
|
|
Loading…
Reference in a new issue