Skip to content

Commit

Permalink
feature: add authentication support
Browse files Browse the repository at this point in the history
Resolves: #6
  • Loading branch information
greenpau committed Jan 13, 2022
1 parent 2b082aa commit d068730
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 37 deletions.
2 changes: 2 additions & 0 deletions assets/config/ssh/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
repo authp.github.io {
base_dir ./tmp/ssh
url git@github.com:authp/authp.github.io.git
# auth key ~/.ssh/id_rsa passphrase {env.MY_SSH_KEY_PASSPHRASE}
# auth key ~/.ssh/id_rsa passphrase {env.MY_SSH_KEY_PASSPHRASE} no_strict_host_key_check
auth key ~/.ssh/id_rsa
branch gh-pages
depth 1
Expand Down
38 changes: 26 additions & 12 deletions caddyfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func init() {
// repo <name> {
// base_dir <path>
// url <path>
// auth key <path> [passcode <passcode>
// auth key <path> [passphrase <passphrase>] [no_strict_host_key_check]
// auth username <username> password <password>
// webhook <name> <header> <secret>
// branch <name>
Expand All @@ -61,7 +61,7 @@ const badRepl string = "ERROR_BAD_REPL"
var argRules = map[string]argRule{
"base_dir": argRule{Min: 1, Max: 1},
"url": argRule{Min: 1, Max: 1},
"auth": argRule{Min: 2, Max: 5},
"auth": argRule{Min: 2, Max: 255},
"branch": argRule{Min: 1, Max: 1},
"depth": argRule{Min: 1, Max: 1},
"update": argRule{Min: 1, Max: 255},
Expand Down Expand Up @@ -109,21 +109,26 @@ func parseCaddyfileAppConfig(d *caddyfile.Dispenser, _ interface{}) (interface{}
authCfg := &service.AuthConfig{}
switch v[0] {
case "key":
switch len(v) {
case 2:
authCfg.KeyPath = v[1]
case 4:
authCfg.KeyPath = v[1]
authCfg.KeyPassphrase = v[3]
default:
return nil, d.Errf("malformed %q directive", k)
if len(v) < 2 {
return nil, d.Errf("malformed %q directive: %v", k, v)
}
authCfg.KeyPath = v[1]
if len(v) > 2 {
if v[2] == "passphrase" {
authCfg.KeyPassphrase = v[3]
}
}
case "username":
if len(v) != 4 {
if len(v) < 4 {
return nil, d.Errf("malformed %q directive", k)
}
authCfg.Username = v[1]
authCfg.Password = v[3]
if v[2] == "password" {
authCfg.Password = v[3]
}
}
if findString(v, "no_strict_host_key_check") {
authCfg.StrictHostKeyCheckingDisabled = true
}
rc.Auth = authCfg
case "webhook":
Expand Down Expand Up @@ -240,3 +245,12 @@ func findReplace(repl *caddy.Replacer, arr []string) (output []string) {
}
return output
}

func findString(arr []string, s string) bool {
for _, x := range arr {
if x == s {
return true
}
}
return false
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ require (
github.com/go-git/go-git/v5 v5.3.0
github.com/google/go-cmp v0.5.6
go.uber.org/zap v1.20.0
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
)
19 changes: 13 additions & 6 deletions pkg/service/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ type Config struct {

// AuthConfig is authentication configuration in RepositoryConfig.
type AuthConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
KeyPath string `json:"key_path,omitempty"`
KeyPassphrase string `json:"key_passphrase,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
KeyPath string `json:"key_path,omitempty"`
KeyPassphrase string `json:"key_passphrase,omitempty"`
StrictHostKeyCheckingDisabled bool `json:"strict_host_key_checking_disabled,omitempty"`
}

// WebhookConfig is a webhook configuration in RepositoryConfig.
Expand Down Expand Up @@ -60,6 +61,7 @@ type RepositoryConfig struct {
Auth *AuthConfig `json:"auth,omitempty"`
Webhooks []*WebhookConfig `json:"webhooks,omitempty"`
PostPullExec []*ExecConfig `json:"post_pull_exec,omitempty"`
transport string `json:"transport,omitempty"`
}

// NewConfig returns an instance of Config.
Expand Down Expand Up @@ -99,10 +101,15 @@ func (rc *RepositoryConfig) validate() error {
if rc.Address == "" {
return errors.ErrRepositoryConfigAddressEmpty
}
if !strings.HasSuffix(rc.Address, ".git") {
return errors.ErrRepositoryConfigAddressUnsupported.WithArgs(rc.Address)
}

switch {
case strings.HasSuffix(rc.Address, ".git"):
case strings.HasPrefix(rc.Address, "https://"), strings.HasPrefix(rc.Address, "http://"):
rc.transport = "http"
default:
return errors.ErrRepositoryConfigAddressUnsupported.WithArgs(rc.Address)
rc.transport = "ssh"
}
return nil
}
136 changes: 117 additions & 19 deletions pkg/service/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ package service
import (
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"go.uber.org/zap"
cryptossh "golang.org/x/crypto/ssh"
"os"
"path"
"path/filepath"
"strings"
"sync"
"time"
)
Expand All @@ -45,6 +50,8 @@ func (r *Repository) update() error {
r.mu.Lock()
defer r.mu.Unlock()

r.Config.BaseDir = expandDir(r.Config.BaseDir)

baseDirExists, err := dirExists(r.Config.BaseDir)
if err != nil {
return err
Expand All @@ -62,19 +69,13 @@ func (r *Repository) update() error {
}
if !repoDirExists {
// Clone the repository.
opts := &git.CloneOptions{
URL: r.Config.Address,
}
if r.Config.Depth > 0 {
opts.Depth = r.Config.Depth
}
if r.Config.Branch != "" {
opts.ReferenceName = plumbing.NewBranchReferenceName(r.Config.Branch)
opts := &git.CloneOptions{}
if err := configureCloneOptions(r.Config, opts); err != nil {
return err
}
if _, err := git.PlainClone(repoDir, false, opts); err != nil {
return err
}
// return nil
}

// Pull the repository.
Expand All @@ -90,18 +91,11 @@ func (r *Repository) update() error {
if err != nil {
return err
}
opts := &git.PullOptions{
RemoteName: "origin",
SingleBranch: true,
}
if r.Config.Branch != "" {
opts.ReferenceName = plumbing.NewBranchReferenceName(r.Config.Branch)
}

if r.Config.Depth > 0 {
opts.Depth = r.Config.Depth
opts := &git.PullOptions{}
if err := configurePullOptions(r.Config, opts); err != nil {
return err
}

if err := w.Pull(opts); err != nil {
if err == git.NoErrAlreadyUpToDate {
r.logger.Debug(
Expand Down Expand Up @@ -140,3 +134,107 @@ func dirExists(s string) (bool, error) {
return true, err

}

func configureCloneOptions(cfg *RepositoryConfig, opts *git.CloneOptions) error {
opts.URL = cfg.Address
trAuthMethod, err := configureAuthOptions(cfg)
if err != nil {
return err
}
opts.Auth = trAuthMethod
if cfg.Depth > 0 {
opts.Depth = cfg.Depth
}
if cfg.Branch != "" {
opts.ReferenceName = plumbing.NewBranchReferenceName(cfg.Branch)
}
return nil
}

func configurePullOptions(cfg *RepositoryConfig, opts *git.PullOptions) error {
opts.RemoteName = "origin"
trAuthMethod, err := configureAuthOptions(cfg)
if err != nil {
return err
}
opts.Auth = trAuthMethod
if cfg.Depth > 0 {
opts.Depth = cfg.Depth
}
if cfg.Branch != "" {
opts.ReferenceName = plumbing.NewBranchReferenceName(cfg.Branch)
opts.SingleBranch = true
}
return nil
}

func configureAuthOptions(cfg *RepositoryConfig) (transport.AuthMethod, error) {
if cfg.Auth == nil {
return nil, nil
}
cfg.Auth.KeyPath = expandDir(cfg.Auth.KeyPath)

switch cfg.transport {
case "http":
// Configure authentication for HTTP/S.
switch {
case cfg.Auth.Username != "":
return &http.BasicAuth{
Username: cfg.Auth.Username,
Password: cfg.Auth.Password,
}, nil
}
case "ssh":
// Configure authentication for SSH.
switch {
case cfg.Auth.KeyPath != "":
var publicKeysUser string
switch {
case strings.Contains(cfg.Address, "@"):
cfgAddressArr := strings.SplitN(cfg.Address, "@", 2)
publicKeysUser = cfgAddressArr[0]
case cfg.Auth.Username != "":
publicKeysUser = cfg.Auth.Username
}

if publicKeysUser == "" {
publicKeysUser = "git"
}

publicKeys, err := ssh.NewPublicKeysFromFile(publicKeysUser, cfg.Auth.KeyPath, cfg.Auth.KeyPassphrase)
if err != nil {
return nil, err
}
if cfg.Auth.StrictHostKeyCheckingDisabled {
publicKeys.HostKeyCallbackHelper = ssh.HostKeyCallbackHelper{
HostKeyCallback: cryptossh.InsecureIgnoreHostKey(),
}
}
return publicKeys, nil
case cfg.Auth.Username != "":
password := &ssh.Password{
User: cfg.Auth.Username,
Password: cfg.Auth.Password,
}
if cfg.Auth.StrictHostKeyCheckingDisabled {
password.HostKeyCallbackHelper = ssh.HostKeyCallbackHelper{
HostKeyCallback: cryptossh.InsecureIgnoreHostKey(),
}
}
return password, nil
}
}
return nil, nil
}

func expandDir(s string) string {
if s == "" || !strings.HasPrefix(s, "~") {
return s
}
hd, err := os.UserHomeDir()
if err != nil {
return s
}
output := hd + s[1:]
return output
}

0 comments on commit d068730

Please sign in to comment.