this seems okay

This commit is contained in:
Bel LaPointe
2019-02-18 16:12:47 -07:00
parent cbf886fb7e
commit 1d854cfff5
17 changed files with 632 additions and 710 deletions

View File

@@ -1,30 +1,14 @@
package server
import (
"local/s2sa/s2sa/server/router"
"local/s2sa/s2sa/services"
"local/s2sa/s2sa/storage"
"local/rproxy3/config"
"local/rproxy3/storage"
)
func New(path string) *Server {
var db storage.DB
db = storage.NewMap()
if len(path) > 0 {
var err error
db, err = storage.NewBolt(path)
if err != nil {
return nil
}
func New() *Server {
port := config.GetPort()
return &Server{
db: storage.NewMap(),
addr: port,
}
authdb := storage.NewMap()
s := &Server{
db: services.New(db),
authdb: services.New(authdb),
router: router.New(),
addr: ":18341",
}
if err := s.authdb.Register(serverNS); err != nil {
panic(err)
}
return s
}

View File

@@ -1,11 +1,9 @@
package server
import (
"os"
"testing"
)
func TestServerNew(t *testing.T) {
New("")
New(os.DevNull)
New()
}

29
server/proxy.go Normal file
View File

@@ -0,0 +1,29 @@
package server
import (
"local/rproxy3/storage/packable"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
func (s *Server) Proxy(w http.ResponseWriter, r *http.Request) {
newURL, err := s.lookup(r.Host)
if err != nil {
http.NotFound(w, r)
log.Printf("unknown host lookup %q", r.Host)
return
}
r.Host = newURL.Host
proxy := httputil.NewSingleHostReverseProxy(newURL)
proxy.ServeHTTP(w, r)
}
func (s *Server) lookup(host string) (*url.URL, error) {
host = strings.Split(host, ".")[0]
v := packable.NewURL()
err := s.db.Get(nsRouting, host, v)
return v.URL(), err
}

View File

@@ -1,197 +1,15 @@
package server
import (
"encoding/json"
"local/s2sa/s2sa/server/router"
"local/s2sa/s2sa/token"
"net/http"
"path"
"strings"
"local/rproxy3/config"
)
const clientsNS = "clients"
const accessorsNS = "accessors"
const wildcard = "{}"
const serverNS = "server"
func (s *Server) Routes() error {
appendWildcards := func(s string, cnt int) string {
s = strings.Trim(s, "/")
return path.Join(s, strings.Repeat("/"+wildcard, cnt))
}
paths := []struct {
base string
wildcards int
method http.HandlerFunc
}{
{
base: "admin/register",
wildcards: 1,
method: s.adminRegister,
},
{
base: "register",
wildcards: 1,
method: s.authenticate(s.registerClient),
},
{
base: "generate",
wildcards: 2,
method: s.authenticate(s.generateToken),
},
{
base: "retrieve",
wildcards: 3,
method: s.authenticate(s.retrieveToken),
},
{
base: "revoke",
wildcards: 2,
method: s.authenticate(s.revokeToken),
},
{
base: "lookup",
wildcards: 2,
method: s.authenticate(s.lookupToken),
},
{
base: "policies",
wildcards: 0,
method: s.authenticate(s.getPolicies),
},
}
for _, path := range paths {
if err := s.Add(appendWildcards(path.base, path.wildcards), path.method); err != nil {
routes := config.GetRoutes()
for k, v := range routes {
if err := s.Route(k, v); err != nil {
return err
}
}
return nil
}
func (s *Server) authenticate(foo http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
recipient, accessorToken, ok := r.BasicAuth()
if !ok {
w.WriteHeader(http.StatusUnauthorized)
return
}
accessor := strings.Split(accessorToken, ":")[0]
tokenValue := strings.Split(accessorToken, ":")[1]
token, err := s.authdb.Get(recipient, accessor)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
if token.Token != tokenValue {
w.WriteHeader(http.StatusUnauthorized)
return
}
if token.To != recipient {
w.WriteHeader(http.StatusUnauthorized)
return
}
foo(w, r)
}
}
func (s *Server) adminRegister(w http.ResponseWriter, r *http.Request) {
var name string
if err := router.Params(r, &name); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
token, err := s.authdb.New(serverNS, name)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
respondWithToken(token, w, r)
}
func (s *Server) registerClient(w http.ResponseWriter, r *http.Request) {
var name string
if err := router.Params(r, &name); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if err := s.db.Register(name); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}
func (s *Server) generateToken(w http.ResponseWriter, r *http.Request) {
var name, to string
if err := router.Params(r, &name, &to); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
token, err := s.db.New(name, to)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
respondWithToken(token, w, r)
}
func (s *Server) retrieveToken(w http.ResponseWriter, r *http.Request) {
var creator, recipient, accessor string
if err := router.Params(r, &creator, &recipient, &accessor); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
token, err := s.db.Get(recipient, accessor)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
respondWithToken(token, w, r)
}
func respondWithToken(token token.Basic, w http.ResponseWriter, r *http.Request) {
if err := json.NewEncoder(w).Encode(token); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}
func (s *Server) revokeToken(w http.ResponseWriter, r *http.Request) {
var name, accessor string
if err := router.Params(r, &name, &accessor); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if err := s.db.Revoke(name, accessor); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func (s *Server) lookupToken(w http.ResponseWriter, r *http.Request) {
var creator, recipient string
if err := router.Params(r, &creator, &recipient); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
token, err := s.db.Lookup(creator, recipient)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
respondWithToken(token, w, r)
}
func (s *Server) getPolicies(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}

View File

@@ -1,75 +1,15 @@
package server
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
type badRouter struct {
accept []string
}
func (br *badRouter) Add(p string, h http.HandlerFunc) error {
for i := range br.accept {
if br.accept[i] == p {
return nil
}
}
return errors.New("rejected path")
}
func (br *badRouter) ServeHTTP(http.ResponseWriter, *http.Request) {}
func TestServerRoutesBadRouter(t *testing.T) {
server, _, _ := mockServer()
br := badRouter{
accept: make([]string, 0),
}
server.router = &br
toAdd := []string{
"/nil",
"/admin/register/{}",
"/register/{}",
"/generate/{}/{}",
"/retrieve/{}/{}",
"/revoke/{}/{}",
"/lookup/{}/{}",
"/policies",
}
for _, path := range toAdd {
br.accept = append(br.accept, path)
if err := server.Routes(); err == nil {
t.Errorf("can add non-allowed routes")
}
}
}
func TestServerRoutes(t *testing.T) {
server, _, _ := mockServer()
if err := server.db.Register("a"); err != nil {
t.Fatalf("cannot register: %v", err)
}
token, err := server.db.New("a", "b")
if err != nil {
t.Fatalf("cannot new: %v", err)
}
server := mockServer()
paths := []string{
"retrieve/a/b/" + token.Accessor,
"revoke/a/" + token.Accessor,
"lookup/a/b",
"policies",
"admin/register/a",
"register/a",
"generate/a/b",
}
paths := []string{}
for _, p := range paths {
w := httptest.NewRecorder()
@@ -81,357 +21,6 @@ func TestServerRoutes(t *testing.T) {
}
}
func TestServerAdminRegister(t *testing.T) {
cases := []struct {
name string
status int
}{
{
name: "",
status: 404,
},
{
name: "name",
status: 200,
},
}
path := "admin/register"
server, _, _ := mockServer()
for i, c := range cases {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", path, nil)
r = addParam(r, "name", c.name)
server.ServeHTTP(w, r)
if w.Code != c.status {
t.Errorf("[%d] wrong code for %s: got %d, expected %d", i, path, w.Code, c.status)
}
body, _ := ioutil.ReadAll(w.Body)
if len(body) < 1 {
t.Errorf("[%d] empty body in admin/register response: %q", i, body)
}
t.Logf("[%d] admin/register body: %q", i, body)
}
}
func TestServerRegister(t *testing.T) {
cases := []struct {
name string
status int
}{
{
name: "",
status: 404,
},
{
name: "name",
status: 200,
},
}
path := "register"
server, _, _ := mockServer()
for i, c := range cases {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", path, nil)
r = addParam(r, "name", c.name)
auth, err := server.authdb.New(serverNS, c.name)
if err != nil {
t.Fatalf("cannot authdb new: %v", err)
}
r.SetBasicAuth(c.name, auth.Accessor+":"+auth.Token)
server.ServeHTTP(w, r)
if w.Code != c.status {
t.Errorf("%d: wrong code for %s: got %d, expected %d", i, path, w.Code, c.status)
}
}
}
func TestServerGenerate(t *testing.T) {
cases := []struct {
name string
to string
status int
}{
{
name: "",
to: "",
status: 404,
},
{
name: "name",
to: "",
status: 404,
},
{
name: "",
to: "to",
status: 404,
},
{
name: "name",
to: "to",
status: 200,
},
}
path := "generate"
server, _, _ := mockServer()
for i, c := range cases {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", path, nil)
r = addParam(r, "name", c.name)
r = addParam(r, "to", c.to)
auth, err := server.authdb.New(serverNS, c.name)
if err != nil {
t.Fatalf("cannot authdb new: %v", err)
}
r.SetBasicAuth(c.name, auth.Accessor+":"+auth.Token)
server.ServeHTTP(w, r)
if w.Code != c.status {
t.Errorf("%d: wrong code for %s: got %d, expected %d", i, path, w.Code, c.status)
}
var result struct {
Token string `json:"token"`
Acc string `json:"accessor"`
TTL int `json:"TTL"`
To string `json:"to"`
}
if err := json.NewDecoder(w.Body).Decode(&result); c.status == 200 && err != nil {
t.Errorf("invalid body: %v", err)
} else if c.status == 200 {
if result.To != c.to {
t.Errorf("wrong `to` in response: got %v, want %v", result.To, c.to)
}
if len(result.Token) == 0 {
t.Errorf("empty `token` in response")
}
if len(result.Acc) == 0 {
t.Errorf("empty `accessor` in response")
}
if result.TTL < 100 {
t.Errorf("short TTL in response")
}
}
}
}
func TestServerRetrieve(t *testing.T) {
server, defaultName, defaultAccessor := mockServer()
cases := []struct {
name string
acc string
status int
}{
{
name: "",
acc: "",
status: 404,
},
{
name: defaultName,
acc: "",
status: 404,
},
{
name: "",
acc: defaultAccessor,
status: 404,
},
{
name: defaultName,
acc: defaultAccessor,
status: 200,
},
{
name: "fake",
acc: "fake",
status: 400,
},
}
path := "retrieve"
for i, c := range cases {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", path, nil)
r = addParam(r, "name", c.name)
r = addParam(r, "to", "to")
r = addParam(r, "accessor", c.acc)
auth, err := server.authdb.New(serverNS, c.name)
if err != nil {
t.Fatalf("cannot authdb new: %v", err)
}
r.SetBasicAuth(c.name, auth.Accessor+":"+auth.Token)
server.ServeHTTP(w, r)
if w.Code != c.status {
//t.Errorf("%d: wrong code for %s with %v: got %d, expected %d", i, path, c, w.Code, c.status)
t.Fatalf("%d: wrong code for %s with %v: got %d, expected %d", i, path, c, w.Code, c.status)
}
}
}
func TestServerRevoke(t *testing.T) {
cases := []struct {
name string
status int
}{
{
name: "",
status: 404,
},
{
name: "name",
status: 200,
},
}
path := "register"
server, _, _ := mockServer()
for i, c := range cases {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", path, nil)
r = addParam(r, "name", c.name)
auth, err := server.authdb.New(serverNS, c.name)
if err != nil {
t.Fatalf("cannot authdb new: %v", err)
}
r.SetBasicAuth(c.name, auth.Accessor+":"+auth.Token)
server.ServeHTTP(w, r)
if w.Code != c.status {
t.Errorf("%d: wrong code for %s: got %d, expected %d", i, path, w.Code, c.status)
}
}
}
func TestServerLookup(t *testing.T) {
from := "from"
to := "to"
cases := []struct {
from string
to string
status int
}{
{
from: "",
to: "",
status: 404,
},
{
from: "",
to: to,
status: 404,
},
{
from: from,
to: "",
status: 404,
},
{
from: from,
to: to,
status: 200,
},
}
path := "lookup"
server, _, _ := mockServer()
server.db.Register(from)
generated, _ := server.db.New(from, to)
for i, c := range cases {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", path, nil)
r = addParam(r, "from", c.from)
r = addParam(r, "to", c.to)
auth, err := server.authdb.New(serverNS, c.from)
if err != nil {
t.Fatalf("cannot authdb new: %v", err)
}
r.SetBasicAuth(c.from, auth.Accessor+":"+auth.Token)
server.ServeHTTP(w, r)
if w.Code != c.status {
t.Errorf("%d: wrong code for %s: got %d, expected %d", i, path, w.Code, c.status)
}
if w.Code != http.StatusOK {
continue
}
b, err := ioutil.ReadAll(w.Body)
if err != nil {
t.Fatalf("%d: cannot read body: %v", i, err)
}
if !bytes.Contains(b, []byte(generated.Accessor)) {
t.Errorf("%d: response didn't contain accessor: got %s, want %s", i, b, generated.Accessor)
}
}
}
func echoHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(r.URL.Path))
}
func addParam(r *http.Request, key, value string) *http.Request {
r.URL.Path += "/" + value
r.Header.Set(key, value)
return r
}
func TestServerAuthenticate(t *testing.T) {
server, _, _ := mockServer()
name := "name"
server.authdb.Register(serverNS)
token, err := server.authdb.New(serverNS, name)
if err != nil {
t.Fatalf("cannot authdb new: %v", err)
}
nilHandle := func(http.ResponseWriter, *http.Request) {}
authFunc := server.authenticate(nilHandle)
cases := []struct {
name string
token string
accessor string
code int
}{
{
name: "bad",
token: token.Token,
accessor: token.Accessor,
code: http.StatusUnauthorized,
},
{
name: name,
token: token.Token,
accessor: "bad",
code: http.StatusUnauthorized,
},
{
name: name,
token: "bad",
accessor: token.Accessor,
code: http.StatusUnauthorized,
},
{
name: name,
token: token.Token,
accessor: token.Accessor,
code: http.StatusOK,
},
}
for i, c := range cases {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/any", nil)
r.SetBasicAuth(c.name, c.accessor+":"+c.token)
authFunc(w, r)
if w.Code != c.code {
t.Errorf("[case %d] failed auth: got %v, wanted %v", i, w.Code, c.code)
}
}
}

View File

@@ -1,51 +1,84 @@
package server
import (
"fmt"
"local/s2sa/s2sa/logg"
"local/s2sa/s2sa/server/router"
"local/s2sa/s2sa/token"
"errors"
"local/rproxy3/config"
"local/rproxy3/storage"
"local/rproxy3/storage/packable"
"log"
"net/http"
"strings"
"net/url"
)
type Router interface {
Add(string, http.HandlerFunc) error
ServeHTTP(http.ResponseWriter, *http.Request)
}
const nsRouting = "routing"
type TokenDatabase interface {
Register(string) error
New(string, string) (token.Basic, error)
Get(string, string) (token.Basic, error)
Revoke(string, string) error
Lookup(string, string) (token.Basic, error)
}
type listenerScheme int
type Messenger interface {
Produce(string, interface{}) error
const (
schemeHTTP listenerScheme = iota
schemeHTTPS listenerScheme = iota
)
func (ls listenerScheme) String() string {
switch ls {
case schemeHTTP:
return "http"
case schemeHTTPS:
return "https"
}
return ""
}
type Server struct {
router Router
db TokenDatabase
authdb TokenDatabase
messenger Messenger
addr string
db storage.DB
addr string
username string
password string
}
func (s *Server) Add(path string, handler http.HandlerFunc) error {
logg.Logf("Adding path %v...\n", path)
path = strings.Replace(path, wildcard, router.Wildcard, -1)
return s.router.Add(path, handler)
func (s *Server) Route(src, dst string) error {
log.Printf("Adding route %q -> %q...\n", src, dst)
u, err := url.Parse(dst)
if err != nil {
return err
}
return s.db.Set(nsRouting, src, packable.NewURL(u))
}
func (s *Server) Run() error {
logg.Logf("Listening on %v...\n", s.addr)
return http.ListenAndServe(s.addr, s)
scheme := schemeHTTP
if _, _, ok := config.GetSSL(); ok {
scheme = schemeHTTPS
}
log.Printf("Listening for %v on %v...\n", scheme, s.addr)
switch scheme {
case schemeHTTP:
return http.ListenAndServe(s.addr, s)
case schemeHTTPS:
c, k, _ := config.GetSSL()
return http.ListenAndServeTLS(s.addr, c, k, s)
}
return errors.New("did not load server")
}
func (s *Server) doAuth(foo http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rusr, rpwd, ok := config.GetAuth()
if ok {
usr, pwd, ok := r.BasicAuth()
if !ok || rusr != usr || rpwd != pwd {
w.WriteHeader(http.StatusUnauthorized)
return
}
}
foo(w, r)
}
}
func (s *Server) Pre(foo http.HandlerFunc) http.HandlerFunc {
return s.doAuth(foo)
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Printf("REQ: %s\n", r.URL.Path)
s.router.ServeHTTP(w, r)
s.Pre(s.Proxy)(w, r)
}

View File

@@ -2,25 +2,17 @@ package server
import (
"fmt"
"io/ioutil"
"local/s2sa/s2sa/logg"
"local/s2sa/s2sa/server/router"
"local/s2sa/s2sa/services"
"local/s2sa/s2sa/storage"
"local/rproxy3/storage"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
)
func TestServerStart(t *testing.T) {
server, _, _ := mockServer()
server := mockServer()
checked := false
if err := server.Add("/hello/world", func(_ http.ResponseWriter, _ *http.Request) {
checked = true
}); err != nil {
if err := server.Route("world", "http://hello.localhost"+server.addr); err != nil {
t.Fatalf("cannot add route: %v", err)
}
@@ -30,58 +22,35 @@ func TestServerStart(t *testing.T) {
}
}()
if resp, err := http.Get("http://localhost" + server.addr + "/hello/world"); err != nil {
r, _ := http.NewRequest("GET", "http://world.localhost"+server.addr, nil)
if _, err := (&http.Client{}).Do(r); err != nil {
t.Errorf("failed to get: %v", err)
} else if resp.StatusCode != 200 {
t.Errorf("wrong status: %v", resp.StatusCode)
} else if !checked {
t.Errorf("didnt hit handler")
}
}
func mockServer() (*Server, string, string) {
f, _ := os.Open(os.DevNull)
logg.ConfigFile(f)
func mockServer() *Server {
portServer := httptest.NewServer(nil)
port := strings.Split(portServer.URL, ":")[2]
portServer.Close()
s := &Server{
router: router.New(),
db: services.New(storage.NewMap()),
authdb: services.New(storage.NewMap()),
addr: ":" + port,
db: storage.NewMap(),
addr: ":" + port,
}
s.authdb.Register(serverNS)
if err := s.Routes(); err != nil {
panic(fmt.Sprintf("cannot initiate server routes; %v", err))
}
defaultName := "name"
if err := s.db.Register(defaultName); err != nil {
panic(fmt.Sprintf("cannot register: %v", err))
}
token, err := s.db.New("name", "to")
if err != nil {
panic(fmt.Sprintf("cannot generate: %v", err))
}
defaultAccessor := token.Accessor
return s, defaultName, defaultAccessor
return s
}
func TestServerAdd(t *testing.T) {
server, _, _ := mockServer()
path := "/hello/world"
if err := server.Add(path, echoHTTP); err != nil {
t.Fatalf("cannot add path: %v", err)
func TestServerRoute(t *testing.T) {
server := mockServer()
if err := server.Route("world", "http://hello.localhost"+server.addr); err != nil {
t.Fatalf("cannot add route: %v", err)
}
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", path, nil)
r, _ := http.NewRequest("GET", "http://world.localhost"+server.addr, nil)
server.ServeHTTP(w, r)
b, err := ioutil.ReadAll(w.Body)
if err != nil {
t.Fatalf("cannot read body: %v", err)
}
if string(b) != path {
t.Errorf("cannot hit endpoint: %s", b)
if w.Code != 502 {
t.Fatalf("cannot proxy from 'world' to 'hello', status %v", w.Code)
}
}