AUTH-3221: Saves org token to disk and uses it to refresh the app token
This commit is contained in:
parent
cad58b9b57
commit
fcc393e2f0
|
@ -222,7 +222,7 @@ func login(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfdToken, err := token.GetTokenIfExists(appURL)
|
cfdToken, err := token.GetAppTokenIfExists(appURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Unable to find token for provided application.")
|
fmt.Fprintln(os.Stderr, "Unable to find token for provided application.")
|
||||||
return err
|
return err
|
||||||
|
@ -267,7 +267,7 @@ func curl(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tok, err := token.GetTokenIfExists(appURL)
|
tok, err := token.GetAppTokenIfExists(appURL)
|
||||||
if err != nil || tok == "" {
|
if err != nil || tok == "" {
|
||||||
if allowRequest {
|
if allowRequest {
|
||||||
logger.Info("You don't have an Access token set. Please run access token <access application> to fetch one.")
|
logger.Info("You don't have an Access token set. Please run access token <access application> to fetch one.")
|
||||||
|
@ -295,7 +295,7 @@ func generateToken(c *cli.Context) error {
|
||||||
fmt.Fprintln(os.Stderr, "Please provide a url.")
|
fmt.Fprintln(os.Stderr, "Please provide a url.")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tok, err := token.GetTokenIfExists(appURL)
|
tok, err := token.GetAppTokenIfExists(appURL)
|
||||||
if err != nil || tok == "" {
|
if err != nil || tok == "" {
|
||||||
fmt.Fprintln(os.Stderr, "Unable to find token for provided application. Please run token command to generate token.")
|
fmt.Fprintln(os.Stderr, "Unable to find token for provided application. Please run token command to generate token.")
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -11,8 +11,27 @@ import (
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateFilePathFromURL will return a filepath for given access application url
|
// GenerateAppTokenFilePathFromURL will return a filepath for given Access org token
|
||||||
func GenerateFilePathFromURL(url *url.URL, suffix string) (string, error) {
|
func GenerateAppTokenFilePathFromURL(url *url.URL, suffix string) (string, error) {
|
||||||
|
configPath, err := getConfigPath()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name := strings.Replace(fmt.Sprintf("%s%s-%s", url.Hostname(), url.EscapedPath(), suffix), "/", "-", -1)
|
||||||
|
return filepath.Join(configPath, name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateOrgTokenFilePathFromURL will return a filepath for given Access application token
|
||||||
|
func GenerateOrgTokenFilePathFromURL(authDomain string) (string, error) {
|
||||||
|
configPath, err := getConfigPath()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name := strings.Replace(fmt.Sprintf("%s-org-token", authDomain), "/", "-", -1)
|
||||||
|
return filepath.Join(configPath, name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigPath() (string, error) {
|
||||||
configPath, err := homedir.Expand(config.DefaultConfigSearchDirectories()[0])
|
configPath, err := homedir.Expand(config.DefaultConfigSearchDirectories()[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -22,9 +41,5 @@ func GenerateFilePathFromURL(url *url.URL, suffix string) (string, error) {
|
||||||
// create config directory if doesn't already exist
|
// create config directory if doesn't already exist
|
||||||
err = os.Mkdir(configPath, 0700)
|
err = os.Mkdir(configPath, 0700)
|
||||||
}
|
}
|
||||||
if err != nil {
|
return configPath, err
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
name := strings.Replace(fmt.Sprintf("%s%s-%s", url.Hostname(), url.EscapedPath(), suffix), "/", "-", -1)
|
|
||||||
return filepath.Join(configPath, name), nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -17,10 +19,12 @@ import (
|
||||||
"github.com/cloudflare/cloudflared/logger"
|
"github.com/cloudflare/cloudflared/logger"
|
||||||
"github.com/cloudflare/cloudflared/origin"
|
"github.com/cloudflare/cloudflared/origin"
|
||||||
"github.com/coreos/go-oidc/jose"
|
"github.com/coreos/go-oidc/jose"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
keyName = "token"
|
keyName = "token"
|
||||||
|
tokenHeader = "CF_Authorization"
|
||||||
)
|
)
|
||||||
|
|
||||||
type lock struct {
|
type lock struct {
|
||||||
|
@ -34,7 +38,7 @@ type signalHandler struct {
|
||||||
signals []os.Signal
|
signals []os.Signal
|
||||||
}
|
}
|
||||||
|
|
||||||
type jwtPayload struct {
|
type appJWTPayload struct {
|
||||||
Aud []string `json:"aud"`
|
Aud []string `json:"aud"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Exp int `json:"exp"`
|
Exp int `json:"exp"`
|
||||||
|
@ -45,7 +49,17 @@ type jwtPayload struct {
|
||||||
Subt string `json:"sub"`
|
Subt string `json:"sub"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p jwtPayload) isExpired() bool {
|
type orgJWTPayload struct {
|
||||||
|
appJWTPayload
|
||||||
|
Aud string `json:"aud"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type transferServiceResponse struct {
|
||||||
|
AppToken string `json:"app_token"`
|
||||||
|
OrgToken string `json:"org_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p appJWTPayload) isExpired() bool {
|
||||||
return int(time.Now().Unix()) > p.Exp
|
return int(time.Now().Unix()) > p.Exp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,55 +155,173 @@ func FetchToken(appURL *url.URL, logger logger.Service) (string, error) {
|
||||||
|
|
||||||
// getToken will either load a stored token or generate a new one
|
// getToken will either load a stored token or generate a new one
|
||||||
func getToken(appURL *url.URL, useHostOnly bool, logger logger.Service) (string, error) {
|
func getToken(appURL *url.URL, useHostOnly bool, logger logger.Service) (string, error) {
|
||||||
if token, err := GetTokenIfExists(appURL); token != "" && err == nil {
|
if token, err := GetAppTokenIfExists(appURL); token != "" && err == nil {
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := path.GenerateFilePathFromURL(appURL, keyName)
|
appTokenPath, err := path.GenerateAppTokenFilePathFromURL(appURL, keyName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "failed to generate app token file path")
|
||||||
}
|
}
|
||||||
|
|
||||||
fileLock := newLock(path)
|
fileLockAppToken := newLock(appTokenPath)
|
||||||
|
if err = fileLockAppToken.Acquire(); err != nil {
|
||||||
err = fileLock.Acquire()
|
return "", errors.Wrap(err, "failed to acquire app token lock")
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
defer fileLock.Release()
|
defer fileLockAppToken.Release()
|
||||||
|
|
||||||
// check to see if another process has gotten a token while we waited for the lock
|
// check to see if another process has gotten a token while we waited for the lock
|
||||||
if token, err := GetTokenIfExists(appURL); token != "" && err == nil {
|
if token, err := GetAppTokenIfExists(appURL); token != "" && err == nil {
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If an app token couldnt be found on disk, check for an org token and attempt to exchange it for an app token.
|
||||||
|
var orgTokenPath string
|
||||||
|
// Get auth domain to format into org token file path
|
||||||
|
if authDomain, err := getAuthDomain(appURL); err != nil {
|
||||||
|
logger.Errorf("failed to get auth domain: %s", err)
|
||||||
|
} else {
|
||||||
|
orgToken, err := GetOrgTokenIfExists(authDomain)
|
||||||
|
if err != nil {
|
||||||
|
orgTokenPath, err = path.GenerateOrgTokenFilePathFromURL(authDomain)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to generate org token file path")
|
||||||
|
}
|
||||||
|
|
||||||
|
fileLockOrgToken := newLock(orgTokenPath)
|
||||||
|
if err = fileLockOrgToken.Acquire(); err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to acquire org token lock")
|
||||||
|
}
|
||||||
|
defer fileLockOrgToken.Release()
|
||||||
|
// check if an org token has been created since the lock was acquired
|
||||||
|
orgToken, err = GetOrgTokenIfExists(authDomain)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if appToken, err := exchangeOrgToken(appURL, orgToken); err != nil {
|
||||||
|
logger.Debugf("failed to exchange org token for app token: %s", err)
|
||||||
|
} else {
|
||||||
|
if err := ioutil.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to write app token to disk")
|
||||||
|
}
|
||||||
|
return appToken, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getTokensFromEdge(appURL, appTokenPath, orgTokenPath, useHostOnly, logger)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,
|
||||||
|
// and return the app token.
|
||||||
|
func getTokensFromEdge(appURL *url.URL, appTokenPath, orgTokenPath string, useHostOnly bool, logger logger.Service) (string, error) {
|
||||||
|
// If no org token exists or if it couldnt be exchanged for an app token, then run the transfer service flow.
|
||||||
|
|
||||||
// this weird parameter is the resource name (token) and the key/value
|
// this weird parameter is the resource name (token) and the key/value
|
||||||
// we want to send to the transfer service. the key is token and the value
|
// we want to send to the transfer service. the key is token and the value
|
||||||
// is blank (basically just the id generated in the transfer service)
|
// is blank (basically just the id generated in the transfer service)
|
||||||
token, err := transfer.Run(appURL, keyName, keyName, "", path, true, useHostOnly, logger)
|
resourceData, err := transfer.Run(appURL, keyName, keyName, "", true, useHostOnly, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "failed to run transfer service")
|
||||||
|
}
|
||||||
|
var resp transferServiceResponse
|
||||||
|
if err = json.Unmarshal(resourceData, &resp); err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to marshal transfer service response")
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(token), nil
|
// If we were able to get the auth domain and generate an org token path, lets write it to disk.
|
||||||
|
if orgTokenPath != "" {
|
||||||
|
if err := ioutil.WriteFile(orgTokenPath, []byte(resp.OrgToken), 0600); err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to write org token to disk")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(appTokenPath, []byte(resp.AppToken), 0600); err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to write app token to disk")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.AppToken, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTokenIfExists will return the token from local storage if it exists and not expired
|
// getAuthDomain makes a request to the appURL and stops at the first redirect. The 302 location header will contain the
|
||||||
func GetTokenIfExists(url *url.URL) (string, error) {
|
// auth domain
|
||||||
path, err := path.GenerateFilePathFromURL(url, keyName)
|
func getAuthDomain(appURL *url.URL) (string, error) {
|
||||||
if err != nil {
|
client := &http.Client{
|
||||||
return "", err
|
// do not follow redirects
|
||||||
}
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
content, err := ioutil.ReadFile(path)
|
return http.ErrUseLastResponse
|
||||||
if err != nil {
|
},
|
||||||
return "", err
|
Timeout: time.Second * 7,
|
||||||
}
|
|
||||||
token, err := jose.ParseJWT(string(content))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var payload jwtPayload
|
authDomainReq, err := http.NewRequest("HEAD", appURL.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to create auth domain request")
|
||||||
|
}
|
||||||
|
resp, err := client.Do(authDomainReq)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to get auth domain")
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
location, err := resp.Location()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get auth domain. Received status code %d from %s", resp.StatusCode, appURL.String())
|
||||||
|
}
|
||||||
|
return location.Hostname(), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// exchangeOrgToken attaches an org token to a request to the appURL and returns an app token. This uses the Access SSO
|
||||||
|
// flow to automatically generate and return an app token without the login page.
|
||||||
|
func exchangeOrgToken(appURL *url.URL, orgToken string) (string, error) {
|
||||||
|
client := &http.Client{
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
// attach org token to login request
|
||||||
|
if strings.Contains(req.URL.Path, "cdn-cgi/access/login") {
|
||||||
|
req.AddCookie(&http.Cookie{Name: tokenHeader, Value: orgToken})
|
||||||
|
}
|
||||||
|
// stop after hitting authorized endpoint since it will contain the app token
|
||||||
|
if strings.Contains(via[len(via)-1].URL.Path, "cdn-cgi/access/authorized") {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Timeout: time.Second * 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
appTokenRequest, err := http.NewRequest("HEAD", appURL.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to create app token request")
|
||||||
|
}
|
||||||
|
resp, err := client.Do(appTokenRequest)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to get app token")
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
var appToken string
|
||||||
|
for _, c := range resp.Cookies() {
|
||||||
|
if c.Name == tokenHeader {
|
||||||
|
appToken = c.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(appToken) > 0 {
|
||||||
|
return appToken, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("response from %s did not contain app token", resp.Request.URL.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrgTokenIfExists(authDomain string) (string, error) {
|
||||||
|
path, err := path.GenerateOrgTokenFilePathFromURL(authDomain)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
token, err := getTokenIfExists(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var payload orgJWTPayload
|
||||||
err = json.Unmarshal(token.Payload, &payload)
|
err = json.Unmarshal(token.Payload, &payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -199,13 +331,49 @@ func GetTokenIfExists(url *url.URL) (string, error) {
|
||||||
err := os.Remove(path)
|
err := os.Remove(path)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return token.Encode(), nil
|
return token.Encode(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAppTokenIfExists(url *url.URL) (string, error) {
|
||||||
|
path, err := path.GenerateAppTokenFilePathFromURL(url, keyName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
token, err := getTokenIfExists(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var payload appJWTPayload
|
||||||
|
err = json.Unmarshal(token.Payload, &payload)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.isExpired() {
|
||||||
|
err := os.Remove(path)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return token.Encode(), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenIfExists will return the token from local storage if it exists and not expired
|
||||||
|
func getTokenIfExists(path string) (*jose.JWT, error) {
|
||||||
|
content, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
token, err := jose.ParseJWT(string(content))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveTokenIfExists removes the a token from local storage if it exists
|
// RemoveTokenIfExists removes the a token from local storage if it exists
|
||||||
func RemoveTokenIfExists(url *url.URL) error {
|
func RemoveTokenIfExists(url *url.URL) error {
|
||||||
path, err := path.GenerateFilePathFromURL(url, keyName)
|
path, err := path.GenerateAppTokenFilePathFromURL(url, keyName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,8 @@ package transfer
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -15,6 +13,7 @@ import (
|
||||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/encrypter"
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/encrypter"
|
||||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/shell"
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/shell"
|
||||||
"github.com/cloudflare/cloudflared/logger"
|
"github.com/cloudflare/cloudflared/logger"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -28,7 +27,7 @@ const (
|
||||||
// The "dance" we refer to is building a HTTP request, opening that in a browser waiting for
|
// The "dance" we refer to is building a HTTP request, opening that in a browser waiting for
|
||||||
// the user to complete an action, while it long polls in the background waiting for an
|
// the user to complete an action, while it long polls in the background waiting for an
|
||||||
// action to be completed to download the resource.
|
// action to be completed to download the resource.
|
||||||
func Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncrypt bool, useHostOnly bool, logger logger.Service) ([]byte, error) {
|
func Run(transferURL *url.URL, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, logger logger.Service) ([]byte, error) {
|
||||||
encrypterClient, err := encrypter.New("cloudflared_priv.pem", "cloudflared_pub.pem")
|
encrypterClient, err := encrypter.New("cloudflared_priv.pem", "cloudflared_pub.pem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -72,11 +71,8 @@ func Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncr
|
||||||
resourceData = buf
|
resourceData = buf
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(path, resourceData, 0600); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return resourceData, nil
|
return resourceData, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildRequestURL creates a request suitable for a resource transfer.
|
// BuildRequestURL creates a request suitable for a resource transfer.
|
||||||
|
@ -93,6 +89,7 @@ func buildRequestURL(baseURL *url.URL, key, value string, cli, useHostOnly bool)
|
||||||
return baseURL.String(), nil
|
return baseURL.String(), nil
|
||||||
}
|
}
|
||||||
q.Set("redirect_url", baseURL.String()) // we add the token as a query param on both the redirect_url and the main url
|
q.Set("redirect_url", baseURL.String()) // we add the token as a query param on both the redirect_url and the main url
|
||||||
|
q.Set("send_org_token", "true") // indicates that the cli endpoint should return both the org and app token
|
||||||
baseURL.RawQuery = q.Encode() // and this actual baseURL.
|
baseURL.RawQuery = q.Encode() // and this actual baseURL.
|
||||||
baseURL.Path = "cdn-cgi/access/cli"
|
baseURL.Path = "cdn-cgi/access/cli"
|
||||||
return baseURL.String(), nil
|
return baseURL.String(), nil
|
||||||
|
|
|
@ -2,6 +2,7 @@ package tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -58,12 +59,16 @@ func login(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = transfer.Run(loginURL, "cert", "callback", callbackStoreURL, path, false, false, logger)
|
resourceData, err := transfer.Run(loginURL, "cert", "callback", callbackStoreURL, false, false, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write the certificate due to the following error:\n%v\n\nYour browser will download the certificate instead. You will have to manually\ncopy it to the following path:\n\n%s\n", err, path)
|
fmt.Fprintf(os.Stderr, "Failed to write the certificate due to the following error:\n%v\n\nYour browser will download the certificate instead. You will have to manually\ncopy it to the following path:\n\n%s\n", err, path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(path, resourceData, 0600); err != nil {
|
||||||
|
return errors.Wrap(err, fmt.Sprintf("error writing cert to %s", path))
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "You have successfully logged in.\nIf you wish to copy your credentials to a server, they have been saved to:\n%s\n", path)
|
fmt.Fprintf(os.Stdout, "You have successfully logged in.\nIf you wish to copy your credentials to a server, they have been saved to:\n%s\n", path)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ var mockRequest func(url, contentType string, body io.Reader) (*http.Response, e
|
||||||
|
|
||||||
// GenerateShortLivedCertificate generates and stores a keypair for short lived certs
|
// GenerateShortLivedCertificate generates and stores a keypair for short lived certs
|
||||||
func GenerateShortLivedCertificate(appURL *url.URL, token string) error {
|
func GenerateShortLivedCertificate(appURL *url.URL, token string) error {
|
||||||
fullName, err := cfpath.GenerateFilePathFromURL(appURL, keyName)
|
fullName, err := cfpath.GenerateAppTokenFilePathFromURL(appURL, keyName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ func TestCertGenSuccess(t *testing.T) {
|
||||||
url, _ := url.Parse("https://cf-test-access.com/testpath")
|
url, _ := url.Parse("https://cf-test-access.com/testpath")
|
||||||
token := tokenGenerator()
|
token := tokenGenerator()
|
||||||
|
|
||||||
fullName, err := cfpath.GenerateFilePathFromURL(url, keyName)
|
fullName, err := cfpath.GenerateAppTokenFilePathFromURL(url, keyName)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
pubKeyName := fullName + ".pub"
|
pubKeyName := fullName + ".pub"
|
||||||
|
|
Loading…
Reference in New Issue