From 6ebcfe7c18a71bfe02bb22f4be7717b39629ed89 Mon Sep 17 00:00:00 2001
From: kayrus <kay.diam@gmail.com>
Date: Tue, 29 Jan 2019 17:06:20 +0100
Subject: [PATCH] Swift: introduce application credential auth support

---
 changelog/unreleased/issue-2155  |  8 ++++++++
 doc/030_preparing_a_new_repo.rst | 12 ++++++++++++
 doc/040_backup.rst               |  4 ++++
 internal/backend/swift/config.go | 10 ++++++++++
 internal/backend/swift/swift.go  | 29 ++++++++++++++++-------------
 5 files changed, 50 insertions(+), 13 deletions(-)
 create mode 100644 changelog/unreleased/issue-2155

diff --git a/changelog/unreleased/issue-2155 b/changelog/unreleased/issue-2155
new file mode 100644
index 000000000..35e5ffe73
--- /dev/null
+++ b/changelog/unreleased/issue-2155
@@ -0,0 +1,8 @@
+Enhancement: add Openstack application credential auth for Swift
+
+Since Openstack Queens Identity (auth V3) service supports an application
+credential auth method.  It allows to create a technical account with the
+limited roles. This commit adds an application credential authentication
+method for the Swift backend.
+
+https://github.com/restic/restic/issues/2155
diff --git a/doc/030_preparing_a_new_repo.rst b/doc/030_preparing_a_new_repo.rst
index 844455a79..bbffd475d 100644
--- a/doc/030_preparing_a_new_repo.rst
+++ b/doc/030_preparing_a_new_repo.rst
@@ -268,6 +268,18 @@ the naming convention of those variables follows the official Python Swift clien
    $ export OS_PROJECT_NAME=<MY_PROJECT_NAME>
    $ export OS_PROJECT_DOMAIN_NAME=<MY_PROJECT_DOMAIN_NAME>
 
+   # For keystone v3 application credential authentication (application credential id)
+   $ export OS_AUTH_URL=<MY_AUTH_URL>
+   $ export OS_APPLICATION_CREDENTIAL_ID=<MY_APPLICATION_CREDENTIAL_ID>
+   $ export OS_APPLICATION_CREDENTIAL_SECRET=<MY_APPLICATION_CREDENTIAL_SECRET>
+
+   # For keystone v3 application credential authentication (application credential name)
+   $ export OS_AUTH_URL=<MY_AUTH_URL>
+   $ export OS_USERNAME=<MY_USERNAME>
+   $ export OS_USER_DOMAIN_NAME=<MY_DOMAIN_NAME>
+   $ export OS_APPLICATION_CREDENTIAL_NAME=<MY_APPLICATION_CREDENTIAL_NAME>
+   $ export OS_APPLICATION_CREDENTIAL_SECRET=<MY_APPLICATION_CREDENTIAL_SECRET>
+
    # For authentication based on tokens
    $ export OS_STORAGE_URL=<MY_STORAGE_URL>
    $ export OS_AUTH_TOKEN=<MY_AUTH_TOKEN>
diff --git a/doc/040_backup.rst b/doc/040_backup.rst
index 75e06e42d..1833f651e 100644
--- a/doc/040_backup.rst
+++ b/doc/040_backup.rst
@@ -371,6 +371,10 @@ environment variables. The following list of environment variables:
     OS_PROJECT_NAME                     Project name for keystone authentication
     OS_PROJECT_DOMAIN_NAME              PRoject domain name for keystone authentication
 
+    OS_APPLICATION_CREDENTIAL_ID        Application Credential ID (keystone v3)
+    OS_APPLICATION_CREDENTIAL_NAME      Application Credential Name (keystone v3)
+    OS_APPLICATION_CREDENTIAL_SECRET    Application Credential Secret (keystone v3)
+
     OS_STORAGE_URL                      Storage URL for token authentication
     OS_AUTH_TOKEN                       Auth token for token authentication
 
diff --git a/internal/backend/swift/config.go b/internal/backend/swift/config.go
index 9c152707e..0ab4b656f 100644
--- a/internal/backend/swift/config.go
+++ b/internal/backend/swift/config.go
@@ -23,6 +23,11 @@ type Config struct {
 	StorageURL string
 	AuthToken  string
 
+	// auth v3 only
+	ApplicationCredentialID     string
+	ApplicationCredentialName   string
+	ApplicationCredentialSecret string
+
 	Container              string
 	Prefix                 string
 	DefaultContainerPolicy string
@@ -96,6 +101,11 @@ func ApplyEnvironment(prefix string, cfg interface{}) error {
 		{&c.UserName, prefix + "ST_USER"},
 		{&c.APIKey, prefix + "ST_KEY"},
 
+		// Application Credential auth
+		{&c.ApplicationCredentialID, prefix + "OS_APPLICATION_CREDENTIAL_ID"},
+		{&c.ApplicationCredentialName, prefix + "OS_APPLICATION_CREDENTIAL_NAME"},
+		{&c.ApplicationCredentialSecret, prefix + "OS_APPLICATION_CREDENTIAL_SECRET"},
+
 		// Manual authentication
 		{&c.StorageURL, prefix + "OS_STORAGE_URL"},
 		{&c.AuthToken, prefix + "OS_AUTH_TOKEN"},
diff --git a/internal/backend/swift/swift.go b/internal/backend/swift/swift.go
index 8a17450d8..dd83e0b06 100644
--- a/internal/backend/swift/swift.go
+++ b/internal/backend/swift/swift.go
@@ -43,19 +43,22 @@ func Open(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
 
 	be := &beSwift{
 		conn: &swift.Connection{
-			UserName:       cfg.UserName,
-			Domain:         cfg.Domain,
-			ApiKey:         cfg.APIKey,
-			AuthUrl:        cfg.AuthURL,
-			Region:         cfg.Region,
-			Tenant:         cfg.Tenant,
-			TenantId:       cfg.TenantID,
-			TenantDomain:   cfg.TenantDomain,
-			TrustId:        cfg.TrustID,
-			StorageUrl:     cfg.StorageURL,
-			AuthToken:      cfg.AuthToken,
-			ConnectTimeout: time.Minute,
-			Timeout:        time.Minute,
+			UserName:                    cfg.UserName,
+			Domain:                      cfg.Domain,
+			ApiKey:                      cfg.APIKey,
+			AuthUrl:                     cfg.AuthURL,
+			Region:                      cfg.Region,
+			Tenant:                      cfg.Tenant,
+			TenantId:                    cfg.TenantID,
+			TenantDomain:                cfg.TenantDomain,
+			TrustId:                     cfg.TrustID,
+			StorageUrl:                  cfg.StorageURL,
+			AuthToken:                   cfg.AuthToken,
+			ApplicationCredentialId:     cfg.ApplicationCredentialID,
+			ApplicationCredentialName:   cfg.ApplicationCredentialName,
+			ApplicationCredentialSecret: cfg.ApplicationCredentialSecret,
+			ConnectTimeout:              time.Minute,
+			Timeout:                     time.Minute,
 
 			Transport: rt,
 		},