create a web interface
This commit is contained in:
27
config/password.go
Normal file
27
config/password.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package i2ptunconf
|
||||||
|
|
||||||
|
// GetPassword takes an argument and a default. If the argument differs from the
|
||||||
|
// default, the argument is always returned. If the argument and default are
|
||||||
|
// the same and the key exists, the key is returned. If the key is absent, the
|
||||||
|
// default is returned.
|
||||||
|
func (c *Conf) GetPassword(arg, def string, label ...string) string {
|
||||||
|
if arg != def {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
if c.Config == nil {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
if x, o := c.Get("username", label...); o {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeys sets the key name from the config file
|
||||||
|
func (c *Conf) SetPassword(label ...string) {
|
||||||
|
if v, ok := c.Get("username", label...); ok {
|
||||||
|
c.Password = v
|
||||||
|
} else {
|
||||||
|
c.Password = "samcatd"
|
||||||
|
}
|
||||||
|
}
|
27
config/user.go
Normal file
27
config/user.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package i2ptunconf
|
||||||
|
|
||||||
|
// GetUserName takes an argument and a default. If the argument differs from the
|
||||||
|
// default, the argument is always returned. If the argument and default are
|
||||||
|
// the same and the key exists, the key is returned. If the key is absent, the
|
||||||
|
// default is returned.
|
||||||
|
func (c *Conf) GetUserName(arg, def string, label ...string) string {
|
||||||
|
if arg != def {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
if c.Config == nil {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
if x, o := c.Get("username", label...); o {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeys sets the key name from the config file
|
||||||
|
func (c *Conf) SetUserName(label ...string) {
|
||||||
|
if v, ok := c.Get("username", label...); ok {
|
||||||
|
c.UserName = v
|
||||||
|
} else {
|
||||||
|
c.UserName = "samcatd"
|
||||||
|
}
|
||||||
|
}
|
100
handler/login.go
Normal file
100
handler/login.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package samtunnelhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a struct that models the structure of a user, both in the request body, and in the DB
|
||||||
|
type Credentials struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomBytes(n int) ([]byte, error) {
|
||||||
|
b := make([]byte, n)
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomString(s int) (string, error) {
|
||||||
|
b, err := GenerateRandomBytes(s)
|
||||||
|
return base64.URLEncoding.EncodeToString(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) Signin(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var creds Credentials
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&creds)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if creds.Username != m.user {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if creds.Password != m.password {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.sessionToken, err = GenerateRandomString(32)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "session_token",
|
||||||
|
Value: m.sessionToken,
|
||||||
|
Expires: time.Now().Add(10 * time.Minute),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) Home(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if m.CheckCookie(w, r) == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r2, err := http.NewRequest("GET", r.URL.Path+"/color", r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "<!DOCTYPE html>\n")
|
||||||
|
fmt.Fprintf(w, "<html>\n")
|
||||||
|
fmt.Fprintf(w, "<head>\n")
|
||||||
|
fmt.Fprintf(w, " <link rel=\"stylesheet\" href=\"/styles.css\">")
|
||||||
|
fmt.Fprintf(w, "</head>\n")
|
||||||
|
fmt.Fprintf(w, "<body>\n")
|
||||||
|
fmt.Fprintf(w, "<h1>\n")
|
||||||
|
w.Write([]byte(fmt.Sprintf("Welcome %s! you are serving %d tunnels.\n", m.user, len(m.tunnels))))
|
||||||
|
fmt.Fprintf(w, "</h1>\n")
|
||||||
|
for _, tunnel := range m.Tunnels() {
|
||||||
|
tunnel.ServeHTTP(w, r2)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, " <script src=\"/scripts.js\"></script>\n")
|
||||||
|
fmt.Fprintf(w, "</body>\n")
|
||||||
|
fmt.Fprintf(w, "</html>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) CSS(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if m.CheckCookie(w, r) == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "text/css")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(fmt.Sprintf("%s\n", m.cssString)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) JS(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if m.CheckCookie(w, r) == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(fmt.Sprintf("%s\n", m.jsString)))
|
||||||
|
}
|
147
handler/mux.go
Normal file
147
handler/mux.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package samtunnelhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TunnelHandlerMux struct {
|
||||||
|
http.Server
|
||||||
|
pagenames []string
|
||||||
|
tunnels []*TunnelHandler
|
||||||
|
user string
|
||||||
|
password string
|
||||||
|
sessionToken string
|
||||||
|
cssString string
|
||||||
|
jsString string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) ListenAndServe() {
|
||||||
|
m.Server.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) PageCheck(path string) bool {
|
||||||
|
for _, v := range m.pagenames {
|
||||||
|
if strings.Contains(path, strings.Replace(v, "/", "", 0)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) CheckCookie(w http.ResponseWriter, r *http.Request) bool {
|
||||||
|
if m.password != "" {
|
||||||
|
if m.sessionToken == "" {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
c, err := r.Cookie("session_token")
|
||||||
|
if err != nil {
|
||||||
|
if err == http.ErrNoCookie {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if m.sessionToken != c.Value {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) HandlerWrapper(h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if m.CheckCookie(w, r) == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.PageCheck(r.URL.Path) {
|
||||||
|
fmt.Fprintf(w, "<!DOCTYPE html>\n")
|
||||||
|
fmt.Fprintf(w, "<html>\n")
|
||||||
|
fmt.Fprintf(w, "<head>\n")
|
||||||
|
fmt.Fprintf(w, " <link rel=\"stylesheet\" href=\"/styles.css\">")
|
||||||
|
fmt.Fprintf(w, "</head>\n")
|
||||||
|
fmt.Fprintf(w, "<body>\n")
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
fmt.Fprintf(w, " <script src=\"/scripts.js\"></script>\n")
|
||||||
|
fmt.Fprintf(w, "</body>\n")
|
||||||
|
fmt.Fprintf(w, "</html>\n")
|
||||||
|
} else if !strings.HasSuffix(r.URL.Path, "color") {
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "<!DOCTYPE html>\n")
|
||||||
|
fmt.Fprintf(w, "<html>\n")
|
||||||
|
fmt.Fprintf(w, "<head>\n")
|
||||||
|
fmt.Fprintf(w, " <link rel=\"stylesheet\" href=\"/styles.css\">")
|
||||||
|
fmt.Fprintf(w, "</head>\n")
|
||||||
|
fmt.Fprintf(w, "<body>\n")
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
fmt.Fprintf(w, " <script src=\"/scripts.js\"></script>\n")
|
||||||
|
fmt.Fprintf(w, "</body>\n")
|
||||||
|
fmt.Fprintf(w, "</html>\n")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TunnelHandlerMux) Tunnels() []*TunnelHandler {
|
||||||
|
return t.tunnels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TunnelHandlerMux) Append(v *TunnelHandler) *TunnelHandlerMux {
|
||||||
|
for _, prev := range m.tunnels {
|
||||||
|
if v.ID() == prev.ID() {
|
||||||
|
log.Printf("v.ID() found, %s == %s", v.ID(), prev.ID())
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("Adding tunnel ID: %s", v.ID())
|
||||||
|
m.tunnels = append(m.tunnels, v)
|
||||||
|
Handler := m.Handler.(*http.ServeMux)
|
||||||
|
Handler.Handle(fmt.Sprintf("/%d", len(m.tunnels)), m.HandlerWrapper(v))
|
||||||
|
Handler.Handle(fmt.Sprintf("/%s", v.ID()), m.HandlerWrapper(v))
|
||||||
|
Handler.Handle(fmt.Sprintf("/%d/color", len(m.tunnels)), m.HandlerWrapper(v))
|
||||||
|
Handler.Handle(fmt.Sprintf("/%s/color", v.ID()), m.HandlerWrapper(v))
|
||||||
|
m.Handler = Handler
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadFile(filename string) (string, error) {
|
||||||
|
r, e := ioutil.ReadFile(filename)
|
||||||
|
return string(r), e
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTunnelHandlerMux(host, port, user, password, css, javascript string) *TunnelHandlerMux {
|
||||||
|
var m TunnelHandlerMux
|
||||||
|
m.Addr = host + ":" + port
|
||||||
|
Handler := http.NewServeMux()
|
||||||
|
m.pagenames = []string{"index.html"}
|
||||||
|
m.user = user
|
||||||
|
m.password = password
|
||||||
|
m.sessionToken = ""
|
||||||
|
m.tunnels = []*TunnelHandler{}
|
||||||
|
var err error
|
||||||
|
m.cssString, err = ReadFile(css)
|
||||||
|
if err != nil {
|
||||||
|
m.cssString = DefaultCSS()
|
||||||
|
}
|
||||||
|
m.jsString, err = ReadFile(javascript)
|
||||||
|
if err != nil {
|
||||||
|
m.jsString = DefaultJS()
|
||||||
|
}
|
||||||
|
for _, v := range m.pagenames {
|
||||||
|
Handler.HandleFunc(fmt.Sprintf("/%s", v), m.Home)
|
||||||
|
}
|
||||||
|
Handler.HandleFunc("/styles.css", m.CSS)
|
||||||
|
Handler.HandleFunc("/scripts.js", m.JS)
|
||||||
|
if m.password != "" {
|
||||||
|
Handler.HandleFunc("/login", m.Signin)
|
||||||
|
}
|
||||||
|
m.Handler = Handler
|
||||||
|
return &m
|
||||||
|
}
|
32
handler/pages.go
Normal file
32
handler/pages.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package samtunnelhandler
|
||||||
|
|
||||||
|
func DefaultCSS() string {
|
||||||
|
return `.server {
|
||||||
|
background-color: #9DABD5;
|
||||||
|
}
|
||||||
|
.http {
|
||||||
|
background-color: #00ffff;
|
||||||
|
}
|
||||||
|
.client {
|
||||||
|
background-color: #2D4470;
|
||||||
|
}
|
||||||
|
.udpserver {
|
||||||
|
background-color: #265ea7;
|
||||||
|
}
|
||||||
|
.udpclient {
|
||||||
|
background-color: #222187;
|
||||||
|
}
|
||||||
|
.TunName {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background-color: #9e9e9e;
|
||||||
|
color: ;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultJS() string {
|
||||||
|
return `
|
||||||
|
`
|
||||||
|
}
|
Reference in New Issue
Block a user