forked from TrueCloudLab/rclone
authorize: add support for custom templates
This adds support for providing custom Go templates for use in the `rclone authorize` command. Fixes #6741
This commit is contained in:
parent
745c0af571
commit
56b582cdb9
7 changed files with 54 additions and 10 deletions
|
@ -12,12 +12,14 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
noAutoBrowser bool
|
noAutoBrowser bool
|
||||||
|
template string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.Root.AddCommand(commandDefinition)
|
cmd.Root.AddCommand(commandDefinition)
|
||||||
cmdFlags := commandDefinition.Flags()
|
cmdFlags := commandDefinition.Flags()
|
||||||
flags.BoolVarP(cmdFlags, &noAutoBrowser, "auth-no-open-browser", "", false, "Do not automatically open auth link in default browser")
|
flags.BoolVarP(cmdFlags, &noAutoBrowser, "auth-no-open-browser", "", false, "Do not automatically open auth link in default browser")
|
||||||
|
flags.StringVarP(cmdFlags, &template, "template", "", "", "The path to a custom Go template for generating HTML responses")
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandDefinition = &cobra.Command{
|
var commandDefinition = &cobra.Command{
|
||||||
|
@ -28,13 +30,15 @@ Remote authorization. Used to authorize a remote or headless
|
||||||
rclone from a machine with a browser - use as instructed by
|
rclone from a machine with a browser - use as instructed by
|
||||||
rclone config.
|
rclone config.
|
||||||
|
|
||||||
Use the --auth-no-open-browser to prevent rclone to open auth
|
Use --auth-no-open-browser to prevent rclone to open auth
|
||||||
link in default browser automatically.`,
|
link in default browser automatically.
|
||||||
|
|
||||||
|
Use --template to generate HTML output via a custom Go template. If a blank string is provided as an argument to this flag, the default template is used.`,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"versionIntroduced": "v1.27",
|
"versionIntroduced": "v1.27",
|
||||||
},
|
},
|
||||||
RunE: func(command *cobra.Command, args []string) error {
|
RunE: func(command *cobra.Command, args []string) error {
|
||||||
cmd.CheckArgs(1, 3, command, args)
|
cmd.CheckArgs(1, 3, command, args)
|
||||||
return config.Authorize(context.Background(), args, noAutoBrowser)
|
return config.Authorize(context.Background(), args, noAutoBrowser, template)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Code generated by vfsgen; DO NOT EDIT.
|
// Code generated by vfsgen; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !dev
|
||||||
// +build !dev
|
// +build !dev
|
||||||
|
|
||||||
package data
|
package data
|
||||||
|
|
|
@ -17,9 +17,13 @@ Remote authorization. Used to authorize a remote or headless
|
||||||
rclone from a machine with a browser - use as instructed by
|
rclone from a machine with a browser - use as instructed by
|
||||||
rclone config.
|
rclone config.
|
||||||
|
|
||||||
Use the --auth-no-open-browser to prevent rclone to open auth
|
Use --auth-no-open-browser to prevent rclone to open auth
|
||||||
link in default browser automatically.
|
link in default browser automatically.
|
||||||
|
|
||||||
|
Use --template to generate HTML output via a custom Go
|
||||||
|
template. If a blank string is provided as an argument to
|
||||||
|
this flag, the default template is used.
|
||||||
|
|
||||||
```
|
```
|
||||||
rclone authorize [flags]
|
rclone authorize [flags]
|
||||||
```
|
```
|
||||||
|
@ -29,6 +33,7 @@ rclone authorize [flags]
|
||||||
```
|
```
|
||||||
--auth-no-open-browser Do not automatically open auth link in default browser
|
--auth-no-open-browser Do not automatically open auth link in default browser
|
||||||
-h, --help help for authorize
|
-h, --help help for authorize
|
||||||
|
--template string Use a custom Go template for generating HTML responses
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [global flags page](/flags/) for global options not listed here.
|
See the [global flags page](/flags/) for global options not listed here.
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
// rclone authorize "fs name"
|
// rclone authorize "fs name"
|
||||||
// rclone authorize "fs name" "base64 encoded JSON blob"
|
// rclone authorize "fs name" "base64 encoded JSON blob"
|
||||||
// rclone authorize "fs name" "client id" "client secret"
|
// rclone authorize "fs name" "client id" "client secret"
|
||||||
func Authorize(ctx context.Context, args []string, noAutoBrowser bool) error {
|
func Authorize(ctx context.Context, args []string, noAutoBrowser bool, templateFile string) error {
|
||||||
ctx = suppressConfirm(ctx)
|
ctx = suppressConfirm(ctx)
|
||||||
ctx = fs.ConfigOAuthOnly(ctx)
|
ctx = fs.ConfigOAuthOnly(ctx)
|
||||||
switch len(args) {
|
switch len(args) {
|
||||||
|
@ -41,6 +41,11 @@ func Authorize(ctx context.Context, args []string, noAutoBrowser bool) error {
|
||||||
inM[ConfigAuthNoBrowser] = "true"
|
inM[ConfigAuthNoBrowser] = "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indicate if we specified a custom template via a file
|
||||||
|
if templateFile != "" {
|
||||||
|
inM[ConfigTemplateFile] = templateFile
|
||||||
|
}
|
||||||
|
|
||||||
// Add extra parameters if supplied
|
// Add extra parameters if supplied
|
||||||
if len(args) == 2 {
|
if len(args) == 2 {
|
||||||
err := inM.Decode(args[1])
|
err := inM.Decode(args[1])
|
||||||
|
|
|
@ -58,6 +58,12 @@ const (
|
||||||
|
|
||||||
// ConfigAuthNoBrowser indicates that we do not want to open browser
|
// ConfigAuthNoBrowser indicates that we do not want to open browser
|
||||||
ConfigAuthNoBrowser = "config_auth_no_browser"
|
ConfigAuthNoBrowser = "config_auth_no_browser"
|
||||||
|
|
||||||
|
// ConfigTemplate is the template content to be used in the authorization webserver
|
||||||
|
ConfigTemplate = "config_template"
|
||||||
|
|
||||||
|
// ConfigTemplateFile is the path to a template file to read into the value of `ConfigTemplate` above
|
||||||
|
ConfigTemplateFile = "config_template_file"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Storage defines an interface for loading and saving config to
|
// Storage defines an interface for loading and saving config to
|
||||||
|
|
|
@ -77,7 +77,7 @@ func unusedDrive(t *testing.T) string {
|
||||||
return string(letter) + ":"
|
return string(letter) + ":"
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkMkdirAll(t *testing.T, path string, valid bool, errormsgs... string) {
|
func checkMkdirAll(t *testing.T, path string, valid bool, errormsgs ...string) {
|
||||||
if valid {
|
if valid {
|
||||||
assert.NoError(t, MkdirAll(path, 0777))
|
assert.NoError(t, MkdirAll(path, 0777))
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,7 +93,7 @@ func checkMkdirAll(t *testing.T, path string, valid bool, errormsgs... string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkMkdirAllSubdirs(t *testing.T, path string, valid bool, errormsgs... string) {
|
func checkMkdirAllSubdirs(t *testing.T, path string, valid bool, errormsgs ...string) {
|
||||||
checkMkdirAll(t, path, valid, errormsgs...)
|
checkMkdirAll(t, path, valid, errormsgs...)
|
||||||
checkMkdirAll(t, path+`\`, valid, errormsgs...)
|
checkMkdirAll(t, path+`\`, valid, errormsgs...)
|
||||||
checkMkdirAll(t, path+`\parent`, valid, errormsgs...)
|
checkMkdirAll(t, path+`\parent`, valid, errormsgs...)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -24,6 +25,11 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// templateString is the template used in the authorization webserver
|
||||||
|
templateString string
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TitleBarRedirectURL is the OAuth2 redirect URL to use when the authorization
|
// TitleBarRedirectURL is the OAuth2 redirect URL to use when the authorization
|
||||||
// code should be returned in the title bar of the browser, with the page text
|
// code should be returned in the title bar of the browser, with the page text
|
||||||
|
@ -49,8 +55,8 @@ const (
|
||||||
// redirects to the local webserver
|
// redirects to the local webserver
|
||||||
RedirectPublicSecureURL = "https://oauth.rclone.org/"
|
RedirectPublicSecureURL = "https://oauth.rclone.org/"
|
||||||
|
|
||||||
// AuthResponseTemplate is a template to handle the redirect URL for oauth requests
|
// DefaultAuthResponseTemplate is the default template used in the authorization webserver
|
||||||
AuthResponseTemplate = `<!DOCTYPE html>
|
DefaultAuthResponseTemplate = `<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
@ -587,6 +593,23 @@ version recommended):
|
||||||
}
|
}
|
||||||
return fs.ConfigGoto(newState("*oauth-done"))
|
return fs.ConfigGoto(newState("*oauth-done"))
|
||||||
case "*oauth-do":
|
case "*oauth-do":
|
||||||
|
// Make sure we can read the HTML template file if it was specified.
|
||||||
|
configTemplateFile, _ := m.Get("config_template_file")
|
||||||
|
configTemplateString, _ := m.Get("config_template")
|
||||||
|
|
||||||
|
if configTemplateFile != "" {
|
||||||
|
dat, err := os.ReadFile(configTemplateFile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read template file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
templateString = string(dat)
|
||||||
|
} else if configTemplateString != "" {
|
||||||
|
templateString = configTemplateString
|
||||||
|
} else {
|
||||||
|
templateString = DefaultAuthResponseTemplate
|
||||||
|
}
|
||||||
code := in.Result
|
code := in.Result
|
||||||
opt, err := getOAuth()
|
opt, err := getOAuth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -755,7 +778,7 @@ func (s *authServer) handleAuth(w http.ResponseWriter, req *http.Request) {
|
||||||
reply := func(status int, res *AuthResult) {
|
reply := func(status int, res *AuthResult) {
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
w.Header().Set("Content-Type", "text/html")
|
w.Header().Set("Content-Type", "text/html")
|
||||||
var t = template.Must(template.New("authResponse").Parse(AuthResponseTemplate))
|
var t = template.Must(template.New("authResponse").Parse(templateString))
|
||||||
if err := t.Execute(w, res); err != nil {
|
if err := t.Execute(w, res); err != nil {
|
||||||
fs.Debugf(nil, "Could not execute template for web response.")
|
fs.Debugf(nil, "Could not execute template for web response.")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue