Github user amiryesh commented on a diff in the pull request:

    
https://github.com/apache/incubator-trafficcontrol/pull/551#discussion_r115421632
  
    --- Diff: traffic_ops/experimental/webfront/webfront.go ---
    @@ -122,188 +145,311 @@ func main() {
        Logger.Fatal(http.ListenAndServeTLS(":" + 
strconv.Itoa(int(config.ListenPort)), "server.pem", "server.key", s))
     }
     
    -func validateToken(tokenString string) (*jwt.Token, error) {
    -
    -   tokenString = strings.Replace(tokenString, "Bearer ", "", 1)
    -   token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token 
*jwt.Token) (interface{}, error) {
    -           if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
    -                   return nil, fmt.Errorf("Unexpected signing method: %v", 
token.Header["alg"])
    -           }
    -           return []byte(os.Args[2]), nil
    -   })
    -   return token, err
    -}
    -
    -// NewServer constructs a Server that reads rules from file with a period
    -// specified by poll.
    +// NewServer constructs a Server that reads Rules from file with a period 
    +// specified by poll
     func NewServer(file string, poll time.Duration) (*Server, error) {
        s := new(Server)
        if err := s.loadRules(file); err != nil {
    -           Logger.Fatal("Error loading rules file: ", err)
    +           Logger.Fatal(fmt.Errorf("Load rules failed: %s", err))
        }
        go s.refreshRules(file, poll)
        return s, nil
     }
     
    -// ServeHTTP matches the Request with a Rule and, if found, serves the
    -// request with the Rule's handler. If the rule's secure field is true, it 
