Minor linting fixes & move to CircleCI/UPX orb.

Some minor linting fixes and general style/scope changes, which should
have no impact on the overall application. Also moved to the offical
CircleCI UPX Orb, rather than my own (which is still maintained by me).
This commit is contained in:
Gabriel Simmer 2020-04-24 09:01:53 +01:00
parent c559f28ebb
commit ce4ee64509
No known key found for this signature in database
GPG key ID: 33BA4D83B160A0A9
9 changed files with 89 additions and 75 deletions

View file

@ -1,6 +1,6 @@
version: 2.1
orbs:
upx: gmem/upx@1.0.1
upx: circleci/upx@1.0.1
jobs:
build:
docker:

View file

@ -32,4 +32,4 @@ dist: clean make_build_dir small small_pi
tar -czf build/tars/nas-$(NAS_VERSION)-x86.tar.gz build/assets build/bin/nas README.md LICENSE
clean:
rm -rf build
rm -rf build

View file

@ -2,11 +2,15 @@ package authentication
import (
"fmt"
"github.com/Nerzal/gocloak/v5"
)
// AuthConfig contains the configuration for the IdP.
var AuthConfig map[string]string
// HasAuth checks the passed token against the IdP, and returns true
// if the IdP can return the user's info, false if not.
func HasAuth(accessToken string) (success bool) {
client := gocloak.NewClient(AuthConfig["provider_url"])
_, err := client.GetUserInfo(accessToken, AuthConfig["realm"])
@ -16,8 +20,9 @@ func HasAuth(accessToken string) (success bool) {
return true
}
// GetLoginLink generates a redirect link to the IdP login page.
func GetLoginLink() (url string) {
baseString := "%v/auth/realms/%v/protocol/openid-connect/auth?client_id=account&response_mode=fragment&response_type=token&login=true&redirect_uri=%v/api/auth/callback"
authUrl := fmt.Sprintf(baseString, AuthConfig["provider_url"], AuthConfig["realm"], AuthConfig["redirect_base_url"])
return authUrl
}
authURL := fmt.Sprintf(baseString, AuthConfig["provider_url"], AuthConfig["realm"], AuthConfig["redirect_base_url"])
return authURL
}

View file

