From 09af83576a877eda40512bfba30b47a1b13061fb Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Wed, 13 Nov 2024 20:47:40 +0100 Subject: Initial (public) commit --- icalproxy.go | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 icalproxy.go (limited to 'icalproxy.go') diff --git a/icalproxy.go b/icalproxy.go new file mode 100644 index 0000000..cf09e5d --- /dev/null +++ b/icalproxy.go @@ -0,0 +1,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) +} -- cgit v1.2.3