Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 12, 2022 09:14 pm GMT

User permission checking in Go

This article was originally published on my personal blog.

The problem

While working on my go-pf module, I was faced with the problem to make sure that the /dev/pf device is readable and writable. While other languages like the Shell or Perl offer test functions to check if the current user/process has read/write/exec permissions on the specific file, in Go this task is not trivial.

Here is how we can do it with Perl:

#!/usr/bin/env perlmy $file = "test.txt";if(!-r $file || !-w $file) {    print $file . " is not read- and/or writable to the process
"; exit(1);}print "We're all good!
"

If we run a quick one-liner in the Shell then, we can see how the script works:

$ for i in 100 200 400 500 600 700; do echo "chmod: $i"; \    chmod $i test.txt; perl test.pl; donechmod: 100test.txt is not read- and/or writable to the processchmod: 200test.txt is not read- and/or writable to the processchmod: 400test.txt is not read- and/or writable to the processchmod: 500test.txt is not read- and/or writable to the processchmod: 600We're all good!chmod: 700We're all good!

In Go we would need to rely on os.Lstat which would then provide us with the Mode().Perm() method, which we could use to get the file mode of the file. But that is only half the rent. Just because the file might return a file mode of 700, which basically would mean that read and write permissions are given, the file might not be owned by us. It gets even more complicated when we need to take in account that we also have group and "other" file permissions.

The solution

Since I couldn't find a ready-made solution, I made it my task to solve the problem - preferably in an equally simple way, such as in Perl.

As mentioned, we can utilize os.Lstat as our starting point. With the file permissions and some bit shifting, we can easily figure out if a file is read/write/executable for "other", group and user. With this information at hand, the next step is to figure out the owner and the group of the file in question. For UNIX-like operating systems, Go provides us with the Sys() method on our FileInfo that is returned by os.Lstat. We can try to type-cast the result of Sys() into a *syscall.Stat_t pointer. If this succeeds, we will get the information that we need with the Uid and Gid properties.

Problem number one is solved, we can see who owns the file and check if our current user is indeed the owner. The os/user.Current method can help us with that. Our next task is group ownership - again this is not a trivial task, since a user can be part of several groups. Again the Go standard library provides us with the tools we need in form of the User.GroupIds method, which will give us a list of groups that our user is part of. With this list, it is fairly easy to figure out if our user is part of the group of the file. The second problem is solved as well. For the "other" permission we can again work off with some bit-shifting.

go-fileperm

You probably guessed already, that I built something out of all of this. go-fileperm uses all the information I discussed before into one simple-to-use Go module. Let's have a look.

First of all, we use the New() method to get an instance of the type UserPerm. New takes a string as an argument, which is the path to the file in question. If this succeeds, the module will provide you with a couple of methods to check the file access permissions (all of which return a boolean value):

  • UserExecutable(): returns true if the current user/process has execution permission on the file in question.
  • UserWritable(): returns true if the current user/process has write permission on the file in question.
  • UserReadable(): returns true if the current user/process has read permission on the file in question.
  • UserWriteExecutable(): returns true if the current user/process has write and execution permission on the file in question.
  • UserReadExecutable(): returns true if the current user/process has read and execution permission on thefile in question.
  • UserWriteReadExecutable(): returns true if the current user/process has read, write and execution permission on the file in question.

Performance

Since go-fileperm mostly makes use of bit-shifting, the performance of the module is pretty fast. Also, we work allocation-free, which is always a plus :)

goos: darwingoarch: arm64pkg: github.com/wneessen/go-filepermBenchmarkPermUser_UserReadableBenchmarkPermUser_UserReadable-8                 7364846               143.6 ns/op             0 B/op          0 allocs/opBenchmarkPermUser_UserWritableBenchmarkPermUser_UserWritable-8                 7803267               154.9 ns/op             0 B/op          0 allocs/opBenchmarkPermUser_UserExecutableBenchmarkPermUser_UserExecutable-8               7922624               149.2 ns/op             0 B/op          0 allocs/opBenchmarkPermUser_UserWriteReadableBenchmarkPermUser_UserWriteReadable-8            6494815               186.1 ns/op             0 B/op          0 allocs/opBenchmarkPermUser_UserWriteExecutableBenchmarkPermUser_UserWriteExecutable-8          6590229               181.0 ns/op             0 B/op          0 allocs/opBenchmarkPermUser_UserReadExecutableBenchmarkPermUser_UserReadExecutable-8           6190532               184.7 ns/op             0 B/op          0 allocs/opBenchmarkPermUser_UserWriteReadExecutableBenchmarkPermUser_UserWriteReadExecutable-8      5728713               208.8 ns/op             0 B/op          0 allocs/opPASS

Full code example

Let's check out a full code example and apply our one-liner that we used in the beginning.

package mainimport (    "fmt"    "log"    "github.com/wneessen/go-fileperm")func main() {    p, err := fileperm.New("test.txt")    if err != nil {        log.Fatalf("failed to create UserPerm instance: %s", err)    }    fmt.Printf("test.txt is user-readable:              %t
", p.UserReadable()) fmt.Printf("test.txt is user-writable: %t
", p.UserWritable()) fmt.Printf("test.txt is user-executable: %t
", p.UserExecutable()) fmt.Printf("test.txt is user-read/writable: %t
", p.UserWriteReadable()) fmt.Printf("test.txt is user-read/executable: %t
", p.UserReadExecutable()) fmt.Printf("test.txt is user-write/executable: %t
", p.UserWriteExecutable()) fmt.Printf("test.txt is user-read/write/executable: %t
", p.UserWriteReadExecutable())}
$ for i in 100 200 400 500 600 700; do echo "chmod: $i"; \    chmod $i test.txt; go run main.go; donechmod: 100test.txt is user-readable:              falsetest.txt is user-writable:              falsetest.txt is user-executable:            truetest.txt is user-read/writable:         falsetest.txt is user-read/executable:       falsetest.txt is user-write/executable:      falsetest.txt is user-read/write/executable: falsechmod: 200test.txt is user-readable:              falsetest.txt is user-writable:              truetest.txt is user-executable:            falsetest.txt is user-read/writable:         falsetest.txt is user-read/executable:       falsetest.txt is user-write/executable:      falsetest.txt is user-read/write/executable: falsechmod: 400test.txt is user-readable:              truetest.txt is user-writable:              falsetest.txt is user-executable:            falsetest.txt is user-read/writable:         falsetest.txt is user-read/executable:       falsetest.txt is user-write/executable:      falsetest.txt is user-read/write/executable: falsechmod: 500test.txt is user-readable:              truetest.txt is user-writable:              falsetest.txt is user-executable:            truetest.txt is user-read/writable:         falsetest.txt is user-read/executable:       truetest.txt is user-write/executable:      falsetest.txt is user-read/write/executable: falsechmod: 600test.txt is user-readable:              truetest.txt is user-writable:              truetest.txt is user-executable:            falsetest.txt is user-read/writable:         truetest.txt is user-read/executable:       falsetest.txt is user-write/executable:      falsetest.txt is user-read/write/executable: falsechmod: 700test.txt is user-readable:              truetest.txt is user-writable:              truetest.txt is user-executable:            truetest.txt is user-read/writable:         truetest.txt is user-read/executable:       truetest.txt is user-write/executable:      truetest.txt is user-read/write/executable: true

Conclusion

As you can see, the module is pretty easy to use and hopefully will be a helpful tool for some of you. As usual, I've published it on GitHub under the MIT license, so feel free to give it a try or even contribute if you think there is things that need improvements.


Original Link: https://dev.to/wneessen/user-permission-checking-in-go-2ohp

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To