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:
Nick Craig-Wood 2018-11-03 16:37:09 +00:00
parent 181267e20e
commit fa0a9653d2
8 changed files with 102 additions and 4 deletions

View file

@ -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
}

View file

@ -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

View file

@ -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{

View file

@ -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

View file

@ -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)
}

View file

@ -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) {

View file

@ -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",

View file

@ -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