Skip to content

Commit c429ab0

Browse files
author
Sam Ban
committed
group commands for users and servers
1 parent faa0e5e commit c429ab0

File tree

10 files changed

+203
-18
lines changed

10 files changed

+203
-18
lines changed

.github/workflows/push.yaml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Develop Test
2+
3+
on:
4+
push:
5+
branches-ignore:
6+
- wip
7+
8+
jobs:
9+
Test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v1
13+
- name: Checkout submodules
14+
uses: textbook/git-checkout-submodule-action@master
15+
with:
16+
remote: true
17+
- name: Install Go
18+
uses: actions/setup-go@v1
19+
with:
20+
go-version: '1.17.x'
21+
- name: Checkout code
22+
uses: actions/checkout@v1
23+
- name: Tests
24+
run: make test

README.md

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
# SSH Manager - manage authorized_key file on remote servers
22

3-
This is a simple tool that I came up after having to on-boarding and off-boarding developers on a
4-
very colourful palette of environments from AWS to 3rd party hosting providers.
3+
This is a simple tool that I came up after having to on-boarding and off-boarding developers on a very colourful palette of environments from AWS to 3rd party hosting providers.
54

6-
As every one of my creations this tool is solving _my_ problem. It does not warranty your problem will be solved,
7-
but in that highly unlikely event please let me know, fixes and pull requests, issues are all very welcome without
8-
again the promise that I'll do anything, I'm normally really busy, apologies.
5+
As every one of my creations this tool is solving _my_ problem. It does not warranty your problem will be solved, but in that highly unlikely event please let me know, fixes and pull requests, issues are all very welcome without again the promise that I'll do anything, I'm normally really busy, apologies.
6+
7+
**Caution**: Plan your group memberships carefully, keep your management key out of any groups so you don't accidentally remove management key from any server, locking yourself out.
98

109
## Installation
1110

