Skip to content

Commit

Permalink
Merge development to master
Browse files Browse the repository at this point in the history
feat(client): Multi authentication provider #6
  • Loading branch information
ikr4-m committed Aug 14, 2024
2 parents 3d0a2c8 + ade48c4 commit a203440
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 33 deletions.
63 changes: 62 additions & 1 deletion cmd/mdrop-client/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bufio"
"errors"
"flag"
"fmt"
"os"
"strconv"
Expand All @@ -11,9 +12,33 @@ import (
"github.com/mplus-oss/mdrop/internal"
)

func AuthCommand() {
func AuthCommand(args []string) {
errChan := make(chan error, 0)

flag := flag.NewFlagSet("mdrop auth", flag.ExitOnError)
var (
defaultInstance = flag.String("setDefault", "", "Set default tunnel instance.")
list = flag.Bool("list", false, "Get list of tunnel instance")
help = flag.Bool("help", false, "Print this message")
)
flag.Parse(args)

if *help {
fmt.Println("Command: mdrop auth [options]")
flag.Usage()
os.Exit(1)
}

if *list {
getTunnelInstance()
os.Exit(0)
}

if *defaultInstance != "" {
setDefaultTunnelInstance(*defaultInstance)
os.Exit(0)
}

// Set prompt
config, err := authPrompt()
if err != nil {
Expand Down Expand Up @@ -49,9 +74,44 @@ func AuthCommand() {
}
}

func setDefaultTunnelInstance(instanceName string) {
var authConfig internal.ConfigSourceAuth
err := authConfig.ParseConfig(&authConfig)
if err != nil {
internal.PrintErrorWithExit("authParseConfig", err, 1)
}

err = authConfig.SetDefault(instanceName)
if err != nil {
internal.PrintErrorWithExit("authSetDefaultTunnelInstance", err, 1)
}
}

func getTunnelInstance() {
var authConfig internal.ConfigSourceAuth
err := authConfig.ParseConfig(&authConfig)
if err != nil {
internal.PrintErrorWithExit("authParseConfig", err, 1)
}

fmt.Println("Default instance:", authConfig.ListConfiguration[authConfig.Default].Name)
for i, instance := range authConfig.ListConfiguration {
fmt.Println(
fmt.Sprintf("[%v] %v", i, instance.Name),
)
}
}

func authPrompt() (config internal.ConfigFile, err error) {
reader := bufio.NewReader(os.Stdin)

fmt.Print("Instance Name: ")
instanceName, err := reader.ReadString('\n')
if err != nil {
return internal.ConfigFile{}, err
}
instanceName = strings.Replace(instanceName, "\n", "", -1)

fmt.Print("Hostname: ")
hostname, err := reader.ReadString('\n')
if err != nil {
Expand Down Expand Up @@ -81,6 +141,7 @@ func authPrompt() (config internal.ConfigFile, err error) {
proxy = strings.Replace(proxy, "\n", "", -1)

config = internal.ConfigFile{
Name: instanceName,
Host: hostname,
Port: portInt,
Proxy: proxy,
Expand Down
10 changes: 6 additions & 4 deletions cmd/mdrop-client/get_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ func GetCommand(args []string) {
}

// Parse Config File
var config internal.ConfigFile
err = config.ParseConfig(&config)
var authFile internal.ConfigSourceAuth
err = authFile.ParseConfig(&authFile)
if err != nil {
internal.PrintErrorWithExit("getParseConfigError", err, 1)
}
config := authFile.ListConfiguration[authFile.Default]
if sender.Host != config.Host {
internal.PrintErrorWithExit("getHostNotMatch", errors.New("Host not match"), 1)
}
Expand Down Expand Up @@ -69,8 +70,9 @@ func GetCommand(args []string) {
// Downloading file
for _, uuid := range sender.Files {
// No error checking needed.
fileName := GetPrompt(*localPort, uuid, *fileNameOpt)
checksum := GetChecksum(*localPort, uuid)
filePath := GetDownload(*localPort, *fileNameOpt, uuid)
filePath := GetDownload(*localPort, uuid, fileName)

// Check checksum
fmt.Println("Checking checksum...")
Expand All @@ -89,5 +91,5 @@ func GetCommand(args []string) {
fileDownloaded.Close()
}

fmt.Println("Download success!")
fmt.Println("\nDownload success!")
}
65 changes: 45 additions & 20 deletions cmd/mdrop-client/get_download.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,36 +33,30 @@ func GetChecksum(localPort int, uuid string) string {
return string(checksumBytes)
}

func GetDownload(localPort int, fileNameOpt string, uuid string) string {
func GetPrompt(localPort int, uuid string, fileNameOpt string) (filePath string) {
reader := bufio.NewReader(os.Stdin)
client := http.Client{}

resp, err := client.Post(
fmt.Sprintf("http://localhost:%v/%v", localPort, uuid),
"binary/octet-stream",
nil,
resp, err := client.Get(
fmt.Sprintf("http://localhost:%v/verify-%v", localPort, uuid),
)
if err != nil {
internal.PrintErrorWithExit("sendHttpClient", err, 1)
internal.PrintErrorWithExit("sendHttpClientPrompt", err, 1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Println("Status Code:", resp.StatusCode)
err = internal.CustomizeError("sendHttpClientResponse", errors.New("Download response error"))
internal.PrintErrorWithExit("sendHttpClientResponse", err, 1)
}

// Set filename from header or from output
fileName := resp.Header.Get("X-Attachment-Name")
if fileName == "" {
internal.PrintErrorWithExit("sendHttpClientInvalidAttachmentName", err, 1)
if resp.StatusCode != http.StatusOK {
err = internal.CustomizeError("sendHttpClientResponsePrompt", errors.New("Prompt response error"))
internal.PrintErrorWithExit("sendHttpClientResponsePrompt", err, 1)
}
if fileNameOpt != "" {
fileName = fileNameOpt
fileNameBytes, err := io.ReadAll(resp.Body)
if err != nil {
internal.PrintErrorWithExit("sendHttpClientReadPrompt", err, 1)
}
fmt.Println("File found:", fileName, fmt.Sprintf("[%v]", resp.Header.Get("X-Mime-Type")))
fileName := string(fileNameBytes)

// Ask client if they wanna download it or not
fmt.Println("\nFile found:", fileName, fmt.Sprintf("[%v]", resp.Header.Get("X-Mime-Type")))
fmt.Print("Download? [(Y)es/(N)o] [Default: Y] -> ")
prompt, err := reader.ReadString('\n')
if err != nil {
Expand All @@ -74,10 +68,14 @@ func GetDownload(localPort int, fileNameOpt string, uuid string) string {
}

// Check if there's duplicate file
filePath, err := os.Getwd()
filePath, err = os.Getwd()
if err != nil {
internal.PrintErrorWithExit("sendFileWorkDir", err, 1)
}
if fileNameOpt != "" {
fmt.Println("Changing filename to", fileNameOpt)
fileName = fileNameOpt
}
if fileStatus, _ := os.Stat(filePath+"/"+fileName); fileStatus != nil {
fmt.Print("There's duplicate file. Action? [(R)eplace/R(e)name/(C)ancel] [Default: R] -> ")
prompt, err := reader.ReadString('\n')
Expand All @@ -101,6 +99,33 @@ func GetDownload(localPort int, fileNameOpt string, uuid string) string {
fileName = prompt
}
}

return fileName
}

func GetDownload(localPort int, uuid string, fileName string) string {
client := http.Client{}

resp, err := client.Post(
fmt.Sprintf("http://localhost:%v/%v", localPort, uuid),
"binary/octet-stream",
nil,
)
if err != nil {
internal.PrintErrorWithExit("sendHttpClient", err, 1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Println("Status Code:", resp.StatusCode)
err = internal.CustomizeError("sendHttpClientResponse", errors.New("Download response error"))
internal.PrintErrorWithExit("sendHttpClientResponse", err, 1)
}

// Set filename from header or from output
filePath, err := os.Getwd()
if err != nil {
internal.PrintErrorWithExit("sendFileWorkDir", err, 1)
}
filePath += "/"+fileName

// Create file
Expand All @@ -110,7 +135,7 @@ func GetDownload(localPort int, fileNameOpt string, uuid string) string {
}

// Downloading file
fmt.Println("Downloading...")
fmt.Println("\nDownloading...")
bar := progressbar.DefaultBytes(resp.ContentLength, fileName)
_, err = io.Copy(io.MultiWriter(bar, file), resp.Body)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/mdrop-client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func main() {
cmd, args := args[0], args[1:]
switch cmd {
case "auth":
AuthCommand()
AuthCommand(args)
case "get":
GetCommand(args)
case "send":
Expand Down
5 changes: 3 additions & 2 deletions cmd/mdrop-client/send_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ func SendCommand(args []string) {
}

// Parse Config
var config internal.ConfigFile
err = config.ParseConfig(&config)
var authFile internal.ConfigSourceAuth
err = authFile.ParseConfig(&authFile)
if err != nil {
internal.PrintErrorWithExit("sendParseConfigError", err, 1)
}
config := authFile.ListConfiguration[authFile.Default]

// Get Port from Tunnel
fmt.Println("Connecting to tunnel for fetch port...")
Expand Down
21 changes: 21 additions & 0 deletions cmd/mdrop-client/send_webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ func SendWebserver(localPort int, file []string, uuid []string) (err error) {
http.Handle("/checksum-"+uuid[i], http.HandlerFunc(func (w http.ResponseWriter, request *http.Request) {
checksumSendWebserver(w, request, file[i])
}))
http.Handle("/verify-"+uuid[i], http.HandlerFunc(func (w http.ResponseWriter, request *http.Request) {
promptSendWebserver(w, request, file[i], mimeType.String())
}))
}

go func() {
Expand All @@ -66,6 +69,24 @@ func SendWebserver(localPort int, file []string, uuid []string) (err error) {
return nil
}

func promptSendWebserver(w http.ResponseWriter, request *http.Request, filePath string, mimeType string) {
file, err := os.Open(filePath)
if err != nil {
senderErrorChan <- internal.CustomizeError("promptOpenFile", err)
}
fileStat, err := file.Stat()
if err != nil {
senderErrorChan <- internal.CustomizeError("promptFileStat", err)
}
defer file.Close()

w.Header().Set("Content-Type", "text/plain")
w.Header().Set("X-Mime-Type", mimeType)
fmt.Fprint(w, fileStat.Name())

request.Close = true
}

func checksumSendWebserver(w http.ResponseWriter, request *http.Request, filePath string) {
fmt.Println("Receiver taking the checksum file.")
file, err := os.Open(filePath)
Expand Down
64 changes: 59 additions & 5 deletions internal/rw_config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ var (
ConfigFileLocation string
)

type ConfigSourceAuth struct {
Default int `json:"default"`
ListConfiguration []ConfigFile `json:"listConfig"`
}

type ConfigFile struct {
Name string `json:"name"`
Host string `json:"host"`
Port int `json:"port"`
Proxy string `json:"proxy"`
Expand All @@ -29,16 +35,29 @@ func init() {
ConfigFileLocation += "/.mdrop"
}

func (c ConfigFile) WriteConfig() error {
func (c ConfigFile) WriteConfig() (err error) {
var config ConfigSourceAuth

fmt.Println("Writing config file...")
if CheckConfigFileExist() {
err := os.Remove(GetConfigPath())
err = config.ParseConfig(&config)
if err != nil {
return err
err = os.Remove(GetConfigPath())
if err != nil {
return err
}
}
}

strJsonByte, err := json.Marshal(c)
// Check if there's multiple configuration
if config.GetTunnelBasedInstanceName(c.Name) != -1 {
return errors.New("Multiple instance name")
}

// Append config file
config.ListConfiguration = append(config.ListConfiguration, c)

strJsonByte, err := json.Marshal(config)
if err != nil {
return err
}
Expand All @@ -52,7 +71,7 @@ func (c ConfigFile) WriteConfig() error {
return nil
}

func (c ConfigFile) ParseConfig(conf *ConfigFile) error {
func (c ConfigSourceAuth) ParseConfig(conf *ConfigSourceAuth) error {
if !CheckConfigFileExist() {
return errors.New("No config file on local. Please log in first.")
}
Expand All @@ -73,6 +92,41 @@ func (c ConfigFile) ParseConfig(conf *ConfigFile) error {
return nil
}

func (c *ConfigSourceAuth) SetDefault(instanceName string) (err error) {
instanceID := c.GetTunnelBasedInstanceName(instanceName)
if instanceID == -1 {
return errors.New("Instance not found.")
}

c.Default = instanceID

strJsonByte, err := json.Marshal(c)
if err != nil {
return err
}
strConfig := base64.StdEncoding.EncodeToString(strJsonByte)
err = os.WriteFile(ConfigFileLocation, []byte(strConfig), 0644)
if err != nil {
return err
}

fmt.Println("Config file is saved on", ConfigFileLocation)
return nil
}

func (c ConfigSourceAuth) GetTunnelBasedInstanceName(instanceName string) (index int) {
index = -1

for i, v := range c.ListConfiguration {
if v.Name == instanceName {
index = i
break
}
}

return index
}

func GetConfigPath() string {
return ConfigFileLocation
}
Expand Down

0 comments on commit a203440

Please sign in to comment.