Skip to content

Commit

Permalink
Use app specific password from Apple ID connection (#102)
Browse files Browse the repository at this point in the history
* Use FileProvider from input package

* Get app_specific_password from connection

* fix invalid bitrise.yml

* Use conn's appspecpass everywhere

* Connection nil check

* improve test workflow naming

* Input provided password overwrites connection provided password

* Handle password from input and connection correctly

* Update step.yml

* Update appleauth/auth_source.go

Co-authored-by: lpusok <[email protected]>

* Use connection password for ConnectionAppleIDFastlaneSource

* Update step.yml

* Update connection input description

Co-authored-by: lpusok <[email protected]>
  • Loading branch information
adborbas and lpusok authored Feb 25, 2021
1 parent c55aafe commit 4a4d938
Show file tree
Hide file tree
Showing 13 changed files with 626 additions and 255 deletions.
15 changes: 13 additions & 2 deletions appleauth/auth_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (*ConnectionAppleIDSource) Fetch(conn *devportalservice.AppleDeveloperConne
Username: conn.AppleIDConnection.AppleID,
Password: conn.AppleIDConnection.Password,
Session: "",
AppSpecificPassword: inputs.AppSpecificPassword,
AppSpecificPassword: appSpecificPasswordFavouringConnection(conn.AppleIDConnection, inputs.AppSpecificPassword),
},
}, nil
}
Expand Down Expand Up @@ -149,7 +149,7 @@ func (*ConnectionAppleIDFastlaneSource) Fetch(conn *devportalservice.AppleDevelo
Username: conn.AppleIDConnection.AppleID,
Password: conn.AppleIDConnection.Password,
Session: session,
AppSpecificPassword: inputs.AppSpecificPassword,
AppSpecificPassword: appSpecificPasswordFavouringConnection(conn.AppleIDConnection, inputs.AppSpecificPassword),
},
}, nil
}
Expand All @@ -175,3 +175,14 @@ func (*InputAppleIDFastlaneSource) Fetch(conn *devportalservice.AppleDeveloperCo
},
}, nil
}

func appSpecificPasswordFavouringConnection(conn *devportalservice.AppleIDConnection, passwordFromInput string) string {
appSpecificPassword := passwordFromInput

// AppSpecifcPassword from the connection overwrites the one from the input
if conn != nil && conn.AppSpecificPassword != "" {
appSpecificPassword = conn.AppSpecificPassword
}

return appSpecificPassword
}
147 changes: 122 additions & 25 deletions appleauth/fetch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,47 @@ import (
"github.com/stretchr/testify/require"
)

var (
argInput = Inputs{
Username: "input_username", Password: "input_password", AppSpecificPassword: "input_appspecificpassword",
APIIssuer: "", APIKeyPath: "",
}

argAppleIDConnection = devportalservice.AppleIDConnection{
AppleID: "connection_appleid", Password: "connection_password", AppSpecificPassword: "connection_appspecificpassword",
}

argAppleIDConnectionMissingPassword = devportalservice.AppleIDConnection{
AppleID: "connection_appleid", Password: "connection_password", AppSpecificPassword: "",
}

argAPIKeyConnection = devportalservice.APIKeyConnection{
KeyID: "keyconnection_keyID", IssuerID: "keyconnection_issuerID", PrivateKey: "keyconnection_PrivateKey",
}
)

var (
expectedAppleIDWithArgInput = AppleID{
Username: argInput.Username,
Password: argInput.Password,
AppSpecificPassword: argInput.AppSpecificPassword,
Session: "",
}

expectedAppleIDWithArgConnection = AppleID{
Username: argAppleIDConnection.AppleID,
Password: argAppleIDConnection.Password,
AppSpecificPassword: argAppleIDConnection.AppSpecificPassword,
Session: "",
}

expectedAppleIDWithAPIKeyConnection = devportalservice.APIKeyConnection{
KeyID: argAPIKeyConnection.KeyID,
IssuerID: argAPIKeyConnection.IssuerID,
PrivateKey: argAPIKeyConnection.PrivateKey,
}
)

