-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨
[command]
Add a way to run commands as a different user without c…
…hanging the command definition (#333) <!-- Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved. SPDX-License-Identifier: Apache-2.0 --> ### Description this is mostly for `posix` platforms where some commands need to be run as `sudo` or as a different user. ### Test Coverage <!-- Please put an `x` in the correct box e.g. `[x]` to indicate the testing coverage of this change. --> - [x] This change is covered by existing or additional automated tests. - [ ] Manual testing has been performed (and evidence provided) as automated testing was not feasible. - [ ] Additional tests are not required for this change (e.g. documentation update).
- Loading branch information
1 parent
8332a4f
commit 3aa6349
Showing
9 changed files
with
190 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
:sparkles: `[platform]` Add way to run commands as a user with privileges on posix systems |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
:sparkles: `[command]` Add utilities to translate commands so that they are run as a separate user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//go:build linux || unix || (js && wasm) || darwin || aix || dragonfly || freebsd || nacl || netbsd || openbsd || solaris | ||
// +build linux unix js,wasm darwin aix dragonfly freebsd nacl netbsd openbsd solaris | ||
|
||
package platform | ||
|
||
import "github.com/ARM-software/golang-utils/utils/subprocess/command" | ||
|
||
var ( | ||
// sudoCommand describes the command to use to execute command as root | ||
// when running in Docker, change to [gosu root](https://github.com/tianon/gosu) | ||
sudoCommand = command.Sudo() | ||
) | ||
|
||
// DefineSudoCommand defines the command to run to be `root` or a user with enough privileges to manage accounts. | ||
// e.g. | ||
// - args="sudo" to run commands as `root` | ||
// - args="su", "tom" if `tom` has enough privileges to run the command | ||
// - args="gosu", "tom" if `tom` has enough privileges to run the command in a container and `gosu` is installed | ||
func DefineSudoCommand(args ...string) { | ||
sudoCommand = command.NewCommandAsDifferentUser(args...) | ||
} | ||
|
||
func defineCommandWithPrivileges(args ...string) (string, []string) { | ||
return sudoCommand.RedefineCommand(args...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package command | ||
|
||
import "strings" | ||
|
||
// CommandAsDifferentUser helps redefining commands so that they are run as a different user or with more privileges. | ||
type CommandAsDifferentUser struct { | ||
// changeUserCmd describes the command to use to execute any command as a different user | ||
// e.g. it can be set as "sudo" to run commands as `root` or as "su","tom" or "gosu","jack" | ||
changeUserCmd []string | ||
} | ||
|
||
// Redefine redefines a command so that it will be run as a different user. | ||
func (c *CommandAsDifferentUser) Redefine(cmd string, args ...string) (cmdName string, cmdArgs []string) { | ||
newArgs := []string{cmd} | ||
newArgs = append(newArgs, args...) | ||
cmdName, cmdArgs = c.RedefineCommand(newArgs...) | ||
return | ||
} | ||
|
||
// RedefineCommand is the same as Redefine but with no separation between the command and its arguments (like the command in Docker) | ||
func (c *CommandAsDifferentUser) RedefineCommand(args ...string) (cmdName string, cmdArgs []string) { | ||
if len(c.changeUserCmd) > 0 { | ||
cmdName = c.changeUserCmd[0] | ||
for i := 1; i < len(c.changeUserCmd); i++ { | ||
cmdArgs = append(cmdArgs, c.changeUserCmd[i]) | ||
} | ||
cmdArgs = append(cmdArgs, args...) | ||
} else { | ||
cmdName = args[0] | ||
for i := 1; i < len(args); i++ { | ||
cmdArgs = append(cmdArgs, args[i]) | ||
} | ||
} | ||
return | ||
} | ||
|
||
// RedefineInShellForm returns the new command defined in shell form. | ||
func (c *CommandAsDifferentUser) RedefineInShellForm(cmd string, args ...string) string { | ||
ncmd, nargs := c.Redefine(cmd, args...) | ||
return AsShellForm(ncmd, nargs...) | ||
} | ||
|
||
// NewCommandAsDifferentUser defines a command wrapper which helps redefining commands so that they are run as a different user. | ||
// e.g. | ||
// - switchUserCmd="sudo" to run commands as `root` | ||
// - switchUserCmd="su", "tom" if `tom` has enough privileges to run the command | ||
// - switchUserCmd="gosu", "tom" if `tom` has enough privileges to run the command in a container and `gosu` is installed | ||
func NewCommandAsDifferentUser(switchUserCmd ...string) *CommandAsDifferentUser { | ||
return &CommandAsDifferentUser{changeUserCmd: switchUserCmd} | ||
} | ||
|
||
// NewCommandAsRoot will create a command translator which will run command with `sudo` | ||
func NewCommandAsRoot() *CommandAsDifferentUser { | ||
return NewCommandAsDifferentUser("sudo") | ||
} | ||
|
||
// Sudo will call commands with `sudo`. Similar to NewCommandAsRoot | ||
func Sudo() *CommandAsDifferentUser { | ||
return NewCommandAsRoot() | ||
} | ||
|
||
// NewCommandInContainerAs will redefine commands to be run in containers as `username`. It will expect [gosu](https://github.com/tianon/gosu) to be installed and the user to have been defined. | ||
func NewCommandInContainerAs(username string) *CommandAsDifferentUser { | ||
return NewCommandAsDifferentUser("gosu", username) | ||
} | ||
|
||
// Gosu is similar to NewCommandInContainerAs. | ||
func Gosu(username string) *CommandAsDifferentUser { | ||
return NewCommandInContainerAs(username) | ||
} | ||
|
||
// Su will run commands as the user username using [su](https://www.unix.com/man-page/posix/1/su/) | ||
func Su(username string) *CommandAsDifferentUser { | ||
return NewCommandAsDifferentUser("su", username) | ||
} | ||
|
||
// Me will run the commands without switching user. It is a no operation wrapper. | ||
func Me() *CommandAsDifferentUser { | ||
return NewCommandAsDifferentUser() | ||
} | ||
|
||
// AsShellForm returns a command in its shell form. | ||
func AsShellForm(cmd string, args ...string) string { | ||
newCmd := []string{cmd} | ||
newCmd = append(newCmd, args...) | ||
return strings.Join(newCmd, " ") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package command | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/bxcodec/faker/v3" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestCommandAsDifferentUser_Redefine(t *testing.T) { | ||
assert.Equal(t, "sudo test 1 2 3", Sudo().RedefineInShellForm("test", "1", "2", "3")) | ||
name := faker.Username() | ||
assert.Equal(t, fmt.Sprintf("su %v test 1 2 3", name), Su(name).RedefineInShellForm("test", "1", "2", "3")) | ||
name = faker.Username() | ||
assert.Equal(t, fmt.Sprintf("gosu %v test 1 2 3", name), Gosu(name).RedefineInShellForm("test", "1", "2", "3")) | ||
assert.Equal(t, "test 1 2 3", NewCommandAsDifferentUser().RedefineInShellForm("test", "1", "2", "3")) | ||
assert.Equal(t, "test", Me().RedefineInShellForm("test")) | ||
assert.Empty(t, Me().RedefineInShellForm("")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters