-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit fd735a3
Showing
18 changed files
with
453 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/.vscode | ||
/.idea | ||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# go-fileshare | ||
|
||
A simple filesharing server in Go. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package fileshare | ||
|
||
import "errors" | ||
|
||
var ErrAuthMalformed = errors.New("malformed authentication token") | ||
var ErrAuthInvalid = errors.New("invalid authentication token") | ||
|
||
type AuthProvider interface { | ||
GetUser(jwt string) (*User, error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package auth | ||
|
||
import ( | ||
"fmt" | ||
"github.com/devgianlu/go-fileshare" | ||
"github.com/golang-jwt/jwt/v5" | ||
) | ||
|
||
type customClaims struct { | ||
jwt.RegisteredClaims | ||
Permissions []string `json:"permissions"` | ||
} | ||
|
||
type jwtAuthProvider struct { | ||
secret []byte | ||
parser *jwt.Parser | ||
} | ||
|
||
func (p *jwtAuthProvider) keyFunc(token *jwt.Token) (interface{}, error) { | ||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { | ||
return nil, fmt.Errorf("unexpected signing method: %s", token.Header["alg"]) | ||
} | ||
|
||
return p.secret, nil | ||
} | ||
|
||
func (p *jwtAuthProvider) GetUser(tokenString string) (*fileshare.User, error) { | ||
token, err := p.parser.ParseWithClaims(tokenString, &customClaims{}, p.keyFunc) | ||
if err != nil { | ||
return nil, fileshare.NewError("", fileshare.ErrAuthMalformed, err) | ||
} | ||
|
||
if !token.Valid { | ||
return nil, fileshare.NewError("", fileshare.ErrAuthInvalid, err) | ||
} | ||
|
||
claims := token.Claims.(*customClaims) | ||
return &fileshare.User{Permissions: claims.Permissions}, nil | ||
} | ||
|
||
func NewJWTAuthProvider(secret []byte) fileshare.AuthProvider { | ||
p := jwtAuthProvider{} | ||
p.secret = secret | ||
p.parser = jwt.NewParser() | ||
return &p | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/devgianlu/go-fileshare" | ||
"github.com/devgianlu/go-fileshare/auth" | ||
"github.com/devgianlu/go-fileshare/http" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
type Server struct { | ||
Auth fileshare.AuthProvider | ||
HTTP fileshare.HttpServer | ||
} | ||
|
||
func main() { | ||
const PORT = 8080 // FIXME | ||
const SECRET = "test1234" // FIXME | ||
|
||
log.SetLevel(log.TraceLevel) | ||
|
||
s := Server{} | ||
s.Auth = auth.NewJWTAuthProvider([]byte(SECRET)) | ||
s.HTTP = http.NewHTTPServer(PORT, s.Auth) | ||
|
||
if err := s.HTTP.ListenForever(); err != nil { | ||
log.WithError(err).Fatalf("failed listening") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package fileshare | ||
|
||
import ( | ||
"context" | ||
"github.com/gofiber/fiber/v2" | ||
) | ||
|
||
type contextKey int | ||
|
||
const ( | ||
userContextKey = contextKey(iota + 1) | ||
) | ||
|
||
func SetContextWithUser(ctx *fiber.Ctx, user *User) { | ||
parent := ctx.UserContext() | ||
if parent == nil { | ||
parent = context.Background() | ||
} | ||
|
||
ctx.SetUserContext(context.WithValue(parent, userContextKey, user)) | ||
} | ||
|
||
func UserFromContext(ctx *fiber.Ctx) *User { | ||
parent := ctx.UserContext() | ||
if parent == nil { | ||
parent = context.Background() | ||
} | ||
|
||
user, _ := parent.Value(userContextKey).(*User) | ||
return user | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package fileshare | ||
|
||
import "fmt" | ||
|
||
func NewError(message string, err error, more ...error) error { | ||
var format string | ||
var args []any | ||
if message != "" { | ||
format = "%w: %s" | ||
args = []any{err, message} | ||
} else { | ||
format = "%w" | ||
args = []any{err} | ||
} | ||
|
||
for _, e := range more { | ||
format += ": %w" | ||
args = append(args, e) | ||
} | ||
|
||
err = fmt.Errorf(format, args...) | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
module github.com/devgianlu/go-fileshare | ||
|
||
go 1.21.3 | ||
|
||
require ( | ||
github.com/gofiber/fiber/v2 v2.51.0 | ||
github.com/gofiber/template/html/v2 v2.0.5 | ||
github.com/golang-jwt/jwt/v5 v5.2.0 | ||
github.com/sirupsen/logrus v1.9.3 | ||
) | ||
|
||
require ( | ||
github.com/andybalholm/brotli v1.0.6 // indirect | ||
github.com/gofiber/template v1.8.2 // indirect | ||
github.com/gofiber/utils v1.1.0 // indirect | ||
github.com/google/uuid v1.4.0 // indirect | ||
github.com/klauspost/compress v1.17.2 // indirect | ||
github.com/mattn/go-colorable v0.1.13 // indirect | ||
github.com/mattn/go-isatty v0.0.20 // indirect | ||
github.com/mattn/go-runewidth v0.0.15 // indirect | ||
github.com/rivo/uniseg v0.4.4 // indirect | ||
github.com/valyala/bytebufferpool v1.0.0 // indirect | ||
github.com/valyala/fasthttp v1.50.0 // indirect | ||
github.com/valyala/tcplisten v1.0.0 // indirect | ||
golang.org/x/sys v0.15.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= | ||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ= | ||
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U= | ||
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk= | ||
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= | ||
github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20= | ||
github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM= | ||
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= | ||
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= | ||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= | ||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= | ||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= | ||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= | ||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= | ||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | ||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | ||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | ||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= | ||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | ||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= | ||
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= | ||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= | ||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= | ||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= | ||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package html | ||
|
||
import ( | ||
"embed" | ||
"fmt" | ||
"github.com/gofiber/template/html/v2" | ||
"io/fs" | ||
"net/http" | ||
) | ||
|
||
//go:embed templates/*.tmpl | ||
var embeddedTemplatesFS embed.FS | ||
|
||
func NewEngine() *html.Engine { | ||
f, err := fs.Sub(embeddedTemplatesFS, "templates") | ||
if err != nil { | ||
panic(fmt.Sprintf("cannot load templates filesystem: %v", err)) | ||
} | ||
|
||
return html.NewFileSystem(http.FS(f), ".tmpl") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<html lang="en"> | ||
<body> | ||
<!-- TODO --> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package fileshare | ||
|
||
type HttpServer interface { | ||
ListenForever() error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package http | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"github.com/devgianlu/go-fileshare" | ||
"github.com/gofiber/fiber/v2" | ||
"strings" | ||
) | ||
|
||
func (s *httpServer) getUser(authHeader string, authCookie string) (*fileshare.User, error) { | ||
var token string | ||
if len(authHeader) > 0 { | ||
authParts := strings.Split(authHeader, " ") | ||
if len(authParts) != 2 { | ||
return nil, fmt.Errorf("invalid authorization header") | ||
} else if authParts[0] != "Bearer" { | ||
return nil, fmt.Errorf("unsupported authorization header: %s", authParts[0]) | ||
} | ||
|
||
token = authParts[1] | ||
} else if len(authCookie) > 0 { | ||
token = authCookie | ||
} else { | ||
return nil, nil | ||
} | ||
|
||
user, err := s.auth.GetUser(token) | ||
if errors.Is(err, fileshare.ErrAuthMalformed) { | ||
return nil, newHttpError(fiber.StatusBadRequest, "malformed bearer token", err) | ||
} else if errors.Is(err, fileshare.ErrAuthInvalid) { | ||
return nil, newHttpError(fiber.StatusUnauthorized, "invalid bearer token", err) | ||
} else if err != nil { | ||
return nil, fmt.Errorf("failed authenticating: %w", err) | ||
} | ||
|
||
return user, nil | ||
} | ||
|
||
func (s *httpServer) newAuthHandler() fiber.Handler { | ||
return func(ctx *fiber.Ctx) error { | ||
if user, err := s.getUser(ctx.Get("Authorization"), ctx.Cookies("token")); err != nil { | ||
return err | ||
} else if user != nil { | ||
fileshare.SetContextWithUser(ctx, user) | ||
} | ||
|
||
return ctx.Next() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package http | ||
|
||
import ( | ||
"errors" | ||
"github.com/gofiber/fiber/v2" | ||
) | ||
|
||
type httpError struct { | ||
statusCode int | ||
message string | ||
err error | ||
} | ||
|
||
func (e *httpError) Error() string { | ||
return e.err.Error() | ||
} | ||
|
||
func (e *httpError) Unwrap() error { | ||
return e.err | ||
} | ||
|
||
func newHttpError(statusCode int, message string, err error) error { | ||
return &httpError{statusCode, message, err} | ||
} | ||
|
||
func asHttpError(err error) (bool, int, string) { | ||
var httpErr *httpError | ||
if !errors.As(err, &httpErr) { | ||
return false, 0, "" | ||
} | ||
|
||
return true, httpErr.statusCode, httpErr.message | ||
} | ||
|
||
func newErrorHandler() fiber.Handler { | ||
return func(ctx *fiber.Ctx) error { | ||
err := ctx.Next() | ||
if ok, statusCode, message := asHttpError(err); ok { | ||
// set status code and message header | ||
ctx.Status(statusCode) | ||
ctx.Set("X-Error-Message", message) | ||
|
||
// return the error for the logger to see, we'll stop it in the error handler | ||
return err | ||
} | ||
|
||
// unhandled error, let it propagate | ||
return err | ||
} | ||
} |
Oops, something went wrong.