func TestSelect(t *testing.T) {
type args struct {
devportalConnection *devportalservice.AppleDeveloperConnection
Expand Down Expand Up @@ -48,58 +89,114 @@ func TestSelect(t *testing.T) {
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{},
authSources: []Source{&ConnectionAPIKeySource{}, &ConnectionAppleIDSource{}, &InputAPIKeySource{}, &InputAppleIDSource{}},
inputs: Inputs{
Username: "a", Password: "b", AppSpecificPassword: "c",
APIIssuer: "", APIKeyPath: "",
inputs: argInput,
},
want: Credentials{
AppleID: &expectedAppleIDWithArgInput,
APIKey: nil,
},
},
{
name: "Connection active (Apple ID), inputs (Apple ID) with ConnectionAppleIDSource",
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{
AppleIDConnection: &argAppleIDConnection,
},
authSources: []Source{&ConnectionAppleIDSource{}},
inputs: argInput,
},
want: Credentials{
AppleID: &expectedAppleIDWithArgConnection,
APIKey: nil,
},
},
{
name: "Connection active (Apple ID), inputs (Apple ID) with InputAppleIDSource",
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{
AppleIDConnection: &argAppleIDConnection,
},
authSources: []Source{&InputAppleIDSource{}},
inputs: argInput,
},
want: Credentials{
AppleID: &expectedAppleIDWithArgInput,
APIKey: nil,
},
},
{
name: "Connection active (Apple ID), inputs (Apple ID) with ConnectionAppleIDFastlaneSource",
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{
AppleIDConnection: &argAppleIDConnection,
},
authSources: []Source{&ConnectionAppleIDFastlaneSource{}},
inputs: argInput,
},
want: Credentials{
AppleID: &expectedAppleIDWithArgConnection,
APIKey: nil,
},
},
{
name: "Connection active but missing password (Apple ID), inputs (Apple ID) with ConnectionAppleIDFastlaneSource",
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{
AppleIDConnection: &argAppleIDConnectionMissingPassword,
},
authSources: []Source{&ConnectionAppleIDFastlaneSource{}},
inputs: argInput,
},
want: Credentials{
AppleID: &AppleID{
Username: "a", Password: "b", AppSpecificPassword: "c", Session: "",
Username: argAppleIDConnection.AppleID,
Password: argAppleIDConnection.Password,
AppSpecificPassword: argInput.AppSpecificPassword,
Session: "",
},
APIKey: nil,
},
},
{
name: "Connection active (Apple ID), inputs (Apple ID) with InputAppleIDFastlaneSource",
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{
AppleIDConnection: &argAppleIDConnection,
},
authSources: []Source{&InputAppleIDFastlaneSource{}},
inputs: argInput,
},
want: Credentials{
AppleID: &expectedAppleIDWithArgInput,
APIKey: nil,
},
},
{
name: "Connection active (API Key), inputs (Apple ID)",
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{
APIKeyConnection: &devportalservice.APIKeyConnection{
KeyID: "x", IssuerID: "y", PrivateKey: "z",
},
APIKeyConnection: &argAPIKeyConnection,
},
authSources: []Source{&ConnectionAPIKeySource{}, &ConnectionAppleIDSource{}, &InputAPIKeySource{}, &InputAppleIDSource{}},
inputs: Inputs{
Username: "a", Password: "b", AppSpecificPassword: "c",
APIIssuer: "", APIKeyPath: "",
},
inputs: argInput,
},
want: Credentials{
AppleID: nil,
APIKey: &devportalservice.APIKeyConnection{
KeyID: "x", IssuerID: "y", PrivateKey: "z",
},
APIKey: &expectedAppleIDWithAPIKeyConnection,
},
},
{
name: "Connection active (API Key), inputs (Apple ID), connection not enabled",
args: args{
devportalConnection: &devportalservice.AppleDeveloperConnection{
APIKeyConnection: &devportalservice.APIKeyConnection{
KeyID: "x", IssuerID: "y", PrivateKey: "z",
},
APIKeyConnection: &argAPIKeyConnection,
},
authSources: []Source{&InputAPIKeySource{}, &InputAppleIDSource{}},
inputs: Inputs{
Username: "a", Password: "b", AppSpecificPassword: "c",
APIIssuer: "", APIKeyPath: "",
},
inputs: argInput,
},
want: Credentials{
AppleID: &AppleID{
Username: "a", Password: "b", AppSpecificPassword: "c", Session: "",
},
APIKey: nil,
AppleID: &expectedAppleIDWithArgInput,
APIKey: nil,
},
},
}
Expand Down
45 changes: 9 additions & 36 deletions appleauth/key_helper.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package appleauth

import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path/filepath"
"regexp"

"github.com/bitrise-io/go-utils/log"
"github.com/bitrise-io/go-steputils/input"
"github.com/bitrise-steplib/bitrise-step-export-universal-apk/filedownloader"
)

func fetchPrivateKey(privateKeyURL string) ([]byte, string, error) {
Expand All @@ -17,46 +17,19 @@ func fetchPrivateKey(privateKeyURL string) ([]byte, string, error) {
return nil, "", err
}

key, err := copyOrDownloadFile(fileURL)
// Download or load local file
filedownloader := filedownloader.New(http.DefaultClient)
fileProvider := input.NewFileProvider(filedownloader)
localFile, err := fileProvider.LocalPath(fileURL.String())
if err != nil {
return nil, "", err
}

return key, getKeyID(fileURL), nil
}

func copyOrDownloadFile(u *url.URL) ([]byte, error) {
// if file -> copy
if u.Scheme == "file" {
b, err := ioutil.ReadFile(u.Path)
if err != nil {
return nil, err
}

return b, err
}

// otherwise download
resp, err := http.Get(u.String())
key, err := ioutil.ReadFile(localFile)
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Errorf("Failed to close file: %s", err)
}
}()

if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("request failed with status %d", resp.StatusCode)
}

contentBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %s", err)
return nil, "", err
}

return contentBytes, nil
return key, getKeyID(fileURL), nil
}

func getKeyID(u *url.URL) string {
Expand Down
Loading

0 comments on commit 4a4d938

Please sign in to comment.