forked from upspin/upspin
/
user.go
160 lines (148 loc) · 4.89 KB
/
user.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"flag"
"fmt"
yaml "gopkg.in/yaml.v2"
"github.com/palager/upspin/factotum"
"github.com/palager/upspin/key/usercache"
"github.com/palager/upspin/upspin"
"github.com/palager/upspin/user"
)
func (s *State) user(args ...string) {
const help = `
User prints in YAML format the user record stored in the key server
for the specified user, by default the current user.
With the -put flag, user writes or replaces the information stored
for the current user, such as to update keys or server information.
The information is read from standard input or from the file provided
with the -in flag. The input must provide the complete record for
the user, and must be in the same YAML format printed by the command
without the -put flag.
When using -put, the command takes no arguments. The name of the
user whose record is to be updated must be provided in the input
record and must either be the current user or the name of another
user whose domain is administered by the current user.
A handy way to use the command is to edit the config file and run
upspin user | upspin user -put
To install new users see the signup command.
`
fs := flag.NewFlagSet("user", flag.ExitOnError)
put := fs.Bool("put", false, "write new user record")
inFile := fs.String("in", "", "input file (default standard input)")
force := fs.Bool("force", false, "force writing user record even if key is empty")
s.ParseFlags(fs, args, help, "user [username...]\n user -put [-in=inputfile] [-force] [username]")
keyServer := s.KeyServer()
if *put {
s.putUser(fs, keyServer, s.GlobOneLocal(*inFile), *force)
return
}
if *inFile != "" {
s.Exitf("-in only available with -put")
}
if *force {
s.Exitf("-force only available with -put")
}
var userNames []upspin.UserName
if fs.NArg() == 0 {
userNames = append(userNames, s.Config.UserName())
} else {
for i := 0; i < fs.NArg(); i++ {
userName, err := user.Clean(upspin.UserName(fs.Arg(i)))
if err != nil {
s.Exit(err)
}
userNames = append(userNames, userName)
}
}
for _, name := range userNames {
u, err := keyServer.Lookup(name)
if err != nil {
s.Exit(err)
}
blob, err := yaml.Marshal(u)
if err != nil {
// TODO(adg): better error message?
s.Exit(err)
}
s.Printf("%s\n", blob)
if name != s.Config.UserName() {
continue
}
// When it's the user asking about herself, the result comes
// from the configuration and may disagree with the value in the
// key store. This is a common source of error so we want to
// diagnose it. To do that, we wipe the key cache and go again.
// This will wipe the memory of our remembered configuration and
// reload it from the key server.
usercache.ResetGlobal()
keyU, err := keyServer.Lookup(name)
if err != nil {
s.Exit(err)
}
var buf bytes.Buffer
if keyU.Name != u.Name {
fmt.Fprintf(&buf, "user name in configuration: %s\n", u.Name)
fmt.Fprintf(&buf, "user name in key server: %s\n", keyU.Name)
}
if keyU.PublicKey != u.PublicKey {
fmt.Fprintf(&buf, "public key in configuration does not match key server\n")
}
// There must be dir servers defined in both and we expect agreement.
if !equalEndpoints(keyU.Dirs, u.Dirs) {
fmt.Fprintf(&buf, "dirs in configuration: %s\n", u.Dirs)
fmt.Fprintf(&buf, "dirs in key server: %s\n", keyU.Dirs)
}
// Remote stores need not be defined (yet).
if len(keyU.Stores) > 0 && !equalEndpoints(keyU.Stores, u.Stores) {
fmt.Fprintf(&buf, "stores in configuration: %s\n", u.Stores)
fmt.Fprintf(&buf, "stores in key server: %s\n", keyU.Stores)
}
if buf.Len() > 0 {
s.Exitf("local configuration differs from public record in key server:\n%s", &buf)
}
}
}
func equalEndpoints(a, b []upspin.Endpoint) bool {
if len(a) != len(b) {
return false
}
for i, e := range a {
if e != b[i] {
return false
}
}
return true
}
func (s *State) putUser(fs *flag.FlagSet, keyServer upspin.KeyServer, inFile string, force bool) {
data := s.ReadAll(inFile)
userStruct := new(upspin.User)
err := yaml.Unmarshal(data, userStruct)
if err != nil {
// TODO(adg): better error message?
s.Exit(err)
}
if fs.NArg() != 0 && upspin.UserName(fs.Arg(0)) != userStruct.Name {
s.Exitf("User name provided does not match the one read from the input file.")
}
// Validate public key.
if userStruct.PublicKey == "" && !force {
s.Exitf("An empty public key will prevent user from accessing services. To override use -force.")
}
_, err = factotum.ParsePublicKey(userStruct.PublicKey)
if err != nil && !force {
s.Exitf("invalid public key, to override use -force: %s", err.Error())
}
// Clean the username.
userStruct.Name, err = user.Clean(userStruct.Name)
if err != nil {
s.Exit(err)
}
err = keyServer.Put(userStruct)
if err != nil {
s.Exit(err)
}
}