will
    -// only allow access if the request has a valid JWT bearer token.
    -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    +// loadRules tests whether file has been modified since its last invocation
    +// and, if so, loads the rule set from file.
    +func (s *Server) loadRules(file string) error {
     
    -   rule := s.getRule(r)
    -   if rule == nil {
    -           Logger.Printf("%v %v No mapping in rules file!", r.Method, 
r.URL.RequestURI())
    -           http.Error(w, "Not found", http.StatusNotFound)
    -           return
    +   fi, err := os.Stat(file)
    +   if err != nil {
    +           return err
        }
     
    -   isAuthorized := false
    +   mtime := fi.ModTime()
    +   if !mtime.After(s.last) && s.Rules != nil {
    +           return nil // no change
    +   }
     
    -   if rule.Secure {
    -           tokenValid := false
    -           token, err := validateToken(r.Header.Get("Authorization"))
    +   Rules, err := parseRules(file)
    +   if err != nil {
    +           return err
    +   }
     
    -           if err == nil {
    -                   tokenValid = true
    -           } else {
    -                   Logger.Println("Token Error:", err.Error())
    +   s.mu.Lock()
    +   s.last = mtime
    +   s.Rules = Rules
    +   s.mu.Unlock()
    +   return nil
    +}
    +
    +// refreshRules polls file periodically and refreshes the Server's rule set
    +// if the file has been modified.
    +func (s *Server) refreshRules(file string, poll time.Duration) {
    +   for {
    +           if err := s.loadRules(file); err != nil {
    +                   Logger.Printf("Refresh rules failed: %s", err)
                }
    +           time.Sleep(poll)
    +   }
    +}
     
    -           if !tokenValid {
    -                   Logger.Printf("%v %v Valid token required, but none 
found!", r.Method, r.URL.RequestURI())
    -                   w.WriteHeader(http.StatusForbidden)
    -                   return
    +// parseRules reads rule definitions from file, constructs the rule 
handlers,
    +// and returns the resultant rules.
    +func parseRules(file string) ([]*FwdRule, error) {
    +
    +   f, err := os.Open(file)
    +   if err != nil {
    +           return nil, err
    +   }
    +   defer f.Close()
    +
    +   Logger.Printf("Loading rules file: %s", file)
    +
    +   var rules []*FwdRule
    +   if err := json.NewDecoder(f).Decode(&rules); err != nil {
    +           return nil, err
    +   }
    +
    +   for _, r := range rules {
    +
    +           if r.Auth {
    +                   r.routes, err = parseRoutes(r.RoutesFile)
    +                   if err != nil {
    +                           Logger.Printf("Skip rule %s ERROR: %s", r.Path, 
err)
    +                           continue
    +                   }                       
                }
     
    -           claims, ok := token.Claims.(*Claims)
    -           if !ok {
    -                   Logger.Printf("%v %v Valid token found, but cannot 
parse claims!", r.Method, r.URL.RequestURI())
    -                   w.WriteHeader(http.StatusForbidden)
    -                   return
    +           r.handler, err = makeHandler(r)
    +           if err != nil {
    +                   Logger.Printf("Skip rule %s ERROR: %s", r.Path, err)
    +                   continue
                }
     
    -           // Authorization: Check is the list of capabilities in the 
token's claims contains 
    -           // the reqired capability that is listed in the rule
    -           for _, c := range claims.Capabilities {
    -           if c == rule.Capabilities[r.Method] {
    -                           isAuthorized = true
    -                           break
    -                   }
    -        }
    +           // Logger.Printf("Loaded rule: %s", r.Path)
    +   }
    +
    +   return rules, nil
    +}
    +
    +// parseRoutes reads route definitions from file, constructs the route 
auth handler,
    +// and returns the resultant routes.
    +func parseRoutes(file string) ([]*Route, error) {
    +
    +   // If the rule defines a routes file, we load the routes and enforce 
access.
    +   // Routes than are not present in this file are forbidden.
     
    -           Logger.Printf("%v %v Valid token. Subject=%v, ExpiresAt=%v, 
Capabilities=%v, Required=%v, Authorized=%v", 
    -                   r.Method, r.URL.RequestURI(), claims.Subject, 
claims.ExpiresAt, claims.Capabilities, 
    -                   rule.Capabilities[r.Method], isAuthorized)
    +   // Note that there is currently no mechanism to trigger an update on a 
change in the route files. 
    +   // To trigger an update, one needs to touch rules.json 
     
    -   } else {
    -           isAuthorized = true
    +   cf, err := os.Open(file)
    +   if err != nil {
    +           return nil, err
    +   }
    +   defer cf.Close()
    +
    +   Logger.Printf("Loading routes file: %s", file)
    +
    +   var routes []*Route
    +   if err := json.NewDecoder(cf).Decode(&routes); err != nil {
    +           return nil, err
        }
     
    -   if isAuthorized {
    -           if h := rule.handler; h != nil {
    -                   h.ServeHTTP(w, r)
    +   for _, r := range routes {
    +
    +           /*
    +           // If the match ends with a slash, it is treated as a prefix. 
    +           // If not, it is an exact match
    +           if !strings.EndsWith(r.Match, "/") {
    +                   r.Match = r.Match + '$'
    +           }
    +           */
    +
    +           r.matchRegexp, err = regexp.Compile(r.Match + "$")
    +           if err != nil {
    +                   Logger.Printf("Skip route %s ERROR: %s", r.Match, err)
    +                   continue
    +           }
    +
    +           // Logger.Printf("Loaded route: %s", r.Match)
    +   }
    +
    +   return routes, nil
    +}
    +
    +// makeHandler constructs the appropriate Handler for the given FwdRule.
    +func makeHandler(r *FwdRule) (http.Handler, error) {
    +
    +   if h := r.Forward; h == "" {
    +           return nil, fmt.Errorf("Not a forward rule")
    +   }
    +
    +   return &httputil.ReverseProxy {
    +           Director: func(req *http.Request) {
    +                   req.URL.Scheme = r.Scheme
    +                   req.URL.Host = r.Forward
    +                   // req.URL.Path = "/boo1" // TODO JvD - regex to change 
path here
    +           },
    +   }, nil
    +}
    +
    
+/////////////////////////////////////////////////////////////////////////////////////////////////////
    +
    +// ServeHTTP matches the Request with a forward rule and, if found, serves 
the
    +// request with therule's handler. If the rule's secure field is true, it 
will
    +// only allow access if the request has a valid JWT bearer token.
    +func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    +
    +   rule := s.matchRule(req)
    +   if rule == nil {
    +           http.Error(w, "Not found", http.StatusNotFound)
    +           return
    +   }
    +
    +   if rule.Auth {
    +           authorized := rule.authorize(w, req)
    +           if !authorized {
                        return
                }
        }
     
    +   if h := rule.handler; h != nil {
    +           h.ServeHTTP(w, req)
    +           return
    +   }
    +
        http.Error(w, "Not Authorized", http.StatusUnauthorized)
        return
     }
     
    -func rejectNoToken(handler http.Handler) http.HandlerFunc {
    -   return func(w http.ResponseWriter, r *http.Request) {
    -           w.WriteHeader(http.StatusForbidden)
    +func (rule *FwdRule) authorize(w http.ResponseWriter, req *http.Request) 
bool {
    --- End diff --
    
    By design, authorization does not check the database to avoid congestion. 
We use short lived access tokens.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastruct...@apache.org or file a JIRA ticket
with INFRA.
---

Reply via email to