github.com/decred/politeia@v1.4.0/politeiawww/server.go (about)

     1  // Copyright (c) 2021-2022 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	v3 "github.com/decred/politeia/politeiawww/api/http/v3"
    14  	"github.com/decred/politeia/util"
    15  	"github.com/gorilla/csrf"
    16  	"github.com/gorilla/mux"
    17  )
    18  
    19  const (
    20  	csrfKeyLength    = 32    // In bytes
    21  	csrfCookieMaxAge = 86400 // 1 day in seconds
    22  )
    23  
    24  // setupRouter sets up the router for the politeiawww http API.
    25  func (p *politeiawww) setupRouter() error {
    26  	// Setup the router
    27  	p.router = mux.NewRouter()
    28  	p.router.StrictSlash(true) // Ignore trailing slashes
    29  
    30  	// Add a 404 handler
    31  	p.router.NotFoundHandler = http.HandlerFunc(p.handleNotFound)
    32  
    33  	// Add router middleware. Middleware is executed
    34  	// in the same order that they are registered in.
    35  	m := middleware{
    36  		reqBodySizeLimit: p.cfg.ReqBodySizeLimit,
    37  	}
    38  	p.router.Use(closeBodyMiddleware) // MUST be registered first
    39  	p.router.Use(m.reqBodySizeLimitMiddleware)
    40  	p.router.Use(loggingMiddleware)
    41  	p.router.Use(recoverMiddleware)
    42  
    43  	// Setup a subrouter that is CSRF protected. Authenticated routes are
    44  	// required to use the protected router. The subrouter takes on the
    45  	// configuration of the router that it was spawned from, including all
    46  	// of the middleware that has already been registered.
    47  	p.protected = p.router.NewRoute().Subrouter()
    48  
    49  	// The CSRF middleware uses the double submit cookie method. The server
    50  	// provides clients with two CSRF tokens: a cookie token and a header
    51  	// token. The cookie token is set automatically by the CSRF protected
    52  	// subrouter anytime one of the protected routes it hit. The header token
    53  	// must be set manually by a request handler. Clients MUST provide both
    54  	// tokens in their request if they want to access a CSRF protected route.
    55  	// The CSRF protected subrouter returns a 403 HTTP status code if a client
    56  	// attempts to access a protected route without providing the proper CSRF
    57  	// tokens.
    58  	csrfKey, err := p.loadCSRFKey()
    59  	if err != nil {
    60  		return err
    61  	}
    62  	csrfMiddleware := csrf.Protect(
    63  		csrfKey,
    64  		// Set the CSRF cookie on all auth router paths and subpaths.
    65  		csrf.Path("/"),
    66  		csrf.MaxAge(csrfCookieMaxAge),
    67  	)
    68  	p.protected.Use(csrfMiddleware)
    69  
    70  	return nil
    71  }
    72  
    73  // setupPluginRoutes set ups the http API routes for the plugin API.
    74  func (p *politeiawww) setupPluginRoutes() {
    75  	// NOTE: This will override the legacy version route.
    76  	// Disable it until we are ready to switch over.
    77  	// addRoute(p.protected, http.MethodGet, "", "/", p.handleVersion)
    78  
    79  	// The version routes set the CSRF header token and thus needs
    80  	// to be part of the CSRF protected auth router so that the
    81  	// cookie CSRF is set too. The CSRF cookie is set on all auth
    82  	// routes. The header token is only set on the version route.
    83  	addRoute(p.protected, http.MethodGet, v3.APIVersionPrefix,
    84  		v3.VersionRoute, p.handleVersion)
    85  
    86  	// Unprotected routes
    87  	addRoute(p.router, http.MethodGet, v3.APIVersionPrefix,
    88  		v3.PolicyRoute, p.handlePolicy)
    89  	addRoute(p.router, http.MethodPost, v3.APIVersionPrefix,
    90  		v3.ReadRoute, p.handleRead)
    91  	addRoute(p.router, http.MethodPost, v3.APIVersionPrefix,
    92  		v3.ReadBatchRoute, p.handleReadBatch)
    93  
    94  	// CSRF protected routes
    95  	addRoute(p.protected, http.MethodPost, v3.APIVersionPrefix,
    96  		v3.NewUserRoute, p.handleNewUser)
    97  	addRoute(p.protected, http.MethodPost, v3.APIVersionPrefix,
    98  		v3.WriteRoute, p.handleWrite)
    99  }
   100  
   101  // addRoute adds a route to the provided router.
   102  func addRoute(router *mux.Router, method string, routePrefix, route string, handler http.HandlerFunc) {
   103  	router.HandleFunc(routePrefix+route, handler).Methods(method)
   104  }
   105  
   106  // loadCSRFKey loads the CSRF key from disk. If a CSRF key does not exist then
   107  // one is created and saved to disk for future use.
   108  func (p *politeiawww) loadCSRFKey() ([]byte, error) {
   109  	log.Infof("Load CSRF key")
   110  
   111  	// Open the CSRF key file
   112  	fp := filepath.Join(p.cfg.DataDir, "csrf.key")
   113  	fCSRF, err := os.Open(fp)
   114  	switch {
   115  	case err == nil:
   116  		// CSRF key exists; continue
   117  
   118  	case os.IsNotExist(err):
   119  		// CSRF key does not exist. Create one
   120  		// and save it to disk.
   121  		key, err := util.Random(csrfKeyLength)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  
   126  		fCSRF, err = os.OpenFile(fp, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
   127  		if err != nil {
   128  			return nil, err
   129  		}
   130  		_, err = fCSRF.Write(key)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  		_, err = fCSRF.Seek(0, 0)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  
   139  		log.Infof("CSRF key created and saved to %v", fp)
   140  
   141  	default:
   142  		// All other errors
   143  		return nil, err
   144  	}
   145  
   146  	// Read the CSRF key from the file
   147  	csrfKey := make([]byte, csrfKeyLength)
   148  	r, err := fCSRF.Read(csrfKey)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	fCSRF.Close()
   153  
   154  	if r != csrfKeyLength {
   155  		return nil, fmt.Errorf("CSRF key corrupt")
   156  	}
   157  
   158  	return csrfKey, nil
   159  }