Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Update config file access permission
Browse files Browse the repository at this point in the history
Default folder should have 700 and file have 600.
Make sure config file path is 600 before accepting.
  • Loading branch information
VijayanB committed Dec 17, 2021
1 parent 46ef2ea commit d9039dc
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 31 deletions.
67 changes: 67 additions & 0 deletions commands/file_operation_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// +build !windows

/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package commands

import (
"fmt"
"os"
"path/filepath"
)

const (
FolderPermission = 0700 // only owner can read, write and execute
FilePermission = 0600 // only owner can read and write
)

// createDefaultConfigFolderIfNotExists creates default config file along with folder if
// it doesn't exists
func createDefaultConfigFileIfNotExists() error {
defaultFilePath := GetDefaultConfigFilePath()
if isExists(defaultFilePath) {
return nil
}
folderPath := filepath.Dir(defaultFilePath)
if !isExists(folderPath) {
err := os.Mkdir(folderPath, FolderPermission)
if err != nil {
return err
}
}
f, err := os.Create(defaultFilePath)
if err != nil {
return err
}
if err = f.Chmod(FilePermission); err != nil {
return err
}
return f.Close()
}

func checkConfigFilePermission(configFilePath string) error {
//check for config file permission
info, err := os.Stat(configFilePath)
if err != nil {
return fmt.Errorf("failed to get config file info due to: %w", err)
}
mode := info.Mode().Perm()

if mode != FilePermission {
return fmt.Errorf("permissions %o for '%s' are too open. It is required that your config file is NOT accessible by others", mode, configFilePath)
}
return nil
}
29 changes: 29 additions & 0 deletions commands/file_operation_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// +build windows

/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package commands

import "fmt"

func createDefaultConfigFileIfNotExists() error {
return fmt.Errorf("creating default config file is not supported for windows. Please create manually")
}

