forked from containerd/accelerated-container-image
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: zhuangbowei.zbw <[email protected]>
- Loading branch information
1 parent
bf3aeab
commit deae222
Showing
15 changed files
with
1,386 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package helpers | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"strings" | ||
) | ||
|
||
type ICredential interface { | ||
Credential(host string) (usename string, password string, err error) | ||
} | ||
|
||
// AuthConfig contains the config related to authentication to a specific registry | ||
type AuthConfig struct { | ||
// Username is the username to login the registry. | ||
Username string `toml:"username" json:"username"` | ||
// Password is the password to login the registry. | ||
Password string `toml:"password" json:"password"` | ||
// Auth is a base64 encoded string from the concatenation of the username, | ||
// a colon, and the password. | ||
Auth string `toml:"auth" json:"auth"` | ||
// IdentityToken is used to authenticate the user and get | ||
// an access token for the registry. | ||
IdentityToken string `toml:"identitytoken" json:"identitytoken"` | ||
} | ||
|
||
type FuncCredential func(host string) (string, string, error) | ||
|
||
func (c FuncCredential) Credential(host string) (string, string, error) { | ||
return c(host) | ||
} | ||
|
||
type BasicCredential struct { | ||
Auths map[string]AuthConfig `json:"auths"` | ||
} | ||
|
||
func (c *BasicCredential) Credential(host string) (string, string, error) { | ||
if c.Auths == nil { | ||
return "", "", nil | ||
} | ||
if auth, ok := c.Auths[host]; ok { | ||
if auth.Username != "" { | ||
return auth.Username, auth.Password, nil | ||
} | ||
if auth.IdentityToken != "" { | ||
return "", auth.IdentityToken, nil | ||
} | ||
if auth.Auth != "" { | ||
decLen := base64.StdEncoding.DecodedLen(len(auth.Auth)) | ||
decoded := make([]byte, decLen) | ||
_, err := base64.StdEncoding.Decode(decoded, []byte(auth.Auth)) | ||
if err != nil { | ||
return "", "", fmt.Errorf("failed to decode auth %q: %w", auth.Auth, err) | ||
} | ||
fields := strings.SplitN(string(decoded), ":", 2) | ||
if len(fields) != 2 { | ||
return "", "", fmt.Errorf("invalid decoded auth %q", decoded) | ||
} | ||
user, passwd := fields[0], fields[1] | ||
return user, strings.Trim(passwd, "\x00"), nil | ||
} | ||
} | ||
return "", "", nil | ||
} | ||
|
||
type FileCredential struct { | ||
CredFilePath string | ||
|
||
content BasicCredential | ||
} | ||
|
||
func (c *FileCredential) init() error { | ||
b, err := os.ReadFile(c.CredFilePath) | ||
if err != nil { | ||
return fmt.Errorf("FileCredential: failed to read file: %w", err) | ||
} | ||
if err := json.Unmarshal(b, &c.content); err != nil { | ||
return fmt.Errorf("FileCredential: failed to unmarshal file: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *FileCredential) Credential(host string) (string, string, error) { | ||
if c.content.Auths == nil { | ||
if err := c.init(); err != nil { | ||
return "", "", fmt.Errorf("FileCredential: failed to init: %w", err) | ||
} | ||
} | ||
return c.content.Credential(host) | ||
} | ||
|
||
type MultiCredential struct { | ||
Children []ICredential | ||
} | ||
|
||
func (c *MultiCredential) Credential(host string) (string, string, error) { | ||
for _, child := range c.Children { | ||
u, p, err := child.Credential(host) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
if u != "" || p != "" { | ||
return u, p, nil | ||
} | ||
} | ||
return "", "", nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package helpers_test | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/containerd/accelerated-container-image/pkg/convertor/helpers" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func testCredential(t *testing.T, getCred func(content string) (helpers.ICredential, error)) { | ||
require := require.New(t) | ||
|
||
prepare := func(content string) helpers.ICredential { | ||
cred, err := getCred(content) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
return cred | ||
} | ||
check := func(cred helpers.ICredential, host, username, password string) { | ||
u, p, err := cred.Credential(host) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
require.Equal(username, u, "username mismatch") | ||
require.Equal(password, p, "password mismatch") | ||
} | ||
|
||
// basic | ||
cred := prepare(` | ||
{ | ||
"auths": { | ||
"hostname": { | ||
"username": "123", | ||
"password": "456" | ||
} | ||
} | ||
}`) | ||
check(cred, "hostname", "123", "456") | ||
|
||
// base64 encode | ||
cred = prepare(` | ||
{ | ||
"auths": { | ||
"hostname": { | ||
"auth": "MTIzOjQ1Ng==" | ||
} | ||
} | ||
}`) | ||
check(cred, "hostname", "123", "456") | ||
|
||
// token | ||
cred = prepare(` | ||
{ | ||
"auths": { | ||
"hostname": { | ||
"identitytoken": "789" | ||
} | ||
} | ||
}`) | ||
check(cred, "hostname", "", "789") | ||
|
||
// not found | ||
cred = prepare(` | ||
{ | ||
"auths": { | ||
"hostname": { | ||
"username": "", | ||
"password": "" | ||
} | ||
} | ||
}`) | ||
check(cred, "hostname2", "", "") | ||
} | ||
|
||
func TestBasicCredential(t *testing.T) { | ||
testCredential(t, func(content string) (helpers.ICredential, error) { | ||
var cred helpers.BasicCredential | ||
if err := json.Unmarshal([]byte(content), &cred); err != nil { | ||
return nil, fmt.Errorf("failed to unmarshal json: %w", err) | ||
} | ||
return &cred, nil | ||
}) | ||
} | ||
|
||
func TestFileCredential(t *testing.T) { | ||
fn := "/tmp/convertor/cred.json" | ||
defer os.RemoveAll(filepath.Dir(fn)) | ||
testCredential(t, func(content string) (helpers.ICredential, error) { | ||
if err := os.Mkdir(filepath.Dir(fn), 0755); err != nil && !os.IsExist(err) { | ||
return nil, fmt.Errorf("failed to mkdir: %w", err) | ||
} | ||
if err := os.WriteFile(fn, []byte(content), 0644); err != nil { | ||
return nil, fmt.Errorf("failed to write file: %w", err) | ||
} | ||
return &helpers.FileCredential{CredFilePath: fn}, nil | ||
}) | ||
} |
Oops, something went wrong.