@ -262,7 +262,7 @@ func (bp *BackblazeProvider) ObjectInfo(path string) (bool, bool, string) {
// Therefore, we can assume everything is a file ;)
// TODO: Return true value.
isDir := path == ""
return true, isDir, FILE_IS_REMOTE
return true, isDir, FileIsRemote
}
func (bp *BackblazeProvider) CreateDirectory(path string) bool {

View file

@ -73,13 +73,13 @@ func (dp *DiskProvider) ObjectInfo(path string) (bool, bool, string) {
fileStat, err := os.Stat(rp)
if err != nil {
fmt.Printf("error gather stats for file %v: %v", rp, err.Error())
return false, false, FILE_IS_LOCAL
return false, false, FileIsLocal
}
if fileStat.IsDir() {
return true, true, FILE_IS_LOCAL
return true, true, FileIsLocal
}
return true, false, FILE_IS_LOCAL
return true, false, FileIsLocal
}
func (dp *DiskProvider) CreateDirectory(path string) bool {

View file

@ -4,9 +4,14 @@ import (
"io"
)
const FILE_IS_REMOTE = "remote"
const FILE_IS_LOCAL = "local"
// FileIsRemote denotes whether file is a remote file.
const FileIsRemote = "remote"
// FileIsLocal denotes whether file is a local file.
const FileIsLocal = "local"
// FileProvider aggregates some very basic properties for authentication and
// provider decoding.
type FileProvider struct {
Name string `yaml:"name"`
Provider string `yaml:"provider"`
@ -15,72 +20,72 @@ type FileProvider struct {
Config map[string]string `yaml:"config"`
}
// Directory contains the path and a collection of FileInfos.
type Directory struct {
Path string
Files []FileInfo
}
// FileInfo describes a single file or directory, doing it's best to
// figure out the extension as well.
type FileInfo struct {
IsDirectory bool
Name string
Extension string
}
type FileContents struct {
Content []byte
IsUrl bool
}
// FileProviderInterface provides some sane default functions.
type FileProviderInterface interface {
// Called on initial startup of the application.
Setup(args map[string]string) (ok bool)
// Fetches the contents of a "directory".
GetDirectory(path string) (directory Directory)
// Builds a path to a file, for serving.
FilePath(path string) (realpath string)
// Fetch and pass along a remote file directly to the response writer.
RemoteFile(path string, writer io.Writer)
// Save a file from an io.Reader.
SaveFile(file io.Reader, filename string, path string) (ok bool)
// Fetch the info for an object given a path to if the file exists and location.
// Should return whether the path exists, if the path is a directory, and if it lives on disk.
// (see constants defined: `FILE_IS_REMOTE` and `FILE_IS_LOCAL`)
ObjectInfo(path string) (exists bool, isDir bool, location string)
// Create a directory if possible, returns the result.
CreateDirectory(path string) (ok bool)
// Delete a file or directory.
Delete(path string) (ok bool)
}
/** DO NOT USE THESE DEFAULTS **/
// Setup runs when the application starts up, and allows for things like authentication.
func (f FileProvider) Setup(args map[string]string) bool {
return false
}
// GetDirectory fetches a directory's contents.
func (f FileProvider) GetDirectory(path string) Directory {
return Directory{}
}
// FilePath returns the path to the file, whether it be a URL or local file path.
func (f FileProvider) FilePath(path string) string {
return ""
}
// RemoteFile will bypass http.ServeContent() and instead write directly to the response.
func (f FileProvider) RemoteFile(path string, writer io.Writer) {
return
}
// SaveFile will save a file with the contents of the io.Reader at the path specified.
func (f FileProvider) SaveFile(file io.Reader, filename string, path string) bool {
return false
}
// ObjectInfo will return the info for an object given a path to if the file exists and location.
// Should return whether the path exists, if the path is a directory, and if it lives on disk.
// (see constants defined: `FILE_IS_REMOTE` and `FILE_IS_LOCAL`)
func (f FileProvider) ObjectInfo(path string) (bool, bool, string) {
return false, false, ""
}
// CreateDirectory will create a directory on services that support it.
func (f FileProvider) CreateDirectory(path string) bool {
return false
}
// Delete simply deletes a file. This is expected to be a destructive action by default.
func (f FileProvider) Delete(path string) bool {
return false
}
}

View file

@ -2,15 +2,18 @@ package router
import (
"fmt"
"github.com/gmemstr/nas/authentication"
"io/ioutil"
"net/http"
"github.com/gmemstr/nas/authentication"
)
// AuthEnabled is a global variable that determines whether we were
// able to set up an authentication method (e.g Keycloak).
var AuthEnabled bool = true
func requiresAuth() Handler {
return func(context *Context, w http.ResponseWriter, r *http.Request) *HTTPError {
func requiresAuth() handler {
return func(context *requestContext, w http.ResponseWriter, r *http.Request) *httpError {
if !AuthEnabled {
return nil
}
@ -20,7 +23,7 @@ func requiresAuth() Handler {
fmt.Println("Error", err.Error())
}
http.Redirect(w, r, authentication.GetLoginLink(), 307)
return &HTTPError{
return &httpError{
Message: "Unauthorized! Redirecting to /login",
StatusCode: http.StatusTemporaryRedirect,
}
@ -29,8 +32,8 @@ func requiresAuth() Handler {
}
}
func callbackAuth() Handler {
return func(context *Context, w http.ResponseWriter, r *http.Request) *HTTPError {
func callbackAuth() handler {
return func(context *requestContext, w http.ResponseWriter, r *http.Request) *httpError {
// Translate callback GET to POST to set cookie, then redirect.
if r.Method == "GET" {
javascript := `
@ -51,4 +54,4 @@ func callbackAuth() Handler {
return nil
}
}
}

View file

@ -13,8 +13,8 @@ import (
"time"
)
func HandleProvider() Handler {
return func(context *Context, w http.ResponseWriter, r *http.Request) *HTTPError {
func handleProvider() handler {
return func(context *requestContext, w http.ResponseWriter, r *http.Request) *httpError {
vars := mux.Vars(r)
providerCodename := vars["provider"]
providerCodename = strings.Replace(providerCodename, "/", "", -1)
@ -24,14 +24,14 @@ func HandleProvider() Handler {
if r.Method == "GET" {
filename, err := url.QueryUnescape(vars["file"])
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error determining filetype for %s\n", filename),
StatusCode: http.StatusInternalServerError,
}
}
ok, isDir, location := provider.ObjectInfo(filename)
if !ok {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error locating file %s\n", filename),
StatusCode: http.StatusNotFound,
}
@ -42,7 +42,7 @@ func HandleProvider() Handler {
data, err := json.Marshal(fileList)
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error fetching filelisting for %s\n", vars["file"]),
StatusCode: http.StatusNotFound,
}
@ -52,12 +52,12 @@ func HandleProvider() Handler {
}
// If the file is local, attempt to use http.ServeContent for correct headers.
if location == files.FILE_IS_LOCAL {
if location == files.FileIsLocal {
rp := provider.FilePath(filename)
if rp != "" {
f, err := os.Open(rp)
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error opening file %s\n", rp),
StatusCode: http.StatusInternalServerError,
}
@ -67,7 +67,7 @@ func HandleProvider() Handler {
}
// If the file is remote, then delegate the writing to the response to the provider.
// This isn't a great workaround, but avoids caching the whole file in mem or on disk.
if location == files.FILE_IS_REMOTE {
if location == files.FileIsRemote {
provider.RemoteFile(filename, w)
}
return nil
@ -81,7 +81,7 @@ func HandleProvider() Handler {
dirname := vars["file"]
success := provider.CreateDirectory(dirname)
if !success {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error creating directory %s\n", dirname),
StatusCode: http.StatusInternalServerError,
}
@ -92,7 +92,7 @@ func HandleProvider() Handler {
err := r.ParseMultipartForm(32 << 20)
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error parsing form for %s\n", vars["file"]),
StatusCode: http.StatusInternalServerError,
}
@ -102,7 +102,7 @@ func HandleProvider() Handler {
success := provider.SaveFile(file, handler.Filename, vars["file"])
if !success {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error saving file %s\n", vars["file"]),
StatusCode: http.StatusInternalServerError,
}
@ -115,7 +115,7 @@ func HandleProvider() Handler {
path := vars["file"]
success := provider.Delete(path)
if !success {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error deleting %s\n", path),
StatusCode: http.StatusInternalServerError,
}
@ -127,16 +127,16 @@ func HandleProvider() Handler {
}
}
func ListProviders() Handler {
return func(context *Context, w http.ResponseWriter, r *http.Request) *HTTPError {
func listProviders() handler {
return func(context *requestContext, w http.ResponseWriter, r *http.Request) *httpError {
var providers []string
for v, _ := range files.ProviderConfig {
for v := range files.ProviderConfig {
providers = append(providers, v)
}
sort.Strings(providers)
data, err := json.Marshal(providers)
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error provider listing"),
StatusCode: http.StatusInternalServerError,
}

View file

@ -2,27 +2,29 @@ package router
import (
"fmt"
"github.com/gorilla/mux"
"io"
"log"
"net/http"
"os"
"strconv"
"github.com/gorilla/mux"
)
type Handler func(context *Context, w http.ResponseWriter, r *http.Request) *HTTPError
type handler func(context *requestContext, w http.ResponseWriter, r *http.Request) *httpError
type HTTPError struct {
Message string
type httpError struct {
Message string
StatusCode int
}
// Context contains any information to be shared with middlewares.
type Context struct {}
type requestContext struct{}
func Handle(handlers ...Handler) http.Handler {
// Loop through passed functions and execute them, passing through the current
// requestContext, response writer and request reader.
func handle(handlers ...handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
context := &Context{}
context := &requestContext{}
for _, handler := range handlers {
err := handler(context, w, r)
if err != nil {
@ -34,8 +36,7 @@ func Handle(handlers ...Handler) http.Handler {
})
}
// Actual router, define endpoints here.
// Init initializes the main router and all routes for the application.
func Init() *mux.Router {
r := mux.NewRouter()
@ -46,29 +47,29 @@ func Init() *mux.Router {
r.PathPrefix("/icons/").Handler(http.StripPrefix("/icons/", http.FileServer(http.Dir("assets/web/icons"))))
// Paths that require specific handlers
r.Handle("/", Handle(
r.Handle("/", handle(
requiresAuth(),
rootHandler(),
)).Methods("GET")
// File & Provider API
r.Handle("/api/providers", Handle(
r.Handle("/api/providers", handle(
requiresAuth(),
ListProviders(),
listProviders(),
)).Methods("GET")
r.Handle(`/api/files/{provider:[a-zA-Z0-9]+\/*}`, Handle(
r.Handle(`/api/files/{provider:[a-zA-Z0-9]+\/*}`, handle(
requiresAuth(),
HandleProvider(),
handleProvider(),
)).Methods("GET", "POST")
r.Handle(`/api/files/{provider:[a-zA-Z0-9]+}/{file:.+}`, Handle(
r.Handle(`/api/files/{provider:[a-zA-Z0-9]+}/{file:.+}`, handle(
requiresAuth(),
HandleProvider(),
handleProvider(),
)).Methods("GET", "POST", "DELETE")
// Auth API & Endpoints
r.Handle(`/api/auth/callback`, Handle(
r.Handle(`/api/auth/callback`, handle(
callbackAuth(),
)).Methods("GET", "POST")
@ -76,11 +77,11 @@ func Init() *mux.Router {
}
// Handles serving index page.
func rootHandler() Handler {
return func(context *Context, w http.ResponseWriter, r *http.Request) *HTTPError {
func rootHandler() handler {
return func(context *requestContext, w http.ResponseWriter, r *http.Request) *httpError {
f, err := os.Open("assets/web/index.html")
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error serving index page from assets/web"),
StatusCode: http.StatusInternalServerError,
}
@ -89,7 +90,7 @@ func rootHandler() Handler {
defer f.Close()
stats, err := f.Stat()
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error serving index page from assets/web"),
StatusCode: http.StatusInternalServerError,
}
@ -99,11 +100,11 @@ func rootHandler() Handler {
_, err = io.Copy(w, f)
if err != nil {
return &HTTPError{
return &httpError{
Message: fmt.Sprintf("error serving index page from assets/web"),
StatusCode: http.StatusInternalServerError,
}
}
return nil
}
}
}