From 48707504cd55245bd9606bb0a6f80372e544c687 Mon Sep 17 00:00:00 2001 From: Jan De Dobbeleer Date: Mon, 16 May 2022 07:27:50 +0200 Subject: [PATCH] feat(path): add Writable resolves #2269 --- src/environment/shell.go | 1 + src/environment/unix.go | 34 ++++++++++++++++++++++++++++++++++ src/environment/windows.go | 22 ++++++++++++++++++++++ src/mock/environment.go | 5 +++++ src/segments/path.go | 2 ++ src/segments/path_test.go | 2 ++ website/docs/segments/path.md | 2 ++ 7 files changed, 68 insertions(+) diff --git a/src/environment/shell.go b/src/environment/shell.go index d2462d77..1f11cf9a 100644 --- a/src/environment/shell.go +++ b/src/environment/shell.go @@ -164,6 +164,7 @@ type Environment interface { HasFileInParentDirs(pattern string, depth uint) bool ResolveSymlink(path string) (string, error) DirMatchesOneOf(dir string, regexes []string) bool + DirIsWritable(path string) bool CommandPath(command string) string HasCommand(command string) bool FileContent(file string) string diff --git a/src/environment/unix.go b/src/environment/unix.go index 6e7b3d07..d218b08a 100644 --- a/src/environment/unix.go +++ b/src/environment/unix.go @@ -6,6 +6,7 @@ import ( "errors" "os" "strings" + "syscall" "time" "github.com/shirou/gopsutil/v3/host" @@ -123,3 +124,36 @@ func (env *ShellEnvironment) WifiNetwork() (*WifiInfo, error) { func (env *ShellEnvironment) LookWinAppPath(file string) (string, error) { return "", errors.New("not relevant") } + +func (env *ShellEnvironment) DirIsWritable(path string) bool { + defer env.Trace(time.Now(), "DirIsWritable") + info, err := os.Stat(path) + if err != nil { + env.Log(Error, "DirIsWritable", err.Error()) + return false + } + + if !info.IsDir() { + env.Log(Error, "DirIsWritable", "Path isn't a directory") + return false + } + + // Check if the user bit is enabled in file permission + if info.Mode().Perm()&(1<<(uint(7))) == 0 { + env.Log(Error, "DirIsWritable", "Write permission bit is not set on this file for user") + return false + } + + var stat syscall.Stat_t + if err = syscall.Stat(path, &stat); err != nil { + env.Log(Error, "DirIsWritable", err.Error()) + return false + } + + if uint32(os.Geteuid()) != stat.Uid { + env.Log(Error, "DirIsWritable", "User doesn't have permission to write to this directory") + return false + } + + return true +} diff --git a/src/environment/windows.go b/src/environment/windows.go index ce1c7574..ace4f519 100644 --- a/src/environment/windows.go +++ b/src/environment/windows.go @@ -527,3 +527,25 @@ type DOT11_SSID struct { // nolint: revive uSSIDLength uint32 ucSSID [32]uint8 } + +func (env *ShellEnvironment) DirIsWritable(path string) bool { + defer env.Trace(time.Now(), "DirIsWritable") + info, err := os.Stat(path) + if err != nil { + env.Log(Error, "DirIsWritable", err.Error()) + return false + } + + if !info.IsDir() { + env.Log(Error, "DirIsWritable", "Path isn't a directory") + return false + } + + // Check if the user bit is enabled in file permission + if info.Mode().Perm()&(1<<(uint(7))) == 0 { + env.Log(Error, "DirIsWritable", "Write permission bit is not set on this file for user") + return false + } + + return true +} diff --git a/src/mock/environment.go b/src/mock/environment.go index 7af47785..51b014a4 100644 --- a/src/mock/environment.go +++ b/src/mock/environment.go @@ -238,3 +238,8 @@ func (env *MockedEnvironment) Trace(start time.Time, function string, args ...st func (env *MockedEnvironment) Log(logType environment.LogType, funcName, message string) { _ = env.Called(logType, funcName, message) } + +func (env *MockedEnvironment) DirIsWritable(path string) bool { + args := env.Called(path) + return args.Bool(0) +} diff --git a/src/segments/path.go b/src/segments/path.go index ba0f7fca..a2f6219d 100644 --- a/src/segments/path.go +++ b/src/segments/path.go @@ -18,6 +18,7 @@ type Path struct { Path string StackCount int Location string + Writable bool } const ( @@ -102,6 +103,7 @@ func (pt *Path) Enabled() bool { } pt.StackCount = pt.env.StackCount() + pt.Writable = pt.env.DirIsWritable(pt.pwd) return true } diff --git a/src/segments/path_test.go b/src/segments/path_test.go index e183f85f..b2816d4b 100644 --- a/src/segments/path_test.go +++ b/src/segments/path_test.go @@ -281,6 +281,7 @@ func TestAgnosterPathStyles(t *testing.T) { env.On("GOOS").Return(tc.GOOS) env.On("StackCount").Return(0) env.On("IsWsl").Return(false) + env.On("DirIsWritable", tc.Pwd).Return(true) args := &environment.Flags{ PSWD: tc.Pswd, } @@ -403,6 +404,7 @@ func TestGetFullPath(t *testing.T) { env.On("GOOS").Return(tc.GOOS) env.On("StackCount").Return(tc.StackCount) env.On("IsWsl").Return(false) + env.On("DirIsWritable", tc.Pwd).Return(true) args := &environment.Flags{ PSWD: tc.Pswd, } diff --git a/website/docs/segments/path.md b/website/docs/segments/path.md index 78a8aab9..2c15cb47 100644 --- a/website/docs/segments/path.md +++ b/website/docs/segments/path.md @@ -147,6 +147,8 @@ folders at the same level, so if `C:\projectA\dev` and `C:\projectB\dev` exist, ### Properties - `.Path`: `string` - the current directory (based on the `style` property) +- `.Location`: `string` - the current directory (raw value) - `.StackCount`: `int` - the stack count +- `.Writable`: `boolean` - is the current directory writable by the user or not [templates]: /docs/configuration/templates