From 1a93f57ff8f6c3a5a3ab543055087f7b2a43c4b9 Mon Sep 17 00:00:00 2001 From: Vladimir Hodakov Date: Sat, 30 Mar 2019 04:31:31 +0400 Subject: [PATCH] Add ability to authorize on Yandex with needed permissions --- domains/yandex/v1/auth.go | 3 +- domains/yandex/v1/exported.go | 7 +++++ domains/yandex/v1/requests.go | 59 +++++++++++++++++++++++++++++++++++ domains/yandex/v1/structs.go | 9 ++++++ internal/context/context.go | 56 +++++++++++++++++++-------------- internal/context/exported.go | 2 ++ 6 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 domains/yandex/v1/requests.go create mode 100644 domains/yandex/v1/structs.go diff --git a/domains/yandex/v1/auth.go b/domains/yandex/v1/auth.go index 4ae4a7d..f5c5f80 100644 --- a/domains/yandex/v1/auth.go +++ b/domains/yandex/v1/auth.go @@ -14,10 +14,11 @@ import ( func authorize() { if !checkAuth() { baseURL := "https://oauth.yandex.ru/authorize?response_type=code&client_id={{ client_id }}" - baseURL += "&device_id={{ device_id }}&device_name=yapusher-cli&force_confirm=yes" + baseURL += "&device_id={{ device_id }}&device_name={{ device_name }}&force_confirm=yes" baseURL = strings.Replace(baseURL, "{{ client_id }}", YANDEX_APPID, 1) baseURL = strings.Replace(baseURL, "{{ device_id }}", c.Config.DeviceID, 1) + baseURL = strings.Replace(baseURL, "{{ device_name }}", DEVICE_NAME, 1) dlog.Info().Msg("Please open in your browser this URL and authorize the app. After getting the code restart the app with -authCode param (see -h for details).") dlog.Info().Msgf("Auth URL: %s", baseURL) diff --git a/domains/yandex/v1/exported.go b/domains/yandex/v1/exported.go index 925b685..524e65a 100644 --- a/domains/yandex/v1/exported.go +++ b/domains/yandex/v1/exported.go @@ -10,6 +10,8 @@ import ( ) const YANDEX_APPID = "7d8a0561fdc44c05bb6695b464403f9c" +const YANDEX_APPPW = "56e12e4ed0d64738bf441a47f68c7146" +const DEVICE_NAME = "yapusher-cli" var ( c *context.Context @@ -33,6 +35,11 @@ func New(cc *context.Context) { // Process handles authorization and files func Process() { + authCode, _ := c.Flagger.GetIntValue("authCode") + if authCode != 0 { + sendCode(authCode) + } + if !checkAuth() { authorize() } diff --git a/domains/yandex/v1/requests.go b/domains/yandex/v1/requests.go new file mode 100644 index 0000000..02df171 --- /dev/null +++ b/domains/yandex/v1/requests.go @@ -0,0 +1,59 @@ +// Yandex Disk File Pusher +// Copyright (c) 2019 Vladimir "fat0troll" Hodakov + +package yandexv1 + +import ( + "encoding/json" + "net/http" + "net/url" + "os" + "strconv" + "strings" +) + +func sendCode(code int) { + baseURL := "https://oauth.yandex.ru/token" + + client := http.Client{} + form := url.Values{} + form.Set("grant_type", "authorization_code") + form.Set("code", strconv.Itoa(code)) + form.Set("device_id", c.Config.DeviceID) + form.Set("device_name", DEVICE_NAME) + form.Set("client_id", YANDEX_APPID) + form.Set("client_secret", YANDEX_APPPW) + + req, _ := http.NewRequest("POST", baseURL, strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode()))) + + resp, err := client.Do(req) + if err != nil { + dlog.Fatal().Err(err).Msg("Failed to send request") + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + err = json.NewDecoder(resp.Body).Decode(&c.Config.Token) + if err != nil { + dlog.Error().Err(err).Msg("Failed to decode response") + } + + c.SaveConfig() + + dlog.Info().Msg("You've authorized successfully. Now you can use this app to upload files to your Yandex.Disk") + dlog.Info().Msg("See -h for details") + } else { + errorData := TokenError{} + err = json.NewDecoder(resp.Body).Decode(&errorData) + if err != nil { + dlog.Error().Err(err).Msg("Failed to decode response") + } + + dlog.Error().Interface("response", errorData).Msg("Got error from Yandex, not authorized. Please retry authorization") + authorize() + } + + os.Exit(0) +} diff --git a/domains/yandex/v1/structs.go b/domains/yandex/v1/structs.go new file mode 100644 index 0000000..99bdadf --- /dev/null +++ b/domains/yandex/v1/structs.go @@ -0,0 +1,9 @@ +// Yandex Disk File Pusher +// Copyright (c) 2019 Vladimir "fat0troll" Hodakov + +package yandexv1 + +type TokenError struct { + Error string `json:"error"` + ErrorDescription string `json:"error_description"` +} diff --git a/internal/context/context.go b/internal/context/context.go index 643d7b0..eb5b990 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -38,6 +38,21 @@ func (c *Context) initFlagger() { c.Flagger.Initialize() } +func (c *Context) readConfig() { + fh, err := os.Open(c.configFilePath) + if err != nil { + dlog.Fatal().Err(err).Msg("Failed to read config file") + } + + defer fh.Close() + + decoder := json.NewDecoder(fh) + err = decoder.Decode(&c.Config) + if err != nil { + dlog.Fatal().Err(err).Msg("Failed to decode config") + } +} + // Init is an initialization function for core context // Without these parts of the application we can't start at all func (c *Context) Init() { @@ -61,36 +76,31 @@ func (c *Context) InitConfig() { dlog.Debug().Str("config directory", configPath).Msg("Got config directory") configFile := filepath.Join(configPath, "settings.json") + c.configFilePath = configFile + if _, err = os.Stat(configFile); os.IsNotExist(err) { // Generating new config on first run dlog.Debug().Msg("Generating new config") c.generateDefaultConfig() - fh, err := os.Create(configFile) - if err != nil { - dlog.Fatal().Err(err).Msg("Failed to create config file") - } - defer fh.Close() - - encoder := json.NewEncoder(fh) - err = encoder.Encode(c.Config) - if err != nil { - dlog.Fatal().Err(err).Msg("Failed to encode config") - } + c.SaveConfig() } else { dlog.Debug().Msg("Using existing config") - fh, err := os.Open(configFile) - if err != nil { - dlog.Fatal().Err(err).Msg("Failed to read config file") - } - - defer fh.Close() - - decoder := json.NewDecoder(fh) - decoder.Decode(c.Config) - if err != nil { - dlog.Fatal().Err(err).Msg("Failed to decode config") - } + c.readConfig() + } +} + +func (c *Context) SaveConfig() { + fh, err := os.Create(c.configFilePath) + if err != nil { + dlog.Fatal().Err(err).Msg("Failed to create config file") + } + defer fh.Close() + + encoder := json.NewEncoder(fh) + err = encoder.Encode(&c.Config) + if err != nil { + dlog.Fatal().Err(err).Msg("Failed to encode config") } } diff --git a/internal/context/exported.go b/internal/context/exported.go index b165b1c..a088d3c 100644 --- a/internal/context/exported.go +++ b/internal/context/exported.go @@ -18,6 +18,8 @@ var ( // Context is the main application context. type Context struct { + configFilePath string + Config config.Config Flagger *flagger.Flagger Logger zerolog.Logger