@@ -137,6 +136,19 @@ $ ./sshman rename user [email protected] [email protected]
137136
$ ./sshman rename server oldalias newalias
138137
```
139138

139+
### Modifying user and server groupping
140+
141+
Modify user's groups, or remove groups from user to allow global access:
142+
```sh
143+
$ ./sshman groups user [email protected] group1 group2
144+
```
145+
146+
Modify server groups or remove from all groups:
147+
```sh
148+
$ ./sshman groups server serveralias group1 group2
149+
```
150+
Note: Removing server from a group will remove all users that are on the server only because of that group. If the server is in another group, the users that are in both groups will not be removed.
151+
140152
### (Possible) Future Plans
141153

142154
- [x] Reuse stored ssh key for modifying user

backend/config.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ func (c *config) getServers(group string) []Hostentry {
7272
return servers
7373
}
7474

75+
func (c *config) getUsers(group string) []User {
76+
var users []User
77+
for _, user := range c.Users {
78+
if contains(user.Groups, group) {
79+
users = append(users, user)
80+
}
81+
}
82+
return users
83+
}
84+
7585
// AddUserToHosts adds user to all allowed hosts' authorized_keys files
7686
func (c *config) AddUserToHosts(newuser *User) {
7787
for alias, host := range c.Hosts {
@@ -205,7 +215,7 @@ func (c *config) RegisterUser(oldgroups []string, args ...string) error {
205215
c.Users[lsum] = newuser
206216
log.Printf("Registering %s %s %s %s\n", parts[0], parts[2], args[0], lsum)
207217
c.Write()
208-
return newuser.updateGroups(c, oldgroups, groups)
218+
return newuser.UpdateGroups(c, oldgroups)
209219
}
210220

211221
// UnregisterServer removes a server from the config

backend/funcs.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,22 @@ func contains(slice []string, s string) bool {
4141
return false
4242
}
4343

44-
func updates(oldItems, newItems []string) []string {
45-
var changes []string
44+
func updates(oldItems, newItems []string) (added []string, removed []string) {
4645
ma := make(map[string]struct{}, len(oldItems))
4746
mb := make(map[string]struct{}, len(newItems))
4847
for _, x := range newItems {
4948
mb[x] = struct{}{}
5049
}
5150
for _, x := range oldItems {
5251
if _, found := mb[x]; !found {
53-
changes = append(changes, x)
52+
removed = append(removed, x)
5453
}
5554
ma[x] = struct{}{}
5655
}
5756
for _, x := range newItems {
5857
if _, found := ma[x]; !found {
59-
changes = append(changes, x)
58+
added = append(added, x)
6059
}
6160
}
62-
return changes
61+
return
6362
}

backend/hostentry.go

+36-3
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,6 @@ func (h *Hostentry) delUser(u *User) error {
130130
log.Printf("Error: Not good line: '%s'\n", line)
131131
}
132132
lsum := checksum(parts[1])
133-
if _, ok := h.Config.Users[lsum]; !ok {
134-
delete(h.Config.Users, lsum)
135-
}
136133
if parts[1] == u.Key {
137134
found = true
138135
continue
@@ -149,8 +146,44 @@ func (h *Hostentry) delUser(u *User) error {
149146
return err
150147
}
151148
log.Printf("Removed %s from %s\n", u.Email, h.Alias)
149+
} else {
150+
log.Printf("user %s not on %s\n", u.Email, h.Alias)
152151
}
153152
h.Checksum = sum
154153
h.Users = userlist
155154
return nil
156155
}
156+
157+
func (h *Hostentry) UpdateGroups(c *config, oldgroups []string) error {
158+
added, removed := updates(oldgroups, h.Groups)
159+
for _, group := range added {
160+
users := c.getUsers(group)
161+
for _, u := range users {
162+
if !h.hasUser(u.Email) {
163+
log.Printf("Adding %s to %s\n", u.Email, h.Alias)
164+
err := h.addUser(&u)
165+
if err != nil {
166+
return err
167+
}
168+
}
169+
}
170+
}
171+
for _, group := range removed {
172+
users := c.getUsers(group)
173+
for _, u := range users {
174+
if match(u.Groups, h.Groups) {
175+
continue
176+
}
177+
if h.hasUser(u.Email) {
178+
log.Printf("Removing %s from %s\n", u.Email, h.Alias)
179+
err := h.delUser(&u)
180+
if err != nil {
181+
return err
182+
}
183+
}
184+
}
185+
}
186+
c.Hosts[h.Alias] = *h
187+
c.Write()
188+
return nil
189+
}

backend/user.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ type User struct {
1010
Groups []string `json:"groups"`
1111
}
1212

13-
func (u *User) updateGroups(C *config, oldgroups, newgroups []string) error {
14-
changes := updates(oldgroups, newgroups)
15-
for _, group := range changes {
13+
func (u *User) UpdateGroups(C *config, oldgroups []string) error {
14+
added, removed := updates(oldgroups, u.Groups)
15+
for _, group := range added {
1616
servers := C.getServers(group)
1717
for _, server := range servers {
1818
server.readUsers()
@@ -25,5 +25,17 @@ func (u *User) updateGroups(C *config, oldgroups, newgroups []string) error {
2525
}
2626
}
2727
}
28+
for _, group := range removed {
29+
servers := C.getServers(group)
30+
for _, server := range servers {
31+
server.readUsers()
32+
if server.hasUser(u.Email) {
33+
err := server.delUser(u)
34+
if err != nil {
35+
return err
36+
}
37+
}
38+
}
39+
}
2840
return nil
2941
}

cmd/groups.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
// groupsCmd represents the groups command
8+
var groupsCmd = &cobra.Command{
9+
Use: "groups",
10+
Short: "Modify user or server groups",
11+
Long: `Modify user's groups, or remove groups from user to allow global access:
12+
$ ./sshman groups user [email protected] group1 group2
13+
Modify server groups or remove from all groups:
14+
$ ./sshman groups server serveralias group1 group2
15+
`,
16+
}
17+
18+
func init() {
19+
rootCmd.AddCommand(groupsCmd)
20+
}

cmd/groupsServer.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package cmd
2+
3+
import (
4+
"log"
5+
6+
"github.com/shoobyban/sshman/backend"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
// groupsServerCmd represents the user group editing command
11+
var groupsServerCmd = &cobra.Command{
12+
Use: "server",
13+
Short: "Modify group assignments for a server",
14+
Long: `Modify server groups or remove from all groups:
15+
$ ./sshman groups server serveralias group1 group2
16+
`,
17+
Run: func(_ *cobra.Command, args []string) {
18+
cfg := backend.ReadConfig(backend.NewSFTP())
19+
if len(args) < 1 {
20+
return
21+
}
22+
email := args[0]
23+
groups := args[1:]
24+
if host, ok := cfg.Hosts[args[0]]; ok {
25+
oldgroups := host.Groups
26+
host.Groups = groups
27+
cfg.Hosts[args[0]] = host
28+
cfg.Write()
29+
log.Printf("Groups for %s edited: %v\n", email, host.Groups)
30+
host.UpdateGroups(cfg, oldgroups)
31+
}
32+
},
33+
}
34+
35+
func init() {
36+
groupsCmd.AddCommand(groupsServerCmd)
37+
}

cmd/groupsUser.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package cmd
2+
3+
import (
4+
"log"
5+
6+
"github.com/shoobyban/sshman/backend"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
// groupsUserCmd represents the user group editing command
11+
var groupsUserCmd = &cobra.Command{
12+
Use: "user",
13+
Short: "Modify group assignments of a user",
14+
Long: `Modify user's groups, or remove groups from user to allow global access:
15+
$ ./sshman groups user [email protected] group1 group2
16+
`,
17+
Run: func(_ *cobra.Command, args []string) {
18+
cfg := backend.ReadConfig(backend.NewSFTP())
19+
if len(args) < 1 {
20+
return
21+
}
22+
email := args[0]
23+
groups := args[1:]
24+
key, user := cfg.GetUserByEmail(email)
25+
if user != nil {
26+
oldgroups := user.Groups
27+
user.Groups = groups
28+
cfg.Users[key] = *user
29+
cfg.Write()
30+
log.Printf("Groups for %s edited: %v\n", email, groups)
31+
user.UpdateGroups(cfg, oldgroups)
32+
}
33+
},
34+
}
35+
36+
func init() {
37+
groupsCmd.AddCommand(groupsUserCmd)
38+
}

cmd/rename.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"github.com/spf13/cobra"
55
)
66

7-
// registerCmd represents the register command
7+
// registerCmd represents the rename command
88
var renameCmd = &cobra.Command{
99
Use: "rename",
1010
Short: "Rename a user (modify email) or server (modify alias)",

0 commit comments

Comments
 (0)