175 lines
5.1 KiB
Go
175 lines
5.1 KiB
Go
/*
|
|
Copyright 2021 The Flux authors
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package git
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
const (
|
|
DefaultOrigin = "origin"
|
|
DefaultBranch = "master"
|
|
DefaultPublicKeyAuthUser = "git"
|
|
)
|
|
|
|
// CheckoutOptions are the options used for a Git checkout.
|
|
type CheckoutOptions struct {
|
|
// Branch to checkout, can be combined with Branch with some
|
|
// Implementations.
|
|
Branch string
|
|
|
|
// Tag to checkout, takes precedence over Branch.
|
|
Tag string
|
|
|
|
// SemVer tag expression to checkout, takes precedence over Tag.
|
|
SemVer string `json:"semver,omitempty"`
|
|
|
|
// Commit SHA1 to checkout, takes precedence over Tag and SemVer,
|
|
// can be combined with Branch with some Implementations.
|
|
Commit string
|
|
|
|
// RecurseSubmodules defines if submodules should be checked out,
|
|
// not supported by all Implementations.
|
|
RecurseSubmodules bool
|
|
|
|
// LastRevision holds the last observed revision of the local repository.
|
|
// It is used to skip clone operations when no changes were detected.
|
|
LastRevision string
|
|
}
|
|
|
|
type TransportType string
|
|
|
|
const (
|
|
SSH TransportType = "ssh"
|
|
HTTPS TransportType = "https"
|
|
HTTP TransportType = "http"
|
|
)
|
|
|
|
// AuthOptions are the authentication options for the Transport of
|
|
// communication with a remote origin.
|
|
type AuthOptions struct {
|
|
Transport TransportType
|
|
Host string
|
|
Username string
|
|
Password string
|
|
Identity []byte
|
|
KnownHosts []byte
|
|
CAFile []byte
|
|
// TransportOptionsURL is a unique identifier for this set of authentication
|
|
// options. It's used by managed libgit2 transports to uniquely identify
|
|
// which credentials to use for a particular Git operation, and avoid misuse
|
|
// of credentials in a multi-tenant environment.
|
|
// It must be prefixed with a valid transport protocol ("ssh:// "or "http://") because
|
|
// of the way managed transports are registered and invoked.
|
|
// It's a field of AuthOptions despite not providing any kind of authentication
|
|
// info, as it's the only way to sneak it into git.Checkout, without polluting
|
|
// it's args and keeping it generic.
|
|
TransportOptionsURL string
|
|
}
|
|
|
|
// KexAlgos hosts the key exchange algorithms to be used for SSH connections.
|
|
// If empty, Go's default is used instead.
|
|
var KexAlgos []string
|
|
|
|
// HostKeyAlgos holds the HostKey algorithms that the SSH client will advertise
|
|
// to the server. If empty, Go's default is used instead.
|
|
var HostKeyAlgos []string
|
|
|
|
// Validate the AuthOptions against the defined Transport.
|
|
func (o AuthOptions) Validate() error {
|
|
switch o.Transport {
|
|
case HTTPS, HTTP:
|
|
if o.Username == "" && o.Password != "" {
|
|
return fmt.Errorf("invalid '%s' auth option: 'password' requires 'username' to be set", o.Transport)
|
|
}
|
|
case SSH:
|
|
if o.Host == "" {
|
|
return fmt.Errorf("invalid '%s' auth option: 'host' is required", o.Transport)
|
|
}
|
|
if len(o.Identity) == 0 {
|
|
return fmt.Errorf("invalid '%s' auth option: 'identity' is required", o.Transport)
|
|
}
|
|
if len(o.KnownHosts) == 0 {
|
|
return fmt.Errorf("invalid '%s' auth option: 'known_hosts' is required", o.Transport)
|
|
}
|
|
case "":
|
|
return fmt.Errorf("no transport type set")
|
|
default:
|
|
return fmt.Errorf("unknown transport '%s'", o.Transport)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AuthOptionsFromSecret constructs an AuthOptions object from the given Secret,
|
|
// and then validates the result. It returns the AuthOptions, or an error.
|
|
func AuthOptionsFromSecret(URL string, secret *v1.Secret) (*AuthOptions, error) {
|
|
if secret == nil {
|
|
return nil, fmt.Errorf("no secret provided to construct auth strategy from")
|
|
}
|
|
|
|
u, err := url.Parse(URL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse URL to determine auth strategy: %w", err)
|
|
}
|
|
|
|
opts := &AuthOptions{
|
|
Transport: TransportType(u.Scheme),
|
|
Host: u.Host,
|
|
Username: string(secret.Data["username"]),
|
|
Password: string(secret.Data["password"]),
|
|
CAFile: secret.Data["caFile"],
|
|
Identity: secret.Data["identity"],
|
|
KnownHosts: secret.Data["known_hosts"],
|
|
}
|
|
if opts.Username == "" {
|
|
opts.Username = u.User.Username()
|
|
}
|
|
if opts.Username == "" {
|
|
opts.Username = DefaultPublicKeyAuthUser
|
|
}
|
|
|
|
if err = opts.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return opts, nil
|
|
}
|
|
|
|
// AuthOptionsWithoutSecret constructs a minimal AuthOptions object from the
|
|
// given URL and then validates the result. It returns the AuthOptions, or an
|
|
// error.
|
|
func AuthOptionsWithoutSecret(URL string) (*AuthOptions, error) {
|
|
u, err := url.Parse(URL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse URL to determine auth strategy: %w", err)
|
|
}
|
|
|
|
opts := &AuthOptions{
|
|
Transport: TransportType(u.Scheme),
|
|
Host: u.Host,
|
|
}
|
|
|
|
if err = opts.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return opts, nil
|
|
}
|