An Interest In:
Web News this Week
- April 2, 2024
- April 1, 2024
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
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
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To