func (con Auth) Handler(c context.Context) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { action := webfw.GetMultiPatternIdentifier(c, r) sess := webfw.GetSession(c, r) var resp responseError switch action { case "auth-data": user := readeef.GetUser(c, r) resp = getAuthData(user, sess, con.capabilities) case "logout": resp = logout(sess) } var b []byte if resp.err == nil { b, resp.err = json.Marshal(resp.val) } if resp.err == nil { w.Write(b) } else { webfw.GetLogger(c).Print(resp.err) w.WriteHeader(http.StatusInternalServerError) return } }) }
func (con Login) Handler(c context.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { l := webfw.GetLogger(c) sess := webfw.GetSession(c, r) renderData := renderer.RenderData{} if r.Method == "GET" { if v, ok := sess.Flash("form-error"); ok { renderData["form-error"] = v } } else { if err := r.ParseForm(); err != nil { l.Fatal(err) } username := data.Login(r.Form.Get("username")) password := r.Form.Get("password") repo := GetRepo(c) conf := GetConfig(c) u := repo.UserByLogin(username) formError := false if u.Err() != nil { sess.SetFlash("form-error", "login-incorrect") formError = true } else if !u.Authenticate(password, []byte(conf.Auth.Secret)) { sess.SetFlash("form-error", "login-incorrect") formError = true } else { sess.Set(AuthUserKey, u) sess.Set(AuthNameKey, username) } if formError { http.Redirect(w, r, r.URL.String(), http.StatusTemporaryRedirect) } else { var returnPath string if v, ok := sess.Flash("return-to"); ok { returnPath = v.(string) } else { returnPath = webfw.GetDispatcher(c).Pattern } http.Redirect(w, r, returnPath, http.StatusTemporaryRedirect) } return } err := webfw.GetRenderCtx(c, r)(w, renderData, "login.tmpl") if err != nil { l.Print(err) } } }
func (con Login) Handler(c context.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { l := webfw.GetLogger(c) sess := webfw.GetSession(c, r) data := renderer.RenderData{} if r.Method == "GET" { if v, ok := sess.Flash("form-error"); ok { data["form-error"] = v } } else { if err := r.ParseForm(); err != nil { l.Fatal(err) } username := r.Form.Get("username") password := r.Form.Get("password") db := GetDB(c) formError := false if u, err := db.GetUser(username); err != nil { sess.SetFlash("form-error", "login-incorrect") formError = true } else if !u.Authenticate(password) { sess.SetFlash("form-error", "login-incorrect") formError = true } else { sess.Set(authkey, u) sess.Set(namekey, u.Login) } if formError { http.Redirect(w, r, r.URL.String(), http.StatusTemporaryRedirect) } else { var returnPath string if v, ok := sess.Flash("return-to"); ok { returnPath = v.(string) } else { returnPath = webfw.GetDispatcher(c).Pattern } http.Redirect(w, r, returnPath, http.StatusTemporaryRedirect) } return } err := webfw.GetRenderCtx(c, r)(w, data, "login.tmpl") if err != nil { l.Print(err) } } }
func (mw Auth) Handler(ph http.Handler, c context.Context, l *log.Logger) http.Handler { handler := func(w http.ResponseWriter, r *http.Request) { for _, prefix := range mw.IgnoreURLPrefix { if prefix[0] == '/' { prefix = prefix[1:] } if strings.HasPrefix(r.URL.Path, mw.Pattern+prefix+"/") { ph.ServeHTTP(w, r) return } } route, _, ok := webfw.GetDispatcher(c).RequestRoute(r) if !ok { ph.ServeHTTP(w, r) return } switch ac := route.Controller.(type) { case AuthController: if !ac.LoginRequired(c, r) { ph.ServeHTTP(w, r) return } sess := webfw.GetSession(c, r) var u User validUser := false if uv, ok := sess.Get(authkey); ok { if u, ok = uv.(User); ok { validUser = true } } if !validUser { if uv, ok := sess.Get(namekey); ok { if n, ok := uv.(string); ok { var err error u, err = mw.DB.GetUser(n) if err == nil { validUser = true sess.Set(authkey, u) } else if _, ok := err.(ValidationError); !ok { l.Print(err) } } } } if validUser && !u.Active { Debug.Println("User " + u.Login + " is inactive") validUser = false } if !validUser { d := webfw.GetDispatcher(c) sess.SetFlash(CtxKey("return-to"), r.URL.Path) path := d.NameToPath("auth-login", webfw.MethodGet) if path == "" { path = "/" } http.Redirect(w, r, path, http.StatusMovedPermanently) return } case ApiAuthController: if !ac.AuthRequired(c, r) { ph.ServeHTTP(w, r) return } var u User var err error auth := r.Header.Get("Authorization") validUser := false if auth != "" { switch { default: if !strings.HasPrefix(auth, "Readeef ") { break } auth = auth[len("Readeef "):] parts := strings.SplitN(auth, ":", 2) login := parts[0] u, err = mw.DB.GetUser(login) if err != nil { l.Printf("Error getting db user '%s': %v\n", login, err) break } var decoded []byte decoded, err = base64.StdEncoding.DecodeString(parts[1]) if err != nil { l.Printf("Error decoding auth header: %v\n", err) break } date := r.Header.Get("X-Date") t, err := time.Parse(http.TimeFormat, date) if err != nil || t.Add(30*time.Second).Before(time.Now()) { break } nonce := r.Header.Get("X-Nonce") if !mw.Nonce.Check(nonce) { break } mw.Nonce.Remove(nonce) buf := util.BufferPool.GetBuffer() defer util.BufferPool.Put(buf) buf.ReadFrom(r.Body) r.Body = ioutil.NopCloser(buf) bodyHash := md5.New() if _, err := bodyHash.Write(buf.Bytes()); err != nil { l.Printf("Error generating the hash for the request body: %v\n", err) break } contentMD5 := base64.StdEncoding.EncodeToString(bodyHash.Sum(nil)) message := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n", r.RequestURI, r.Method, contentMD5, r.Header.Get("Content-Type"), date, nonce) b := make([]byte, base64.StdEncoding.EncodedLen(len(u.MD5API))) base64.StdEncoding.Encode(b, u.MD5API) hm := hmac.New(sha256.New, b) if _, err := hm.Write([]byte(message)); err != nil { l.Printf("Error generating the hashed message: %v\n", err) break } if !hmac.Equal(hm.Sum(nil), decoded) { l.Printf("Error matching the supplied auth message to the generated one.\n") break } if !u.Active { Debug.Println("User " + u.Login + " is inactive") break } validUser = true } } if !validUser { w.WriteHeader(http.StatusForbidden) return } c.Set(r, context.BaseCtxKey("user"), u) } ph.ServeHTTP(w, r) } return http.HandlerFunc(handler) }
func (con WebSocket) Handler(c context.Context) http.Handler { var mutex sync.RWMutex receivers := make(map[chan content.Feed]bool) logger := webfw.GetLogger(c) go func() { for { select { case feed := <-con.updateFeed: logger.Infoln("New articles notification for " + feed.String()) mutex.RLock() for receiver, _ := range receivers { receiver <- feed } mutex.RUnlock() } } }() cfg := readeef.GetConfig(c) return websocket.Handler(func(ws *websocket.Conn) { user := readeef.GetUser(c, ws.Request()) sess := webfw.GetSession(c, ws.Request()) msg := make(chan apiRequest) resp := make(chan apiResponse) receiver := make(chan content.Feed) mutex.Lock() receivers[receiver] = true mutex.Unlock() defer func() { mutex.Lock() close(receiver) delete(receivers, receiver) mutex.Unlock() }() go func() { for { var r responseError select { case data := <-msg: var err error var processor Processor if processor, err = data.processor(c, sess, user, con.fm, con.sp, con.extractor, con.capabilities, []byte(cfg.Auth.Secret)); err == nil { if len(data.Arguments) > 0 { err = json.Unmarshal([]byte(data.Arguments), processor) } if err == nil { r = processor.Process() } } if err != nil { r.err = err switch err.(type) { case *json.UnmarshalTypeError: r.errType = errTypeInvalidArgValue default: if err == errInvalidMethodValue { r.errType = errTypeInvalidMethodValue } else if err == content.ErrNoContent { r.err = errResourceNotFound r.errType = errTypeResourceNotFound } } } go func() { var err string if r.err != nil { err = r.err.Error() } resp <- apiResponse{ Success: r.err == nil, Error: err, ErrorType: r.errType, Method: data.Method, Tag: data.Tag, Arguments: r.val, } }() case f := <-receiver: if f == nil || user == nil { // Socket was closed return } logger.Infoln("Received notification for feed update of " + f.String()) r := newResponse() uf := user.FeedById(f.Data().Id) if !uf.HasErr() { r.val["Feed"] = uf go func() { var err string if r.err != nil { err = r.err.Error() } resp <- apiResponse{ Success: r.err == nil, Error: err, ErrorType: r.errType, Method: "feed-update-notifier", Tag: "", Arguments: r.val, } }() } case r := <-resp: websocket.JSON.Send(ws, r) } } }() for { var data apiRequest if err := websocket.JSON.Receive(ws, &data); err != nil { if err == io.EOF { // Websocket was closed break } else { websocket.JSON.Send(ws, apiResponse{ Success: false, ErrorType: errTypeMessageParse, Error: err.Error(), Method: data.Method, }) } } if forbidden(c, ws.Request()) { websocket.JSON.Send(ws, apiResponse{ Success: false, ErrorType: errTypeUnauthorized, Error: errUnauthorized.Error(), Method: data.Method, }) break } msg <- data } logger.Infoln("Closing web socket") }) }
func (con Proxy) Handler(c context.Context) http.Handler { logger := webfw.GetLogger(c) config := readeef.GetConfig(c) client := readeef.NewTimeoutClient(config.Timeout.Converted.Connect, config.Timeout.Converted.ReadWrite) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sess := webfw.GetSession(c, r) if _, ok := sess.Get(readeef.AuthNameKey); !ok { w.WriteHeader(http.StatusForbidden) return } if r.Method == "HEAD" { return } r.ParseForm() var err error switch { default: var u *url.URL u, err = url.Parse(r.Form.Get("url")) if err != nil { err = fmt.Errorf("Error parsing url to proxy (%s): %v", r.Form.Get("url"), err) break } if u.Scheme == "" { u.Scheme = "http" } var req *http.Request req, err = http.NewRequest("GET", u.String(), nil) if err != nil { err = fmt.Errorf("Error creating proxy request to %s: %v", u, err) break } var resp *http.Response resp, err = client.Do(req) if err != nil { err = fmt.Errorf("Error getting proxy response from %s: %v", u, err) break } defer resp.Body.Close() for k, values := range resp.Header { for _, v := range values { w.Header().Add(k, v) } } var b []byte b, err = ioutil.ReadAll(resp.Body) if err != nil { err = fmt.Errorf("Error reading proxy response from %s: %v", u, err) break } _, err = w.Write(b) } if err != nil { logger.Infoln(err) w.WriteHeader(http.StatusNotAcceptable) return } return }) }
func (imw I18N) Handler(ph http.Handler, c context.Context) http.Handler { for _, l := range imw.Languages { file, err := fs.DefaultFS.OpenRoot(imw.Dir, l+".all.json") if err == nil { var b []byte if b, err = ioutil.ReadAll(file); err == nil { err = i18n.ParseTranslationFileBytes(l+".all.json", b) } } if err != nil { panic(fmt.Sprintf("Error opening locale file '%s.all.json': %v\n", l, err)) } } renderer := webfw.GetRenderer(c) renderer.Funcs(imw.TemplateFuncMap()) handler := func(w http.ResponseWriter, r *http.Request) { c.Set(r, context.BaseCtxKey("langs"), imw.Languages) if len(imw.Languages) == 0 { c.Set(r, context.BaseCtxKey("lang"), "") ph.ServeHTTP(w, r) return } found := false uriParts := strings.SplitN(r.RequestURI, "?", 2) if uriParts[0] == "" { uriParts[0] = r.URL.Path } for _, prefix := range imw.IgnoreURLPrefix { if prefix[0] == '/' { prefix = prefix[1:] } if strings.HasPrefix(uriParts[0], imw.Pattern+prefix+"/") { found = true break } if uriParts[0] == imw.Pattern+prefix { found = true break } } if !found { for _, language := range imw.Languages { if uriParts[0] == imw.Pattern+language { url := uriParts[0] + "/" if len(uriParts) > 1 && uriParts[1] != "" { url += "?" + uriParts[1] } http.Redirect(w, r, url, http.StatusFound) return } if strings.HasPrefix(uriParts[0], imw.Pattern+language+"/") { r.URL.Path = imw.Pattern + r.URL.Path[len(imw.Pattern+language+"/"):] uriParts[0] = imw.Pattern + uriParts[0][len(imw.Pattern+language+"/"):] r.RequestURI = strings.Join(uriParts, "?") c.Set(r, context.BaseCtxKey("lang"), language) found = true s := webfw.GetSession(c, r) s.Set("language", language) break } } } if !found { fallback := webfw.GetFallbackLanguage(c, r, imw.FallbackLanguage) index := strings.Index(fallback, "-") short := fallback if index > -1 { short = fallback[:index] } foundShort := false for _, language := range imw.Languages { if language == fallback { found = true break } if language == short { foundShort = true } } var language string if found { language = fallback } else if foundShort { language = short } else { language = imw.FallbackLanguage } url := imw.Pattern + language + uriParts[0][len(imw.Pattern)-1:] if len(uriParts) > 1 && uriParts[1] != "" { url += "?" + uriParts[1] } http.Redirect(w, r, url, http.StatusFound) return } ph.ServeHTTP(w, r) } return http.HandlerFunc(handler) }
func (mw Auth) Handler(ph http.Handler, c context.Context) http.Handler { logger := webfw.GetLogger(c) handler := func(w http.ResponseWriter, r *http.Request) { for _, prefix := range mw.IgnoreURLPrefix { if prefix[0] == '/' { prefix = prefix[1:] } if strings.HasPrefix(r.URL.Path, mw.Pattern+prefix+"/") { ph.ServeHTTP(w, r) return } } route, _, ok := webfw.GetDispatcher(c).RequestRoute(r) if !ok { ph.ServeHTTP(w, r) return } repo := GetRepo(c) switch ac := route.Controller.(type) { case AuthController: if !ac.LoginRequired(c, r) { ph.ServeHTTP(w, r) return } sess := webfw.GetSession(c, r) var u content.User validUser := false if uv, ok := sess.Get(AuthUserKey); ok { if u, ok = uv.(content.User); ok { validUser = true } } if !validUser { if uv, ok := sess.Get(AuthNameKey); ok { if n, ok := uv.(data.Login); ok { u = repo.UserByLogin(n) if u.HasErr() { logger.Print(u.Err()) } else { validUser = true sess.Set(AuthUserKey, u) } } } } if validUser && !u.Data().Active { logger.Infoln("User " + u.Data().Login + " is inactive") validUser = false } if !validUser { d := webfw.GetDispatcher(c) sess.SetFlash(CtxKey("return-to"), r.URL.Path) path := d.NameToPath("auth-login", webfw.MethodGet) if path == "" { path = "/" } http.Redirect(w, r, path, http.StatusMovedPermanently) return } case ApiAuthController: if !ac.AuthRequired(c, r) { ph.ServeHTTP(w, r) return } url, login, signature, nonce, date, t := authData(r) validUser := false var u content.User if login != "" && signature != "" && !t.IsZero() { switch { default: u = repo.UserByLogin(data.Login(login)) if u.HasErr() { logger.Printf("Error getting db user '%s': %v\n", login, u.Err()) break } decoded, err := base64.StdEncoding.DecodeString(signature) if err != nil { logger.Printf("Error decoding auth header: %v\n", err) break } if t.Add(30 * time.Second).Before(time.Now()) { break } if !mw.Nonce.Check(nonce) { break } mw.Nonce.Remove(nonce) buf := util.BufferPool.GetBuffer() defer util.BufferPool.Put(buf) buf.ReadFrom(r.Body) r.Body = ioutil.NopCloser(buf) bodyHash := md5.New() if _, err := bodyHash.Write(buf.Bytes()); err != nil { logger.Printf("Error generating the hash for the request body: %v\n", err) break } contentMD5 := base64.StdEncoding.EncodeToString(bodyHash.Sum(nil)) message := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n", url, r.Method, contentMD5, r.Header.Get("Content-Type"), date, nonce) b := make([]byte, base64.StdEncoding.EncodedLen(len(u.Data().MD5API))) base64.StdEncoding.Encode(b, u.Data().MD5API) hm := hmac.New(sha256.New, b) if _, err := hm.Write([]byte(message)); err != nil { logger.Printf("Error generating the hashed message: %v\n", err) break } if !hmac.Equal(hm.Sum(nil), decoded) { logger.Printf("Error matching the supplied auth message to the generated one.\n") break } if !u.Data().Active { logger.Println("User " + u.Data().Login + " is inactive") break } validUser = true } } if validUser { c.Set(r, context.BaseCtxKey("user"), u) } else { if rej, ok := ac.(AuthRejectHandler); ok { rej.AuthReject(c, r) } else { w.WriteHeader(http.StatusUnauthorized) return } } } ph.ServeHTTP(w, r) } return http.HandlerFunc(handler) }