From 4538c500c1faf4ba57b388af62f55e649949e4e6 Mon Sep 17 00:00:00 2001 From: Illyoung Choi Date: Thu, 12 May 2022 18:03:32 -0700 Subject: [PATCH] Implement icommands session and refactor --- test/testcases/irods_environment_test.go | 74 +++++- utils/icommands/environrment.go | 324 +++++++++++------------ utils/icommands/environrment_raw.go | 121 +++++++++ 3 files changed, 347 insertions(+), 172 deletions(-) create mode 100644 utils/icommands/environrment_raw.go diff --git a/test/testcases/irods_environment_test.go b/test/testcases/irods_environment_test.go index 5260afd..7c8f9f2 100644 --- a/test/testcases/irods_environment_test.go +++ b/test/testcases/irods_environment_test.go @@ -1,22 +1,51 @@ package testcases import ( + "os" + "path/filepath" "testing" + + "github.com/cyverse/go-irodsclient/test/server" + "github.com/cyverse/go-irodsclient/utils/icommands" + "github.com/stretchr/testify/assert" ) func TestIRODSEnvironment(t *testing.T) { - //t.Run("test LoadCurrentUserEnv", testLoadCurrentUserEnv) - //t.Run("test SaveEnv", testSaveEnv) + t.Run("test SaveAndLoadEnv", testSaveAndLoadEnv) + t.Run("test SaveAndLoadEnvSession", testSaveAndLoadEnvSession) } -/* -func testLoadCurrentUserEnv(t *testing.T) { - account, err := icommands.CreateAccountFromCurrentUserAndHome() +func testSaveAndLoadEnv(t *testing.T) { + account, err := server.GetLocalAccount() + assert.NoError(t, err) + + homeDir, err := os.UserHomeDir() + assert.NoError(t, err) + + newEnvDir := filepath.Join(homeDir, ".irods2") + envMgr, err := icommands.CreateIcommandsEnvironmentManagerFromIRODSAccount(newEnvDir, 0, account) + assert.NoError(t, err) + + err = envMgr.Save() + assert.NoError(t, err) + + envMgr2, err := icommands.CreateIcommandsEnvironmentManager(newEnvDir, 0) + assert.NoError(t, err) + + envMgr2.Load() + + env2 := envMgr2.GetEnvironment() + assert.Equal(t, account.Host, env2.Host) + assert.Equal(t, account.Port, env2.Port) + assert.Equal(t, account.ClientZone, env2.Zone) + assert.Equal(t, account.ClientUser, env2.Username) + assert.Equal(t, account.Password, envMgr2.GetPassword()) + + err = os.RemoveAll(newEnvDir) assert.NoError(t, err) - assert.Equal(t, "iychoi", account.ClientUser) } -func testSaveEnv(t *testing.T) { +func testSaveAndLoadEnvSession(t *testing.T) { account, err := server.GetLocalAccount() assert.NoError(t, err) @@ -24,7 +53,34 @@ func testSaveEnv(t *testing.T) { assert.NoError(t, err) newEnvDir := filepath.Join(homeDir, ".irods2") - err = icommands.SaveAccountToDir(newEnvDir, 0, account) + envMgr, err := icommands.CreateIcommandsEnvironmentManagerFromIRODSAccount(newEnvDir, 0, account) + assert.NoError(t, err) + + testWorkingDir := "/test/working/dir" + + envMgr.GetSession().CurrentWorkingDir = testWorkingDir + + err = envMgr.Save() + assert.NoError(t, err) + err = envMgr.SaveSession() + assert.NoError(t, err) + + envMgr2, err := icommands.CreateIcommandsEnvironmentManager(newEnvDir, 0) + assert.NoError(t, err) + + envMgr2.Load() + + env2 := envMgr2.GetEnvironment() + assert.Equal(t, account.Host, env2.Host) + assert.Equal(t, account.Port, env2.Port) + assert.Equal(t, account.ClientZone, env2.Zone) + assert.Equal(t, account.ClientUser, env2.Username) + assert.Equal(t, account.Password, envMgr2.GetPassword()) + + sess2 := envMgr2.GetSession() + t.Logf("%v", sess2) + assert.Equal(t, testWorkingDir, sess2.CurrentWorkingDir) + + err = os.RemoveAll(newEnvDir) assert.NoError(t, err) } -*/ diff --git a/utils/icommands/environrment.go b/utils/icommands/environrment.go index fd55390..a306a09 100644 --- a/utils/icommands/environrment.go +++ b/utils/icommands/environrment.go @@ -1,9 +1,7 @@ package icommands import ( - "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" @@ -12,89 +10,32 @@ import ( const ( environmentDir string = ".irods" - environmentFile string = "irods_environment.json" passwordFile string = ".irodsA" + environmentFile string = "irods_environment.json" ) -// ICommandsEnvironment stores irods environment file (config file) -type ICommandsEnvironment struct { - AuthenticationFile string `json:"irods_authentication_file,omitempty"` - AuthenticationScheme string `json:"irods_authentication_scheme,omitempty"` - ClientServerNegotiation string `json:"irods_client_server_negotiation,omitempty"` - ClientServerPolicy string `json:"irods_client_server_policy,omitempty"` - ControlPlanePort int `json:"irods_control_plane_port,omitempty"` - ControlPlaneKey string `json:"irods_control_plane_key,omitempty"` - CurrentWorkingDir string `json:"irods_cwd,omitempty"` - Debug int `json:"irods_debug,omitempty"` - DefaultHashScheme string `json:"irods_default_hash_scheme,omitempty"` - Host string `json:"irods_host,omitempty"` - Port int `json:"irods_port,omitempty"` - Username string `json:"irods_user_name,omitempty"` - Zone string `json:"irods_zone_name,omitempty"` - DefaultResource string `json:"irods_default_resource,omitempty"` - EncryptionAlgorithm string `json:"irods_encryption_algorithm,omitempty"` - EncryptionKeySize int `json:"irods_encryption_key_size,omitempty"` - EncryptionNumHashRounds int `json:"irods_encryption_num_hash_rounds,omitempty"` - EncryptionSaltSize int `json:"irods_encryption_salt_size,omitempty"` - GSIServerDN string `json:"irods_gsi_server_dn,omitempty"` - Home string `json:"irods_home,omitempty"` - LogLevel int `json:"irods_log_level,omitempty"` - MatchHashPolicy string `json:"irods_match_hash_policy,omitempty"` - PluginsHome string `json:"irods_plugins_home,omitempty"` - SSLCACertificateFile string `json:"irods_ssl_ca_certificate_file,omitempty"` - SSLCACertificatePath string `json:"irods_ssl_ca_certificate_path,omitempty"` - SSLCertificateChainFile string `json:"irods_ssl_certificate_chain_file,omitempty"` - SSLCertificateKeyFile string `json:"irods_ssl_certificate_key_file,omitempty"` - SSLDHParamsFile string `json:"irods_ssl_dh_params_file,omitempty"` - SSLVerifyServer string `json:"irods_ssl_verify_server,omitempty"` - XMessageHost string `json:"irods_xmsg_host,omitempty"` - XMessagePort int `json:"irods_xmsg_port,omitempty"` -} - -// CreateICommandsEnvironmentFromDir creates ICommandsEnvironment from a dir -func CreateICommandsEnvironmentFromDir(envDirPath string) (*ICommandsEnvironment, error) { - environmentFilePath := filepath.Join(envDirPath, environmentFile) - return CreateICommandsEnvironmentFromFile(environmentFilePath) -} - -// CreateICommandsEnvironmentFromFile creates ICommandsEnvironment from a file -func CreateICommandsEnvironmentFromFile(envPath string) (*ICommandsEnvironment, error) { - data, err := ioutil.ReadFile(envPath) - if err != nil { - return nil, err - } - - return CreateICommandsEnvironmentFromJSON(data) +// ICommandsEnvironmentManager is a struct that manages icommands environment files +type ICommandsEnvironmentManager struct { + dirPath string + uid int + password string + environment *ICommandsEnvironment + session *ICommandsEnvironment } -// ReadPassword decyphers password file (.irodsA) -func ReadPasswordFromDir(envDirPath string, uid int) (string, error) { - passwordFilePath := filepath.Join(envDirPath, passwordFile) - return DecodePasswordFile(passwordFilePath, uid) +// CreateIcommandsEnvironmentManager creates ICommandsEnvironmentManager +func CreateIcommandsEnvironmentManager(envDirPath string, uid int) (*ICommandsEnvironmentManager, error) { + return &ICommandsEnvironmentManager{ + dirPath: envDirPath, + uid: uid, + password: "", + environment: &ICommandsEnvironment{}, + session: &ICommandsEnvironment{}, + }, nil } -// CreateAccountFromDir creates IRODSAccount from a dir -func CreateAccountFromDir(envDirPath string, uid int) (*types.IRODSAccount, error) { - env, err := CreateICommandsEnvironmentFromDir(envDirPath) - if err != nil { - return nil, err - } - - // doesn't fill password here - account := env.ToAccount() - - // load password from .irodsA - password, err := ReadPasswordFromDir(envDirPath, uid) - if err != nil { - return nil, err - } - - account.Password = password - return account, nil -} - -// CreateAccountFromCurrentUserAndHome creates IRODSAccount from a home dir -func CreateAccountFromCurrentUserAndHome() (*types.IRODSAccount, error) { +// CreateIcommandsEnvironmentManagerWithDefault creates ICommandsEnvironmentManager +func CreateIcommandsEnvironmentManagerWithDefault() (*ICommandsEnvironmentManager, error) { homedir, err := os.UserHomeDir() if err != nil { return nil, err @@ -103,78 +44,11 @@ func CreateAccountFromCurrentUserAndHome() (*types.IRODSAccount, error) { uid := os.Getuid() - return CreateAccountFromDir(irodsEnvDir, uid) + return CreateIcommandsEnvironmentManager(irodsEnvDir, uid) } -// CreateICommandsEnvironmentFromJSON creates ICommandsEnvironment from JSON -func CreateICommandsEnvironmentFromJSON(jsonBytes []byte) (*ICommandsEnvironment, error) { - var environment ICommandsEnvironment - err := json.Unmarshal(jsonBytes, &environment) - if err != nil { - return nil, fmt.Errorf("JSON Unmarshal Error - %v", err) - } - - return &environment, nil -} - -// ToAccount creates IRODSAccount -func (env *ICommandsEnvironment) ToAccount() *types.IRODSAccount { - negotiationRequired := false - negotiationPolicy := types.CSNegotiationRequireTCP - if types.AuthScheme(env.AuthenticationScheme) == types.AuthSchemePAM { - negotiationRequired = true - negotiationPolicy = types.CSNegotiationRequireSSL - } - - if env.ClientServerNegotiation == "request_server_negotiation" { - negotiationRequired = true - } - - return &types.IRODSAccount{ - AuthenticationScheme: types.AuthScheme(env.AuthenticationScheme), - ClientServerNegotiation: negotiationRequired, - CSNegotiationPolicy: negotiationPolicy, - Host: env.Host, - Port: env.Port, - ClientUser: env.Username, - ClientZone: env.Zone, - ProxyUser: env.Username, - ProxyZone: env.Zone, - Password: "", - DefaultResource: env.DefaultResource, - PamTTL: types.PamTTLDefault, - SSLConfiguration: &types.IRODSSSLConfig{ - CACertificateFile: env.SSLCACertificateFile, - EncryptionKeySize: env.EncryptionKeySize, - EncryptionAlgorithm: env.EncryptionAlgorithm, - SaltSize: env.EncryptionSaltSize, - HashRounds: env.EncryptionNumHashRounds, - }, - } -} - -// ToJSON converts to JSON bytes -func (env *ICommandsEnvironment) ToJSON() ([]byte, error) { - jsonBytes, err := json.Marshal(env) - if err != nil { - return nil, fmt.Errorf("JSON Marshal Error - %v", err) - } - - return jsonBytes, nil -} - -// ToAccount creates IRODSAccount -func (env *ICommandsEnvironment) ToFile(envPath string) error { - jsonByte, err := env.ToJSON() - if err != nil { - return err - } - - return ioutil.WriteFile(envPath, jsonByte, 0664) -} - -// CreateICommandsEnvironmentFromAccount creates ICommandsEnvironment from IRODSAccount -func CreateICommandsEnvironmentFromAccount(account *types.IRODSAccount) (*ICommandsEnvironment, error) { +// CreateIcommandsEnvironmentManagerFromIRODSAccount creates ICommandsEnvironmentManager from IRODSAccount +func CreateIcommandsEnvironmentManagerFromIRODSAccount(envDirPath string, uid int, account *types.IRODSAccount) (*ICommandsEnvironmentManager, error) { csNegotiation := "" if account.ClientServerNegotiation { csNegotiation = "request_server_negotiation" @@ -209,51 +83,175 @@ func CreateICommandsEnvironmentFromAccount(account *types.IRODSAccount) (*IComma environment.EncryptionNumHashRounds = account.SSLConfiguration.HashRounds } - return &environment, nil + return &ICommandsEnvironmentManager{ + dirPath: envDirPath, + uid: uid, + password: account.Password, + environment: &environment, + session: &ICommandsEnvironment{}, + }, nil } -// SaveAccountToCurrentUserAndHome creates environment file on a home dir -func SaveAccountToCurrentUserAndHome(account *types.IRODSAccount) error { +// CreateIcommandsEnvironmentManagerFromIRODSAccountWithDefault creates ICommandsEnvironmentManager +func CreateIcommandsEnvironmentManagerFromIRODSAccountWithDefault(account *types.IRODSAccount) (*ICommandsEnvironmentManager, error) { homedir, err := os.UserHomeDir() if err != nil { - return err + return nil, err } irodsEnvDir := filepath.Join(homedir, environmentDir) uid := os.Getuid() - return SaveAccountToDir(irodsEnvDir, uid, account) + return CreateIcommandsEnvironmentManagerFromIRODSAccount(irodsEnvDir, uid, account) +} + +// Load loads from environment dir +func (mgr *ICommandsEnvironmentManager) Load() error { + env, err := mgr.readEnvironment() + if err != nil { + return err + } + + mgr.environment = env + + session, err := mgr.readSession() + if err != nil { + return err + } + + mgr.session = session + + password, err := mgr.readPassword() + if err != nil { + return err + } + + mgr.password = password + return nil +} + +// readEnvironment reads environment +func (mgr *ICommandsEnvironmentManager) readEnvironment() (*ICommandsEnvironment, error) { + environmentFilePath := filepath.Join(mgr.dirPath, environmentFile) + return CreateICommandsEnvironmentFromFile(environmentFilePath) } -// SaveAccountToDir creates environment file on a dir -func SaveAccountToDir(envDirPath string, uid int, account *types.IRODSAccount) error { - st, err := os.Stat(envDirPath) +// readPassword decyphers password file (.irodsA) +func (mgr *ICommandsEnvironmentManager) readPassword() (string, error) { + passwordFilePath := filepath.Join(mgr.dirPath, passwordFile) + return DecodePasswordFile(passwordFilePath, mgr.uid) +} + +// readSession reads session +func (mgr *ICommandsEnvironmentManager) readSession() (*ICommandsEnvironment, error) { + ppid := os.Getppid() + sessionFilename := fmt.Sprintf("%s.%d", environmentFile, ppid) + sessionFilePath := filepath.Join(mgr.dirPath, sessionFilename) + + _, err := os.Stat(sessionFilePath) if err != nil { if os.IsNotExist(err) { - err := os.MkdirAll(envDirPath, 0700) + return &ICommandsEnvironment{}, nil + } else { + return nil, err + } + } + + return CreateICommandsEnvironmentFromFile(sessionFilePath) + +} + +func (mgr *ICommandsEnvironmentManager) ToIRODSAccount() (*types.IRODSAccount, error) { + if mgr.environment == nil { + return nil, fmt.Errorf("environment is not set") + } + + account := mgr.environment.ToIRODSAccount() + account.Password = mgr.password + return account, nil +} + +// makeDir makes a directory for environment files +func (mgr *ICommandsEnvironmentManager) makeDir() error { + st, err := os.Stat(mgr.dirPath) + if err != nil { + if os.IsNotExist(err) { + err := os.MkdirAll(mgr.dirPath, 0700) if err != nil { return err } + return nil } else { return err } } if !st.IsDir() { - return fmt.Errorf("path %s is not a directory", envDirPath) + return fmt.Errorf("path %s is not a directory", mgr.dirPath) } - environmentFilePath := filepath.Join(envDirPath, environmentFile) - env, err := CreateICommandsEnvironmentFromAccount(account) + return nil +} + +// Save saves to a dir +func (mgr *ICommandsEnvironmentManager) Save() error { + err := mgr.makeDir() if err != nil { return err } - err = env.ToFile(environmentFilePath) + if mgr.environment == nil { + return fmt.Errorf("environment is not set") + } + + environmentFilePath := filepath.Join(mgr.dirPath, environmentFile) + err = mgr.environment.ToFile(environmentFilePath) + if err != nil { + return err + } + + passwordFilePath := filepath.Join(mgr.dirPath, passwordFile) + return EncodePasswordFile(passwordFilePath, mgr.password, mgr.uid) +} + +// SaveSession saves session to a dir +func (mgr *ICommandsEnvironmentManager) SaveSession() error { + err := mgr.makeDir() if err != nil { return err } - passwordFilePath := filepath.Join(envDirPath, passwordFile) - return EncodePasswordFile(passwordFilePath, account.Password, uid) + if mgr.session == nil { + return nil + } + + ppid := os.Getppid() + sessionFilename := fmt.Sprintf("%s.%d", environmentFile, ppid) + sessionFilePath := filepath.Join(mgr.dirPath, sessionFilename) + return mgr.session.ToFile(sessionFilePath) +} + +// GetDirPath returns dir path for environment +func (mgr *ICommandsEnvironmentManager) GetDirPath() string { + return mgr.dirPath +} + +// GetUID returns uid for environment +func (mgr *ICommandsEnvironmentManager) GetUID() int { + return mgr.uid +} + +// GetPassword returns password for environment +func (mgr *ICommandsEnvironmentManager) GetPassword() string { + return mgr.password +} + +// GetEnvironment returns environment +func (mgr *ICommandsEnvironmentManager) GetEnvironment() *ICommandsEnvironment { + return mgr.environment +} + +// GetSession returns environment session +func (mgr *ICommandsEnvironmentManager) GetSession() *ICommandsEnvironment { + return mgr.session } diff --git a/utils/icommands/environrment_raw.go b/utils/icommands/environrment_raw.go new file mode 100644 index 0000000..b858091 --- /dev/null +++ b/utils/icommands/environrment_raw.go @@ -0,0 +1,121 @@ +package icommands + +import ( + "encoding/json" + "fmt" + "io/ioutil" + + "github.com/cyverse/go-irodsclient/irods/types" +) + +// ICommandsEnvironment stores irods environment data (config file) +type ICommandsEnvironment struct { + AuthenticationFile string `json:"irods_authentication_file,omitempty"` + AuthenticationScheme string `json:"irods_authentication_scheme,omitempty"` + ClientServerNegotiation string `json:"irods_client_server_negotiation,omitempty"` + ClientServerPolicy string `json:"irods_client_server_policy,omitempty"` + ControlPlanePort int `json:"irods_control_plane_port,omitempty"` + ControlPlaneKey string `json:"irods_control_plane_key,omitempty"` + CurrentWorkingDir string `json:"irods_cwd,omitempty"` + Debug int `json:"irods_debug,omitempty"` + DefaultHashScheme string `json:"irods_default_hash_scheme,omitempty"` + Host string `json:"irods_host,omitempty"` + Port int `json:"irods_port,omitempty"` + Username string `json:"irods_user_name,omitempty"` + Zone string `json:"irods_zone_name,omitempty"` + DefaultResource string `json:"irods_default_resource,omitempty"` + EncryptionAlgorithm string `json:"irods_encryption_algorithm,omitempty"` + EncryptionKeySize int `json:"irods_encryption_key_size,omitempty"` + EncryptionNumHashRounds int `json:"irods_encryption_num_hash_rounds,omitempty"` + EncryptionSaltSize int `json:"irods_encryption_salt_size,omitempty"` + GSIServerDN string `json:"irods_gsi_server_dn,omitempty"` + Home string `json:"irods_home,omitempty"` + LogLevel int `json:"irods_log_level,omitempty"` + MatchHashPolicy string `json:"irods_match_hash_policy,omitempty"` + PluginsHome string `json:"irods_plugins_home,omitempty"` + SSLCACertificateFile string `json:"irods_ssl_ca_certificate_file,omitempty"` + SSLCACertificatePath string `json:"irods_ssl_ca_certificate_path,omitempty"` + SSLCertificateChainFile string `json:"irods_ssl_certificate_chain_file,omitempty"` + SSLCertificateKeyFile string `json:"irods_ssl_certificate_key_file,omitempty"` + SSLDHParamsFile string `json:"irods_ssl_dh_params_file,omitempty"` + SSLVerifyServer string `json:"irods_ssl_verify_server,omitempty"` + XMessageHost string `json:"irods_xmsg_host,omitempty"` + XMessagePort int `json:"irods_xmsg_port,omitempty"` +} + +// CreateICommandsEnvironmentFromFile creates ICommandsEnvironment from a file +func CreateICommandsEnvironmentFromFile(envPath string) (*ICommandsEnvironment, error) { + data, err := ioutil.ReadFile(envPath) + if err != nil { + return nil, err + } + + return CreateICommandsEnvironmentFromJSON(data) +} + +// CreateICommandsEnvironmentFromJSON creates ICommandsEnvironment from JSON +func CreateICommandsEnvironmentFromJSON(jsonBytes []byte) (*ICommandsEnvironment, error) { + var environment ICommandsEnvironment + err := json.Unmarshal(jsonBytes, &environment) + if err != nil { + return nil, fmt.Errorf("JSON Unmarshal Error - %v", err) + } + + return &environment, nil +} + +// ToIRODSAccount creates IRODSAccount +func (env *ICommandsEnvironment) ToIRODSAccount() *types.IRODSAccount { + negotiationRequired := false + negotiationPolicy := types.CSNegotiationRequireTCP + if types.AuthScheme(env.AuthenticationScheme) == types.AuthSchemePAM { + negotiationRequired = true + negotiationPolicy = types.CSNegotiationRequireSSL + } + + if env.ClientServerNegotiation == "request_server_negotiation" { + negotiationRequired = true + } + + return &types.IRODSAccount{ + AuthenticationScheme: types.AuthScheme(env.AuthenticationScheme), + ClientServerNegotiation: negotiationRequired, + CSNegotiationPolicy: negotiationPolicy, + Host: env.Host, + Port: env.Port, + ClientUser: env.Username, + ClientZone: env.Zone, + ProxyUser: env.Username, + ProxyZone: env.Zone, + Password: "", + DefaultResource: env.DefaultResource, + PamTTL: types.PamTTLDefault, + SSLConfiguration: &types.IRODSSSLConfig{ + CACertificateFile: env.SSLCACertificateFile, + EncryptionKeySize: env.EncryptionKeySize, + EncryptionAlgorithm: env.EncryptionAlgorithm, + SaltSize: env.EncryptionSaltSize, + HashRounds: env.EncryptionNumHashRounds, + }, + } +} + +// ToJSON converts to JSON bytes +func (env *ICommandsEnvironment) ToJSON() ([]byte, error) { + jsonBytes, err := json.Marshal(env) + if err != nil { + return nil, fmt.Errorf("JSON Marshal Error - %v", err) + } + + return jsonBytes, nil +} + +// ToFile saves to a file +func (env *ICommandsEnvironment) ToFile(envPath string) error { + jsonByte, err := env.ToJSON() + if err != nil { + return err + } + + return ioutil.WriteFile(envPath, jsonByte, 0664) +}