refactor: fold auth and crypto into deezer domain
This commit is contained in:
+26
-4
@@ -1,10 +1,14 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mathismqn/godeez/internal/auth"
|
||||
"github.com/mathismqn/godeez/internal/deezer"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func newLoginCmd() *cobra.Command {
|
||||
@@ -13,16 +17,16 @@ func newLoginCmd() *cobra.Command {
|
||||
Short: "Log in to Deezer with your email and password",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := auth.CheckGatewayEnv(); err != nil {
|
||||
if err := deezer.CheckGatewayEnv(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
email, password, err := auth.PromptCredentials()
|
||||
email, password, err := promptCredentials()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, username, err := auth.Login(cmd.Context(), email, password)
|
||||
_, username, err := deezer.Login(cmd.Context(), email, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -33,3 +37,21 @@ func newLoginCmd() *cobra.Command {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func promptCredentials() (string, string, error) {
|
||||
fmt.Print("Email: ")
|
||||
line, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
email := strings.TrimSpace(line)
|
||||
|
||||
fmt.Print("Password: ")
|
||||
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return email, string(passwordBytes), nil
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mathismqn/godeez/internal/auth"
|
||||
"github.com/mathismqn/godeez/internal/deezer"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ func newLogoutCmd() *cobra.Command {
|
||||
Short: "Remove stored Deezer credentials",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := auth.Clear(); err != nil {
|
||||
if err := deezer.ClearCredentials(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func PromptCredentials() (string, string, error) {
|
||||
fmt.Print("Email: ")
|
||||
line, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
email := strings.TrimSpace(line)
|
||||
|
||||
fmt.Print("Password: ")
|
||||
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return email, string(passwordBytes), nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package crypto
|
||||
package deezer
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
@@ -13,7 +13,7 @@ var (
|
||||
blowfishSecretKey = []byte("g4el58wc0zvf9na1")
|
||||
)
|
||||
|
||||
func GetBlowfishKey(trackID string) []byte {
|
||||
func BlowfishKey(trackID string) []byte {
|
||||
hash := md5.Sum([]byte(trackID))
|
||||
hashHex := hex.EncodeToString(hash[:])
|
||||
|
||||
@@ -8,17 +8,14 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mathismqn/godeez/internal/auth"
|
||||
"github.com/mathismqn/godeez/internal/config"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Session *Session
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig *config.Config) (*Client, error) {
|
||||
session, err := resolveSession(ctx, appConfig)
|
||||
func NewClient(ctx context.Context, arlCookie string) (*Client, error) {
|
||||
session, err := resolveSession(ctx, arlCookie)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to authenticate: %w", err)
|
||||
}
|
||||
@@ -28,14 +25,14 @@ func NewClient(ctx context.Context, appConfig *config.Config) (*Client, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveSession(ctx context.Context, appConfig *config.Config) (*Session, error) {
|
||||
if appConfig.ARLCookie != "" {
|
||||
return Authenticate(ctx, appConfig.ARLCookie)
|
||||
func resolveSession(ctx context.Context, arlCookie string) (*Session, error) {
|
||||
if arlCookie != "" {
|
||||
return authenticate(ctx, arlCookie)
|
||||
}
|
||||
|
||||
var session *Session
|
||||
validate := func(ctx context.Context, arl string) error {
|
||||
s, err := Authenticate(ctx, arl)
|
||||
s, err := authenticate(ctx, arl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -44,13 +41,13 @@ func resolveSession(ctx context.Context, appConfig *config.Config) (*Session, er
|
||||
return nil
|
||||
}
|
||||
|
||||
arl, err := auth.Resolve(ctx, validate)
|
||||
arl, err := resolveARL(ctx, validate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
session, err = Authenticate(ctx, arl)
|
||||
session, err = authenticate(ctx, arl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package auth
|
||||
package deezer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -19,7 +19,7 @@ type Credentials struct {
|
||||
ARL string `json:"arl,omitempty"`
|
||||
}
|
||||
|
||||
func Load() (*Credentials, error) {
|
||||
func LoadCredentials() (*Credentials, error) {
|
||||
secret, err := keyring.Get(keyringService, keyringUser)
|
||||
if err != nil {
|
||||
if errors.Is(err, keyring.ErrNotFound) {
|
||||
@@ -36,7 +36,7 @@ func Load() (*Credentials, error) {
|
||||
return &creds, nil
|
||||
}
|
||||
|
||||
func Save(creds *Credentials) error {
|
||||
func SaveCredentials(creds *Credentials) error {
|
||||
data, err := json.Marshal(creds)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -49,7 +49,7 @@ func Save(creds *Credentials) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Clear() error {
|
||||
func ClearCredentials() error {
|
||||
if err := keyring.Delete(keyringService, keyringUser); err != nil {
|
||||
if errors.Is(err, keyring.ErrNotFound) {
|
||||
return nil
|
||||
@@ -1,4 +1,4 @@
|
||||
package crypto
|
||||
package deezer
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ZeroPad(data []byte) []byte {
|
||||
func zeroPad(data []byte) []byte {
|
||||
bs := aes.BlockSize
|
||||
padded := make([]byte, len(data)+(bs-len(data)%bs)%bs)
|
||||
copy(padded, data)
|
||||
@@ -14,11 +14,11 @@ func ZeroPad(data []byte) []byte {
|
||||
return padded
|
||||
}
|
||||
|
||||
func EncryptECB(key, data []byte) ([]byte, error) {
|
||||
func ecbEncrypt(key, data []byte) ([]byte, error) {
|
||||
return ecbTransform(key, data, (cipher.Block).Encrypt)
|
||||
}
|
||||
|
||||
func DecryptECB(key, data []byte) ([]byte, error) {
|
||||
func ecbDecrypt(key, data []byte) ([]byte, error) {
|
||||
return ecbTransform(key, data, (cipher.Block).Decrypt)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package auth
|
||||
package deezer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -14,8 +14,6 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mathismqn/godeez/internal/crypto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -72,7 +70,7 @@ func newMobileClient() (*mobileClient, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mobileClient) Login(ctx context.Context, email, password string) (*Credentials, string, error) {
|
||||
func (m *mobileClient) login(ctx context.Context, email, password string) (*Credentials, string, error) {
|
||||
token, tokenKey, userKey, err := m.authenticate(ctx)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@@ -117,7 +115,7 @@ func (m *mobileClient) authenticate(ctx context.Context) (string, string, string
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
decrypted, err := crypto.DecryptECB(m.gwKey, encrypted)
|
||||
decrypted, err := ecbDecrypt(m.gwKey, encrypted)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
@@ -130,7 +128,7 @@ func (m *mobileClient) authenticate(ctx context.Context) (string, string, string
|
||||
}
|
||||
|
||||
func (m *mobileClient) checkToken(ctx context.Context, token, tokenKey string) error {
|
||||
encrypted, err := crypto.EncryptECB([]byte(tokenKey), []byte(token))
|
||||
encrypted, err := ecbEncrypt([]byte(tokenKey), []byte(token))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -156,7 +154,7 @@ func (m *mobileClient) checkToken(ctx context.Context, token, tokenKey string) e
|
||||
}
|
||||
|
||||
func (m *mobileClient) userAuth(ctx context.Context, email, password, userKey string) (string, string, error) {
|
||||
encryptedPassword, err := crypto.EncryptECB([]byte(userKey), crypto.ZeroPad([]byte(password)))
|
||||
encryptedPassword, err := ecbEncrypt([]byte(userKey), zeroPad([]byte(password)))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package auth
|
||||
package deezer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Resolve(ctx context.Context, validate func(ctx context.Context, arl string) error) (string, error) {
|
||||
creds, err := Load()
|
||||
func resolveARL(ctx context.Context, validate func(ctx context.Context, arl string) error) (string, error) {
|
||||
creds, err := LoadCredentials()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -35,12 +35,12 @@ func Login(ctx context.Context, email, password string) (string, string, error)
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
creds, username, err := client.Login(ctx, email, password)
|
||||
creds, username, err := client.login(ctx, email, password)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if err := Save(creds); err != nil {
|
||||
if err := SaveCredentials(creds); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ type Session struct {
|
||||
Premium bool
|
||||
}
|
||||
|
||||
func Authenticate(ctx context.Context, arlCookie string) (*Session, error) {
|
||||
func authenticate(ctx context.Context, arlCookie string) (*Session, error) {
|
||||
jar, err := cookiejar.New(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mathismqn/godeez/internal/config"
|
||||
"github.com/mathismqn/godeez/internal/crypto"
|
||||
"github.com/mathismqn/godeez/internal/deezer"
|
||||
"github.com/mathismqn/godeez/internal/fileutil"
|
||||
"github.com/mathismqn/godeez/internal/store"
|
||||
@@ -55,7 +54,7 @@ func (c *Client) Run(ctx context.Context, opts Options, id string) error {
|
||||
|
||||
func (c *Client) initDeezerClient(ctx context.Context, opts Options) error {
|
||||
var err error
|
||||
c.deezerClient, err = deezer.NewClient(ctx, c.appConfig)
|
||||
c.deezerClient, err = deezer.NewClient(ctx, c.appConfig.ARLCookie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -155,7 +154,7 @@ func (c *Client) downloadTrack(ctx context.Context, resource deezer.Resource, tr
|
||||
fileName := track.Filename(c.kind, mediaFormat)
|
||||
outputPath := path.Join(outputDir, fileName)
|
||||
|
||||
key := crypto.GetBlowfishKey(track.ID)
|
||||
key := deezer.BlowfishKey(track.ID)
|
||||
if err := c.streamToFile(dlCtx, stream, outputPath, key); err != nil {
|
||||
fileutil.DeleteFile(outputPath)
|
||||
return downloadResult{err: fmt.Errorf("failed to stream to file: %w", err)}
|
||||
@@ -213,7 +212,7 @@ func (c *Client) streamToFile(ctx context.Context, stream io.ReadCloser, outputP
|
||||
}
|
||||
|
||||
if chunk%3 == 0 && totalRead == chunkSize {
|
||||
buffer, err = crypto.DecryptBlowfish(buffer, key)
|
||||
buffer, err = deezer.DecryptBlowfish(buffer, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user