Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI support for custom credentials for OneDrive (client_id/client_secret) #632

Merged
merged 1 commit into from Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 35 additions & 8 deletions src/duplicacy_oneclient.go
Expand Up @@ -5,6 +5,7 @@
package duplicacy

import (
"context"
"bytes"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -39,6 +40,7 @@ type OneDriveClient struct {

TokenFile string
Token *oauth2.Token
OAConfig *oauth2.Config
TokenLock *sync.Mutex

IsConnected bool
Expand All @@ -49,7 +51,7 @@ type OneDriveClient struct {
APIURL string
}

func NewOneDriveClient(tokenFile string, isBusiness bool) (*OneDriveClient, error) {
func NewOneDriveClient(tokenFile string, isBusiness bool, client_id string, client_secret string) (*OneDriveClient, error) {

description, err := ioutil.ReadFile(tokenFile)
if err != nil {
Expand All @@ -65,10 +67,25 @@ func NewOneDriveClient(tokenFile string, isBusiness bool) (*OneDriveClient, erro
HTTPClient: http.DefaultClient,
TokenFile: tokenFile,
Token: token,
OAConfig: nil,
TokenLock: &sync.Mutex{},
IsBusiness: isBusiness,
}

if (client_id != "") {
oneOauthConfig := oauth2.Config{
ClientID: client_id,
ClientSecret: client_secret,
Scopes: []string{"Files.ReadWrite", "offline_access"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
TokenURL: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
},
}

client.OAConfig = &oneOauthConfig
}

if isBusiness {
client.RefreshTokenURL = "https://duplicacy.com/odb_refresh"
client.APIURL = "https://graph.microsoft.com/v1.0/me"
Expand Down Expand Up @@ -218,15 +235,25 @@ func (client *OneDriveClient) RefreshToken(force bool) (err error) {
return nil
}

readCloser, _, err := client.call(client.RefreshTokenURL, "POST", client.Token, "")
if err != nil {
return fmt.Errorf("failed to refresh the access token: %v", err)
}
if (client.OAConfig == nil) {
readCloser, _, err := client.call(client.RefreshTokenURL, "POST", client.Token, "")
if err != nil {
return fmt.Errorf("failed to refresh the access token: %v", err)
}

defer readCloser.Close()
defer readCloser.Close()

if err = json.NewDecoder(readCloser).Decode(client.Token); err != nil {
return err
if err = json.NewDecoder(readCloser).Decode(client.Token); err != nil {
return err
}
} else {
ctx := context.Background()
tokenSource := client.OAConfig.TokenSource(ctx, client.Token)
token, err := tokenSource.Token()
if err != nil {
return fmt.Errorf("failed to refresh the access token: %v", err)
}
client.Token = token
}

description, err := json.Marshal(client.Token)
Expand Down
4 changes: 2 additions & 2 deletions src/duplicacy_onestorage.go
Expand Up @@ -19,13 +19,13 @@ type OneDriveStorage struct {
}

// CreateOneDriveStorage creates an OneDrive storage object.
func CreateOneDriveStorage(tokenFile string, isBusiness bool, storagePath string, threads int) (storage *OneDriveStorage, err error) {
func CreateOneDriveStorage(tokenFile string, isBusiness bool, storagePath string, threads int, client_id string, client_secret string) (storage *OneDriveStorage, err error) {

for len(storagePath) > 0 && storagePath[len(storagePath)-1] == '/' {
storagePath = storagePath[:len(storagePath)-1]
}

client, err := NewOneDriveClient(tokenFile, isBusiness)
client, err := NewOneDriveClient(tokenFile, isBusiness, client_id, client_secret)
if err != nil {
return nil, err
}
Expand Down
18 changes: 17 additions & 1 deletion src/duplicacy_storage.go
Expand Up @@ -647,12 +647,28 @@ func CreateStorage(preference Preference, resetPassword bool, threads int) (stor
storagePath := matched[3] + matched[4]
prompt := fmt.Sprintf("Enter the path of the OneDrive token file (downloadable from https://duplicacy.com/one_start):")
tokenFile := GetPassword(preference, matched[1] + "_token", prompt, true, resetPassword)
oneDriveStorage, err := CreateOneDriveStorage(tokenFile, matched[1] == "odb", storagePath, threads)

// client_id, just like tokenFile, can be stored in preferences
//prompt = fmt.Sprintf("Enter client_id for custom Azure app (if empty will use duplicacy.com one):")
client_id := GetPasswordFromPreference(preference, matched[1] + "_client_id")
client_secret := ""

if client_id != "" {
// client_secret should go into keyring
prompt = fmt.Sprintf("Enter client_secret for custom Azure app (if empty will use duplicacy.com one):")
client_secret = GetPassword(preference, matched[1] + "_client_secret", prompt, true, resetPassword)
}

oneDriveStorage, err := CreateOneDriveStorage(tokenFile, matched[1] == "odb", storagePath, threads, client_id, client_secret)
if err != nil {
LOG_ERROR("STORAGE_CREATE", "Failed to load the OneDrive storage at %s: %v", storageURL, err)
return nil
}

SavePassword(preference, matched[1] + "_token", tokenFile)
if client_id != "" {
SavePassword(preference, matched[1] + "_client_secret", client_secret)
}
return oneDriveStorage
} else if matched[1] == "hubic" {
storagePath := matched[3] + matched[4]
Expand Down