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

Added support for different credential providers #11

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,12 @@ func createConfigFile() {
# Connection settings for deployment target (FTP only)
ftp:
host: <enter host id / ip address>
port: <enter port - usually 21 for FTP over TLS>
port: <enter port - usually 21 for FTP over TLS>
rootdir: <enter root directory of website, e.g. /public_html/>
disabletls: false
credentialProvider: classic
user: <enter user id>
pwd: <enter password>
rootdir: <enter root directory of website, e.g. /public_html/>
disabletls: false

# Connection settings for deployment target (SFTP only)
sftp:
Expand Down
35 changes: 35 additions & 0 deletions cred/classic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cred

import (
"errors"
"github.com/spf13/viper"
)

/**
CredentialProvider which reads the credentials directly from the config file
(old behavior)
**/
type ClassicCredProvider struct {
}

func (cstore ClassicCredProvider) GetCredentials(connectionType string) (credentials *Credentials, err error) {

uid := viper.GetString(connectionType + ".user")
pwd := viper.GetString(connectionType + ".pwd")

serr := ""

if uid == "" {
serr = serr + "UID not found. Define " + connectionType + ".user in config file. "
}
if uid == "" {
serr = serr + "PWD not found. Define " + connectionType + ".pwd in config file. "
}

if (serr != "") {
return nil, errors.New(serr)
}

return &Credentials{ UID: uid, PWD: pwd}, nil
}

26 changes: 26 additions & 0 deletions cred/cred.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cred

type Credentials struct {
UID string
PWD string
}

type CredentialProvider interface {
// connectionType e.g. ftp, sftp
GetCredentials(connectionType string) (*Credentials, error)
}

func GetCredentialProvider(identifier string) CredentialProvider {
switch identifier {
case "classic":
return ClassicCredProvider{}
case "interactive":
return InteractiveCredProvider{}
case "pwdfile":
return PwdFileCredProvider{}
case "wincred":
return WinCredProvider{}
default:
return nil;
}
}
68 changes: 68 additions & 0 deletions cred/interactive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cred

import (
"fmt"
"os"
"bufio"
"github.com/eiannone/keyboard"
"github.com/spf13/viper"
)

/**
CredentialProvider which asks the user to input their credentials on stdin
once they are required
**/
type InteractiveCredProvider struct {
}

func readPwd() (pwd string, err error) {
s := ""

for {
char, key, err := keyboard.GetSingleKey()

if (err != nil) {
return "", err
}

if (key == keyboard.KeyEnter) {
break
}

s += string(char)
}

return s, err
}

func (cstore InteractiveCredProvider) GetCredentials(connectionType string) (credentials *Credentials, err error) {
reader := bufio.NewReader(os.Stdin)

fmt.Println("Please enter credentials for connection type " + connectionType);

// is the username preset?
uname := viper.GetString(connectionType + ".user")

if (uname != "") {
fmt.Println("Username: " + uname);
} else {
// user has to enter username
fmt.Print("Enter username: ");
unameBy, _, err := reader.ReadLine()
uname = string(unameBy)

if (err != nil) {
return nil, err
}
}

fmt.Print("Enter password (input will be hidden): ");
pwd, err := readPwd();

if (err != nil) {
return nil, err
}

return &Credentials{ UID: uname, PWD: pwd }, nil
}

44 changes: 44 additions & 0 deletions cred/pwdfile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package cred

import (
"errors"
"github.com/spf13/viper"
)

/**
CredentialProvider which reads the credentials from a separate file
**/
type PwdFileCredProvider struct {
}

func (cstore PwdFileCredProvider) GetCredentials(connectionType string) (credentials *Credentials, err error) {

pwdfile := viper.GetString(connectionType + ".pwdfile")

pwdConfig := viper.New()
pwdConfig.SetConfigName(pwdfile)
pwdConfig.AddConfigPath(".")
err = pwdConfig.ReadInConfig()
if err != nil {
return nil, err
}

uid := pwdConfig.GetString(connectionType + ".user")
pwd := pwdConfig.GetString(connectionType + ".pwd")

serr := ""

if uid == "" {
serr = serr + "UID not found. Define " + connectionType + ".user in credential config file. "
}
if uid == "" {
serr = serr + "PWD not found. Define " + connectionType + ".pwd in credential config file. "
}

if (serr != "") {
return nil, errors.New(serr)
}

return &Credentials{ UID: uid, PWD: pwd}, nil
}

16 changes: 16 additions & 0 deletions cred/wincred_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// +build !windows

package cred

import "errors"

/**
Credential Provider which retrieves the credentials from the Windows Credential Manager
(Windows only, naturally)
**/
type WinCredProvider struct {
}

func (cstore WinCredProvider) GetCredentials(connectionType string) (credentials *Credentials, err error) {
return nil, errors.New("The wincred credential provider is only supported on Windows");
}
35 changes: 35 additions & 0 deletions cred/wincred_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cred

import (
"errors"
"strings"
"github.com/danieljoos/wincred"
"github.com/spf13/viper"
)

/**
Credential Provider which retrieves the credentials from the Windows Credential Manager
(Windows only, naturally)
**/
type WinCredProvider struct {
}

func (cstore WinCredProvider) GetCredentials(connectionType string) (credentials *Credentials, err error) {
identifier := viper.GetString(connectionType + ".wincred-identifier")

if (identifier == "") {
return nil, errors.New("Wincred identifier not found. Define " + connectionType +".wincred-identifier in config file.")
}

cred, err := wincred.GetGenericCredential(identifier)
if err != nil {
return nil, err
}

// the returned password contains null bytes between
// the characters for some reason -> remove these
pwd := string(cred.CredentialBlob)
pwd = strings.Replace(pwd, string(0), "", -1)

return &Credentials{ UID: cred.UserName, PWD: pwd }, nil
}
44 changes: 31 additions & 13 deletions deploy/ftp.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import (
"path"
"strings"
"os"

"github.com/dutchcoders/goftp"
"github.com/mindok/hugodeploy/cred"

jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
Expand All @@ -47,8 +49,32 @@ func (f *FTPDeployer) Initialise() error {
// Gather together settings
f.HostID = viper.GetString("ftp.host")
f.Port = viper.GetString("ftp.port")
f.UID = viper.GetString("ftp.user")
f.PWD = viper.GetString("ftp.pwd")

// get credentials
credProviderName := viper.GetString("ftp.credentialProvider")
if (credProviderName == "") {
// when no credental provider is given
// fall back to old style credentials
credProviderName = "classic"
}

jww.DEBUG.Println("CredentialProvider is ", credProviderName)

credProvider := cred.GetCredentialProvider(credProviderName)
if (credProvider == nil) {
jww.ERROR.Println("Unknown or unsupported credential provider: ", credProviderName)
return errors.New ( "Unknown or unsupported credential provider: " + credProviderName );
}

credentials, err := credProvider.GetCredentials("ftp")
if (err != nil) {
jww.ERROR.Println("Unable to retrieve credentials: ", err)
return err;
}

f.UID = credentials.UID
f.PWD = credentials.PWD

f.RootDir = viper.GetString("ftp.rootdir")
f.DisableTLS = false
if viper.IsSet("ftp.disabletls") {
Expand All @@ -62,13 +88,7 @@ func (f *FTPDeployer) Initialise() error {
}
if f.Port == "" {
serr = serr + "Port not found. Define ftp.port in config file. "
}
if f.UID == "" {
serr = serr + "UID not found. Define ftp.user in config file. "
}
if f.PWD == "" {
serr = serr + "PWD not found. Define ftp.pwd in config file. "
}
}
if f.RootDir == "" {
f.RootDir = "/"
jww.WARN.Println("FTP: Website root directory not found (ftp: rootdir in config). Defaulting to '/'")
Expand All @@ -77,9 +97,7 @@ func (f *FTPDeployer) Initialise() error {
if serr != "" {
jww.ERROR.Println("Error initialising FTP Deployer: ", serr)
panic(errors.New("Error initialising FTP Deployer. " + serr))
}

var err error
}

jww.FEEDBACK.Println("Creating FTP connection... ")
//Create initial connection
Expand Down
40 changes: 29 additions & 11 deletions deploy/sftp.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
"golang.org/x/crypto/ssh"
"github.com/mindok/hugodeploy/cred"
)

/* NOTE: INCOMPLETE, UNTESTED CODE */
Expand All @@ -43,28 +44,45 @@ func (s *SFTPDeployer) Initialise() error {
// Gather together settings
s.HostID = viper.GetString("sftp.host")
s.Port = viper.GetString("sftp.port")
s.UID = viper.GetString("sftp.user")
s.PWD = viper.GetString("sftp.pwd")

// get credentials
credProviderName := viper.GetString("ftp.credentialProvider")
if (credProviderName == "") {
// when no credental provider is given
// fall back to old style credentials
credProviderName = "classic"
}

jww.DEBUG.Println("CredentialProvider is ", credProviderName)

credProvider := cred.GetCredentialProvider(credProviderName)
if (credProvider == nil) {
jww.ERROR.Println("Unknown or unsupported credential provider: ", credProviderName)
return errors.New ( "Unknown or unsupported credential provider: " + credProviderName );
}

credentials, err := credProvider.GetCredentials("sftp")
if (err != nil) {
jww.ERROR.Println("Unable to retrieve credentials: ", err)
return err;
}

s.UID = credentials.UID
s.PWD = credentials.PWD

jww.INFO.Println("Got SFTP settings: ", s.HostID, s.Port, s.UID)

if s.HostID == "" {
serr = serr + "HostID not found. Define sftp.host in config file. "
}
if s.Port == "" {
serr = serr + "Port not found. Define sftp.port in config file. "
}
if s.UID == "" {
serr = serr + "UID not found. Define sftp.user in config file. "
}
if s.PWD == "" {
serr = serr + "PWD not found. Define sftp.pwd in config file. "
}

}
if serr != "" {
return errors.New("Error initialising SFTP Deployer. " + serr)
}

err := errors.New("") //Must be away to avoid this, but double function returns below barf
err = errors.New("") //Must be away to avoid this, but double function returns below barf

//Attempt to connect. First create the SSH client:
config := &ssh.ClientConfig{
Expand Down