func checkConfigFilePermission(configFilePath string) error {
// since windows doesn't support create default config file, no validation is required
return nil
}
3 changes: 3 additions & 0 deletions commands/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ func getProfileController(cfgFlagValue string) (profile.Controller, error) {
if err != nil {
return nil, fmt.Errorf("failed to get config file due to: %w", err)
}
if err = checkConfigFilePermission(configFilePath); err != nil {
return nil, err
}
configController := config.New(configFilePath)
profileController := profile.New(configController)
return profileController, nil
Expand Down
24 changes: 1 addition & 23 deletions commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ const (
defaultConfigFileName = "config"
flagConfig = "config"
flagProfileName = "profile"
folderPermission = 0755 // only owner can write, while everyone can read and execute
odfeConfigEnvVarName = "ODFE_CLI_CONFIG"
RootCommandName = "odfe-cli"
version = "1.1.0"
version = "1.1.1"
)

func buildVersionString() string {
Expand Down Expand Up @@ -99,27 +98,6 @@ func GetConfigFilePath(configFlagValue string) (string, error) {
return GetDefaultConfigFilePath(), nil
}

// createDefaultConfigFolderIfNotExists creates default config file along with folder if
// it doesn't exists
func createDefaultConfigFileIfNotExists() error {
defaultFilePath := GetDefaultConfigFilePath()
if isExists(defaultFilePath) {
return nil
}
folderPath := filepath.Dir(defaultFilePath)
if !isExists(folderPath) {
err := os.Mkdir(folderPath, folderPermission)
if err != nil {
return err
}
}
f, err := os.Create(defaultFilePath)
if err != nil {
return err
}
return f.Close()
}

//isExists check if given path exists or not
//if path is just a name, it will check in current directory
func isExists(path string) bool {
Expand Down
74 changes: 66 additions & 8 deletions commands/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
package commands

import (
"fmt"
"io/ioutil"
"odfe-cli/entity"
"os"
"path/filepath"
"runtime"
"testing"

Expand Down Expand Up @@ -64,24 +67,56 @@ func TestVersionString(t *testing.T) {
assert.EqualValues(t, expected, cmd.Version)
})
}
func createTempConfigFile(testFilePath string) (*os.File, error) {
content, err := ioutil.ReadFile(testFilePath)
if err != nil {
return nil, err
}
tmpfile, err := ioutil.TempFile(os.TempDir(), "test-file")
if err != nil {
return nil, err
}
if _, err := tmpfile.Write(content); err != nil {
os.Remove(tmpfile.Name()) // clean up
return nil, err
}
if runtime.GOOS == "windows" {
return tmpfile, nil
}
if err := tmpfile.Chmod(0600); err != nil {
os.Remove(tmpfile.Name()) // clean up
return nil, err
}
return tmpfile, nil
}


func TestGetProfile(t *testing.T) {
t.Run("get default profile", func(t *testing.T) {
root := GetRoot()
assert.NotNil(t, root)
root.SetArgs([]string{"--config", "testdata/config.yaml"})
_, err := root.ExecuteC()
profileFile, err := createTempConfigFile("testdata/config.yaml")
assert.NoError(t, err)
filePath, err := filepath.Abs(profileFile.Name())
assert.NoError(t, err)
root.SetArgs([]string{"--config", filePath})
_, err = root.ExecuteC()
assert.NoError(t, err)
actual, err := GetProfile()
assert.NoError(t, err)
expectedProfile := entity.Profile{Name: "default", Endpoint: "http://localhost:9200", UserName: "default", Password: "admin"}
assert.EqualValues(t, expectedProfile, *actual)
os.Remove(profileFile.Name())
})
t.Run("test get profile", func(t *testing.T) {
root := GetRoot()
assert.NotNil(t, root)
root.SetArgs([]string{"--config", "testdata/config.yaml", "--profile", "test"})
_, err := root.ExecuteC()
profileFile, err := createTempConfigFile("testdata/config.yaml")
assert.NoError(t, err)
filePath, err := filepath.Abs(profileFile.Name())
assert.NoError(t, err)
root.SetArgs([]string{"--config", filePath, "--profile", "test"})
_, err = root.ExecuteC()
assert.NoError(t, err)
actual, err := GetProfile()
assert.NoError(t, err)
Expand All @@ -91,11 +126,16 @@ func TestGetProfile(t *testing.T) {
t.Run("Profile mismatch", func(t *testing.T) {
root := GetRoot()
assert.NotNil(t, root)
root.SetArgs([]string{"--config", "testdata/config.yaml", "--profile", "test1"})
_, err := root.ExecuteC()
profileFile, err := createTempConfigFile("testdata/config.yaml")
assert.NoError(t, err)
_, err = GetProfile()
filePath, err := filepath.Abs(profileFile.Name())
assert.NoError(t, err)
root.SetArgs([]string{"--config", filePath, "--profile", "test1"})
_, err = root.ExecuteC()
assert.NoError(t, err)
a, err := GetProfile()
assert.EqualErrorf(t, err, "profile 'test1' does not exist", "unexpected error")
fmt.Print(a)
})
t.Run("no config file found", func(t *testing.T) {
root := GetRoot()
Expand All @@ -104,6 +144,24 @@ func TestGetProfile(t *testing.T) {
_, err := root.ExecuteC()
assert.NoError(t, err)
_, err = GetProfile()
assert.EqualError(t, err, "open testdata/config1.yaml: no such file or directory", "unexpected error")
assert.EqualError(t, err, "failed to get config file info due to: stat testdata/config1.yaml: no such file or directory", "unexpected error")
})
t.Run("invalid config file permission", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skipf("test case does not work on %s", runtime.GOOS)
}

root := GetRoot()
assert.NotNil(t, root)
profileFile, err := createTempConfigFile("testdata/config.yaml")
assert.NoError(t, err)
assert.NoError(t, profileFile.Chmod(0750))
filePath, err := filepath.Abs(profileFile.Name())
assert.NoError(t, err)
root.SetArgs([]string{"--config", filePath, "--profile", "test"})
_, err = root.ExecuteC()
assert.NoError(t, err)
_, err = GetProfile()
assert.EqualError(t, err, fmt.Sprintf("permissions 750 for '%s' are too open. It is required that your config file is NOT accessible by others", filePath), "unexpected error")
})
}

0 comments on commit d9039dc

Please sign in to comment.