-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathkeybase.go
141 lines (116 loc) · 3.71 KB
/
keybase.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
/*
pipethis: Stop piping the internet into your shell
Copyright 2016 Ellotheth
Use of this source code is governed by the GNU Public License version 2
(GPLv2). You should have received a copy of the GPLv2 along with your copy of
the source. If not, see http://www.gnu.org/licenses/gpl-2.0.html.
*/
package lookup
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"regexp"
"golang.org/x/crypto/openpgp"
)
type keybaseResponse struct {
Status struct {
Code int `json:"code"`
Name string `json:"name"`
} `json:"status"`
Users []struct {
Details struct {
Username keybaseUserVal `json:"username"`
Fingerprint keybaseUserVal `json:"key_fingerprint"`
FullName keybaseUserVal `json:"full_name"`
Twitter keybaseUserVal `json:"twitter"`
GitHub keybaseUserVal `json:"github"`
HackerNews keybaseUserVal `json:"hackernews"`
Reddit keybaseUserVal `json:"reddit"`
Sites []keybaseUserVal `json:"websites"`
} `json:"components"`
} `json:"completions"`
}
type keybaseUserVal struct {
Value string `json:"val"`
}
// KeybaseService implements the KeyService interface for https://keybase.io
type KeybaseService struct{}
func (k KeybaseService) lookup(query string) ([]byte, error) {
if matches, _ := regexp.MatchString(`^[a-zA-Z0-9_\-\.]+$`, query); !matches {
return nil, errors.New("Invalid user requested")
}
resp, err := http.Get("https://keybase.io/_/api/1.0/user/autocomplete.json?q=" + query)
if err != nil {
return nil, err
}
defer resp.Body.Close()
results, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return results, nil
}
func (k KeybaseService) parse(body []byte) (*keybaseResponse, error) {
lookup := &keybaseResponse{}
if err := json.Unmarshal(body, lookup); err != nil {
return nil, err
}
if lookup.Status.Code != 0 {
return nil, errors.New("Bad status code: " + lookup.Status.Name)
}
return lookup, nil
}
// Matches finds all the Keybase users that match query in any of their details
// (username, Twitter identity, Github identity, public key fingerprint,
// etc.). At most 10 matches will be found. If no matches are found, Matches
// returns an error.
func (k KeybaseService) Matches(query string) ([]User, error) {
results, err := k.lookup(query)
if err != nil {
return nil, err
}
lookup, err := k.parse(results)
if err != nil {
return nil, err
}
matches := []User{}
for _, match := range lookup.Users {
user := User{
Username: match.Details.Username.Value,
Fingerprint: match.Details.Fingerprint.Value,
GitHub: match.Details.GitHub.Value,
Twitter: match.Details.Twitter.Value,
HackerNews: match.Details.HackerNews.Value,
Reddit: match.Details.Reddit.Value,
}
for _, site := range match.Details.Sites {
user.Sites = append(user.Sites, site.Value)
}
matches = append(matches, user)
}
return matches, nil
}
// Key finds the PGP public key for one Keybase user by Keybase username and
// returns the key ring representation of the key. If the Keybase username is
// invalid, or the key itself is missing or invalid, Key returns an error.
func (k KeybaseService) Key(user User) (openpgp.EntityList, error) {
// I think I set this up to match Keybase's own username pattern. I think.
if matches, _ := regexp.MatchString(`^[a-zA-Z0-9_\-\.]+$`, user.Username); !matches {
return nil, errors.New("Invalid user requested")
}
resp, err := http.Get("https://keybase.io/" + user.Username + "/key.asc")
defer resp.Body.Close()
if err != nil {
return nil, err
}
ring, err := openpgp.ReadArmoredKeyRing(resp.Body)
if err != nil {
return nil, err
}
if len(ring) != 1 {
return nil, errors.New("More than one key returned, not sure what to do")
}
return ring, nil
}