package emulatorsvc import ( "bufio" "errors" "fmt" "io/fs" "os" "path/filepath" "golang.org/x/term" "gopkg.in/yaml.v2" ) const ( envFilePathDir = "RCLI_WORKSPACE_DIR" defaultDir = "./" authFileName = "rcli_auth.yaml" DefaultProfileName = "default" ProfileFlag = "profile" UsernameFlag = "username" PasswordFlag = "password" Endpoint = "api-endpoint" OrganizationFlag = "organization" ) type AuthFile struct { path string DefaultProfile string `yaml:"defaultProfile"` Profiles map[string]Profile `yaml:"profiles"` } func OpenAuthFile(dir string) (*AuthFile, error) { path := filepath.Join(dir, authFileName) auth := AuthFile{path: path} auth.Profiles = make(map[string]Profile) data, err := os.ReadFile(path) // If file does not exist, don't return error if errors.Is(err, fs.ErrNotExist) { return &auth, nil } if err != nil { return nil, err } err = yaml.UnmarshalStrict(data, &auth) if err != nil { return nil, err } return &auth, nil } func (af AuthFile) Path() string { return af.path } func (af *AuthFile) isInitialized() bool { return af.DefaultProfile != "" && len(af.Profiles) > 0 } func (af *AuthFile) UpdateAuthFile(profile Profile, name string) error { if af.isInitialized() { return nil } // If file has no defaultProfile, set it to either user-input profile or "default" if af.DefaultProfile == "" { if name != "" { af.DefaultProfile = name } else { af.DefaultProfile = DefaultProfileName } } // If there are no profiles, add default profile if len(af.Profiles) == 0 { af.Profiles[af.DefaultProfile] = profile } return af.writeFile() } func (af *AuthFile) writeFile() error { var data []byte data, err := yaml.Marshal(af) if err != nil { return err } return os.WriteFile(af.path, data, 0640) } func (af *AuthFile) LoadProfile(profile Profile, name string) (Profile, error) { // Authfile has not been set up yet if !af.isInitialized() { return profile, nil } if name == "" { name = af.DefaultProfile } p, ok := af.Profiles[name] if !ok { return Profile{}, fmt.Errorf("profile does not exist in rcli auth file") } if profile.Username == "" { profile.Username = p.Username } if profile.Organization == "" { profile.Organization = p.Organization } if profile.API == "" { profile.API = p.API } return profile, nil } type Config struct { dir string Profile Profile } func NewConfig(dir string, profile Profile) Config { return Config{ dir: dir, Profile: profile, } } type Profile struct { Username string `yaml:"username"` Password string `yaml:"-"` API string `yaml:"apiEndpoint"` Organization string `yaml:"organization"` SessionCookie string `yaml:"-"` } func (profile *Profile) RequestEmptyFields() error { var err error if profile.Username == "" { fmt.Print("Please enter your username: ") profile.Username, err = promptGeneric() if err != nil { return err } } if profile.Password == "" { fmt.Print("Please enter your password: ") profile.Password, err = promptPassword() if err != nil { return err } } if profile.API == "" { fmt.Print("Please enter the Edge API URL: ") profile.API, err = promptGeneric() if err != nil { return err } } if profile.Organization == "" { fmt.Print("Please enter your Edge organization: ") profile.Organization, err = promptGeneric() if err != nil { return err } } return nil } func promptGeneric() (string, error) { var input string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { if err := scanner.Err(); err != nil { return "", err } if input = scanner.Text(); input != "" { break } fmt.Print("Please enter a valid value: ") } return input, nil } func promptPassword() (string, error) { byteInput, err := term.ReadPassword(int(os.Stdin.Fd())) if err != nil { return "", err } fmt.Println() for string(byteInput) == "" { fmt.Print("Please enter a valid value: ") byteInput, err = term.ReadPassword(int(os.Stdin.Fd())) if err != nil { return "", err } fmt.Println() } return string(byteInput), nil }