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
|
package main
import (
"bytes"
"log"
"net/http"
"net/url"
"time"
"github.com/emersion/go-ical"
"golang.org/x/crypto/argon2"
)
func main() {
cfg := loadConfig()
printConfig(&cfg)
handler := handler{ignore: cfg.Ignore, tokens: cfg.UserTokens}
mux := http.ServeMux{}
mux.HandleFunc("/", handler.handle)
server := http.Server{
Addr: ":" + cfg.Port,
Handler: &mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 30 * time.Second,
ReadHeaderTimeout: 2 * time.Second,
}
log.Println("Listening on", cfg.Port)
log.Fatal(server.ListenAndServe())
}
type handler struct {
ignore ignoreRules
tokens map[string]userToken
calURL url.URL
}
func (h handler) makeTokenURL(token string) string {
newURL := h.calURL
query := newURL.Query()
query.Set("token", token)
newURL.RawQuery = query.Encode()
return newURL.String()
}
func (h handler) handle(w http.ResponseWriter, r *http.Request) {
brightspaceToken := r.URL.Query().Get("brightspace_token")
proxyUserID := r.URL.Query().Get("proxy_user_id")
proxyToken := r.URL.Query().Get("proxy_token")
if !userOK(h.tokens, proxyUserID, []byte(proxyToken)) {
http.Error(w, "Bad proxy_user_id or proxy_token", http.StatusUnauthorized)
return
}
resp, err := http.Get(h.makeTokenURL(brightspaceToken))
if err != nil {
http.Error(w, "Error sending request to Brightspace", http.StatusInternalServerError)
return
}
cal, err := ical.NewDecoder(resp.Body).Decode()
if err != nil {
http.Error(w, "Could not decode iCal", http.StatusInternalServerError)
return
}
filter(h.ignore, cal.Component)
if err = ical.NewEncoder(w).Encode(cal); err != nil {
log.Println("Error writing calendar:", err)
}
}
func filter(ignore ignoreRules, c *ical.Component) {
j := 0
for _, child := range c.Children {
keep := true
if child.Name == ical.CompEvent {
if location := child.Props.Get(ical.PropLocation); location != nil {
for _, locationRule := range ignore.LocationRegexes {
if locationRule.MatchString(location.Value) {
keep = false
}
}
}
if summary := child.Props.Get(ical.PropSummary); summary != nil {
for _, summaryRule := range ignore.SummaryRegexes {
if summaryRule.MatchString(summary.Value) {
keep = false
}
}
}
}
if keep {
c.Children[j] = child
j++
}
}
c.Children = c.Children[:j]
}
func userOK(tokens map[string]userToken, id string, token []byte) bool {
info, ok := tokens[id]
if !ok {
return false
}
return bytes.Compare(hash(token, info.Salt), info.Hash) == 0
}
func hash(token []byte, salt []byte) []byte {
return argon2.IDKey(token, salt, 1, 64*1024, 4, 32)
}
|