added login page and authentication middleware, altought not yet implemented
ci/woodpecker/push/woodpecker Pipeline failed
Details
ci/woodpecker/push/woodpecker Pipeline failed
Details
parent
f6ab0bb4e2
commit
ddc95f49cd
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal" created-in="IU-232.9559.62">
|
||||
<component name="dataSourceStorageLocal" created-in="IU-232.9921.47">
|
||||
<data-source name="filegate@filegate.dev.jmbit.de" uuid="28e7aa7f-2fe3-45ee-997f-c35496a4a3eb">
|
||||
<database-info product="PostgreSQL" version="15.3 (Debian 15.3-0+deb12u1)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="15.3" exact-driver-version="42.6">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/pgsetup.sql" dialect="PostgreSQL" />
|
||||
<file url="file://$PROJECT_DIR$/sqliteschema.sql" dialect="SQLite" />
|
||||
</component>
|
||||
</project>
|
|
@ -8,7 +8,45 @@
|
|||
<option name="autoReloadType" value="ALL" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="eeedfed2-369e-4948-a46c-18edddaeec9c" name="Changes" comment="" />
|
||||
<list default="true" id="eeedfed2-369e-4948-a46c-18edddaeec9c" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/Makefile" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/database/user.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/filegate.service" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/templates/login.gohtml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/web/auth/hashPassword.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/web/auth/login.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/web/auth/middleware.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/web/routes.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/web/session.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.local.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dataSources/28e7aa7f-2fe3-45ee-997f-c35496a4a3eb.xml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/sqldialects.xml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Dockerfile" beforeDir="false" afterPath="$PROJECT_DIR$/Dockerfile" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Installation.md" beforeDir="false" afterPath="$PROJECT_DIR$/Installation.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/config.yaml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config/config.go" beforeDir="false" afterPath="$PROJECT_DIR$/config/config.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config/defaults.go" beforeDir="false" afterPath="$PROJECT_DIR$/config/defaults.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config/struct.go" beforeDir="false" afterPath="$PROJECT_DIR$/config/struct.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/database/files.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/files.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/database/models.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/models.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/database/postgresql.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/postgresql.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/files/filemanager.go" beforeDir="false" afterPath="$PROJECT_DIR$/files/filemanager.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/files/minio.go" beforeDir="false" afterPath="$PROJECT_DIR$/files/minio.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/files/staticanalysis.go" beforeDir="false" afterPath="$PROJECT_DIR$/files/staticanalysis.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/main.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pgsetup.sql" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/podthing/downloadContainer.go" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/podthing/podmanager.go" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/sqliteschema.sql" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/templates/filelist.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/templates/filelist.gohtml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/templates/footer.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/templates/footer.gohtml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/browser.go" beforeDir="false" afterPath="$PROJECT_DIR$/web/browser/browser.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/filelist.go" beforeDir="false" afterPath="$PROJECT_DIR$/web/filelist.go" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
|
@ -39,48 +77,48 @@
|
|||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"DefaultGoTemplateProperty": "Go File",
|
||||
"DefaultHtmlFileTemplate": "HTML File",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.go.formatter.settings.were.checked": "true",
|
||||
"RunOnceActivity.go.migrated.go.modules.settings": "true",
|
||||
"RunOnceActivity.go.modules.automatic.dependencies.download": "true",
|
||||
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
|
||||
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"WebServerToolWindowFactoryState": "false",
|
||||
"git-widget-placeholder": "main",
|
||||
"go.import.settings.migrated": "true",
|
||||
"go.sdk.automatically.set": "true",
|
||||
"last_opened_file_path": "/root",
|
||||
"list.type.of.created.stylesheet": "CSS",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"ruby.rails.projectView.checked": "true",
|
||||
"run.code.analysis.last.selected.profile": "pProject Default",
|
||||
"settings.editor.selected.configurable": "database.query.execution",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"DefaultGoTemplateProperty": "Go File",
|
||||
"DefaultHtmlFileTemplate": "HTML File",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.go.formatter.settings.were.checked": "true",
|
||||
"RunOnceActivity.go.migrated.go.modules.settings": "true",
|
||||
"RunOnceActivity.go.modules.automatic.dependencies.download": "true",
|
||||
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
|
||||
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"WebServerToolWindowFactoryState": "false",
|
||||
"git-widget-placeholder": "main",
|
||||
"go.import.settings.migrated": "true",
|
||||
"go.sdk.automatically.set": "true",
|
||||
"last_opened_file_path": "/home/johannes/git/filegate/filegate/templates",
|
||||
"list.type.of.created.stylesheet": "CSS",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"ruby.rails.projectView.checked": "true",
|
||||
"run.code.analysis.last.selected.profile": "pProject Default",
|
||||
"settings.editor.selected.configurable": "preferences.lookFeel",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
},
|
||||
"keyToStringList": {
|
||||
"DatabaseDriversLRU": [
|
||||
"sqlite"
|
||||
"keyToStringList": {
|
||||
"DatabaseDriversLRU": [
|
||||
"sqlite"
|
||||
],
|
||||
"RunConfigurationTargetLRU": [
|
||||
"28e7aa7f-2fe3-45ee-997f-c35496a4a3eb/database/\"filegate\""
|
||||
"RunConfigurationTargetLRU": [
|
||||
"28e7aa7f-2fe3-45ee-997f-c35496a4a3eb/database/\"filegate\""
|
||||
],
|
||||
"com.intellij.ide.scratch.LRUPopupBuilder$1/": [
|
||||
"SQLite"
|
||||
"com.intellij.ide.scratch.LRUPopupBuilder$1/": [
|
||||
"SQLite"
|
||||
],
|
||||
"com.intellij.ide.scratch.LRUPopupBuilder$1/SQL Dialect": [
|
||||
"SQLite"
|
||||
"com.intellij.ide.scratch.LRUPopupBuilder$1/SQL Dialect": [
|
||||
"SQLite"
|
||||
]
|
||||
}
|
||||
}</component>
|
||||
}]]></component>
|
||||
<component name="RdControllerToolWindowsLayoutState" isNewUi="true">
|
||||
<layout>
|
||||
<window_info id="Space Code Reviews" />
|
||||
|
@ -118,10 +156,11 @@
|
|||
<recent name="$PROJECT_DIR$/files" />
|
||||
</key>
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="$PROJECT_DIR$/templates" />
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/web/browser" />
|
||||
<recent name="$PROJECT_DIR$/" />
|
||||
<recent name="$PROJECT_DIR$/src/" />
|
||||
<recent name="$PROJECT_DIR$/database" />
|
||||
|
@ -204,11 +243,11 @@
|
|||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Database Script.pgsetup.sql" />
|
||||
<item itemvalue="Go Build.go run ." />
|
||||
<item itemvalue="Docker.devel-compose.yml: Compose Deployment" />
|
||||
<item itemvalue="Docker.Dockerfile" />
|
||||
<item itemvalue="Docker.Dockerfile builder" />
|
||||
<item itemvalue="Go Build.go run ." />
|
||||
<item itemvalue="Database Script.pgsetup.sql" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
|
@ -246,12 +285,26 @@
|
|||
<workItem from="1692525884012" duration="3960000" />
|
||||
<workItem from="1692549204776" duration="6122000" />
|
||||
<workItem from="1694330563801" duration="7776000" />
|
||||
<workItem from="1695542369457" duration="4674000" />
|
||||
<workItem from="1695990751089" duration="514000" />
|
||||
<workItem from="1695991319785" duration="59000" />
|
||||
<workItem from="1695991389809" duration="448000" />
|
||||
<workItem from="1695991846569" duration="902000" />
|
||||
<workItem from="1696234645954" duration="4224000" />
|
||||
<workItem from="1696248724514" duration="4736000" />
|
||||
<workItem from="1696311727002" duration="6423000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="UnknownFeatures">
|
||||
<option featureType="dependencySupport" implementationName="executable:docker" />
|
||||
<option featureType="dependencySupport" implementationName="executable:podman" />
|
||||
<option featureType="dependencySupport" implementationName="executable:kubectl" />
|
||||
<option featureType="dependencySupport" implementationName="executable:terraform" />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
|
|
|
@ -3,7 +3,7 @@ LABEL authors="Johannes Bülow <johannes.buelow@jmbit.de>"
|
|||
# Get all the stuff for CGO and Podman bindings
|
||||
WORKDIR /usr/local/src
|
||||
COPY ./ ./
|
||||
RUN go get . && go mod download && go build -o filegate
|
||||
RUN go get . && go mod download && go build -v -x -race -o filegate
|
||||
|
||||
FROM alpine
|
||||
LABEL authors="Johannes Bülow <johannes.buelow@jmbit.de>"
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
# Installation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
OS: Linux, preferrably Debian
|
||||
Database: PostgreSQL
|
||||
Object Storage: Preferrably MinIO, but anny S3-Compatible Storage should do
|
||||
Container Engine: Podman
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Install the OS
|
||||
|
||||
This depends a lot on the Distribution you are using, but should usually be documented there
|
||||
|
||||
### 2. Install required Packages
|
||||
|
||||
For Debian, this would be:
|
||||
|
||||
```shell
|
||||
export DEBIAN_FRONTEND="noninteractive"
|
||||
apt-get update
|
||||
apt-get upgrade -y
|
||||
apt-get install podman file -y
|
||||
apt-get install podman file postgres-y
|
||||
# Please check the official install instructions for your up to date minio Version
|
||||
wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio_20230816201730.0.0_amd64.deb -O minio.deb
|
||||
dpkg -i minio.deb
|
||||
|
@ -24,10 +29,19 @@ curl https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc
|
|||
chmod +x /usr/local/bin/mc
|
||||
```
|
||||
|
||||
Enable the podman socket for your user:
|
||||
|
||||
```shell
|
||||
systemctl --user enable --now podman.socket
|
||||
```
|
||||
|
||||
### 3. Configure Minio
|
||||
More advanced Information can be found in the [Minio Documentation](https://min.io/docs/minio/linux/operations/installation.html)
|
||||
|
||||
More advanced Information can be found in
|
||||
the [Minio Documentation](https://min.io/docs/minio/linux/operations/installation.html)
|
||||
|
||||
Example `/etc/default/minio`:
|
||||
|
||||
```shell
|
||||
# MINIO_ROOT_USER and MINIO_ROOT_PASSWORD sets the root account for the MinIO server.
|
||||
# This user has unrestricted permissions to perform S3 and administrative API operations on any resource in the deployment.
|
||||
|
@ -46,24 +60,34 @@ MINIO_VOLUMES="/var/lib/minio"
|
|||
|
||||
# Uncomment the following line and replace the value with the correct hostname for the local machine and port for the MinIO server (9000 by default).
|
||||
|
||||
#MINIO_SERVER_URL="http://minio.example.net:9000"
|
||||
MINIO_SERVER_URL="http://localhost:9000"
|
||||
MINIO_CONSOLE_PORT=9001
|
||||
```
|
||||
|
||||
#### 3.1. Create User
|
||||
|
||||
#### 3.2. Create Bucket
|
||||
|
||||
|
||||
### 4. Configure PostgreSQL
|
||||
|
||||
Example script to set up PostgreSQL for Filegate:
|
||||
*Slightly Jank (could have better permission management)*
|
||||
|
||||
```sql
|
||||
CREATE DATABASE filegate;
|
||||
CREATE user fgowner WITH PASSWORD 'supersecretpassword';
|
||||
CREATE user fgwriter WITH PASSWORD 'alsoverysecretpassword';
|
||||
CREATE USER fgreader WITH PASSWORD 'anothersecretpassword';
|
||||
GRANT ALL PRIVILEGES ON DATABASE filegate TO fgowner WITH GRANT OPTION ;
|
||||
GRANT ALL PRIVILEGES ON SCHEMA public TO fgowner WITH GRANT OPTION;
|
||||
CREATE
|
||||
DATABASE filegate;
|
||||
CREATE
|
||||
user filegate WITH PASSWORD 'supersecretpassword';
|
||||
-- CREATE user fgwriter WITH PASSWORD 'alsoverysecretpassword';
|
||||
--CREATE USER fgreader WITH PASSWORD 'anothersecretpassword';
|
||||
GRANT ALL PRIVILEGES ON DATABASE
|
||||
filegate TO filegate WITH GRANT OPTION;
|
||||
GRANT ALL PRIVILEGES ON SCHEMA
|
||||
public TO filegate WITH GRANT OPTION;
|
||||
```
|
||||
|
||||
If you have changed the usernames for this database, you will also need to change them in `pgsetup.sql`
|
||||
### 5. Install filegate itself
|
||||
```shell
|
||||
make
|
||||
sudo make install
|
||||
```
|
|
@ -0,0 +1,19 @@
|
|||
build: dependencies
|
||||
GOOS=linux go build -v -x -race -o filegate .
|
||||
|
||||
dependencies:
|
||||
go get .
|
||||
go mod download
|
||||
run: dependencies
|
||||
go run .
|
||||
install: build
|
||||
mkdir -P /etc/filegate
|
||||
mkdir -P /var/local/filegate/
|
||||
cp filegate /usr/local/bin/filegate
|
||||
cp filegate.service /etc/systemd/system/filegate.service
|
||||
useradd -d /var/local/filegate -m -U filegate
|
||||
chownr -R filegate:filegate /var/local/filegate
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now filegate
|
||||
clean:
|
||||
rm filegate
|
|
@ -8,7 +8,11 @@ manual analysis
|
|||
# Icon
|
||||
Based on https://pictogrammers.com/library/mdi/icon/gate/ and https://pictogrammers.com/library/mdi/icon/floppy/
|
||||
|
||||
|
||||
# AccessLevel
|
||||
0 = User // can add files, view status of files created by them, edit comments, download cleared files
|
||||
3 = Helpdesk // can see all files, full analysis of all files, can manually trigger steps
|
||||
5 = Analyst // can do everything with files, but not change the software settings
|
||||
10 = Admin // Can do literally anything
|
||||
|
||||
## Useful Links for Development
|
||||
https://chenyitian.gitbooks.io/gin-tutorials/content/gin/8.html // Go Templates
|
||||
|
|
34
config.yaml
34
config.yaml
|
@ -1,20 +1,20 @@
|
|||
serverhostname: filegate.dev.jmbit.de
|
||||
serverport: 80
|
||||
serverhostname: 0.0.0.0
|
||||
serverport: 8080
|
||||
loglevel: info
|
||||
scratchdisk: "/var/tmp/filegate"
|
||||
trustedproxies:
|
||||
- 127.0.0.1
|
||||
db:
|
||||
hostname: filegate.dev.jmbit.de
|
||||
port: 5432
|
||||
owner: fgowner
|
||||
ownerpassword: 0b26aca2-b10f-496e-9bd8-8bbfb143ad8b
|
||||
writer: fgwriter
|
||||
writerpassword: d5af6fde-7156-4ffa-ab0e-951df657990d
|
||||
reader: fgreader
|
||||
readerpassword: a788a837-f984-4b20-97db-b6dfd6b97a56
|
||||
hostname: localhost
|
||||
port: 5432
|
||||
name: filegate
|
||||
user: filegate
|
||||
password: 80f9859c-d2fe-4864-a962-a58adbdcb5f3
|
||||
minio:
|
||||
accesskeyid: lZvkgrfXNbEMye6BSf6s
|
||||
accesskeysecret: U109MtkE1jcc6qm3SIGk3IEZsq1cl8vTxqIRr3ZH
|
||||
hostname: filegate.dev.jmbit.de
|
||||
port: 9000
|
||||
bucket: filegate
|
||||
usessl: false
|
||||
location: filegate-local
|
||||
accesskeyid: lZvkgrfXNbEMye6BSf6s
|
||||
accesskeysecret: U109MtkE1jcc6qm3SIGk3IEZsq1cl8vTxqIRr3ZH
|
||||
hostname: 127.0.0.1
|
||||
port: 9000
|
||||
bucket: filegate
|
||||
usessl: false
|
||||
location: filegate-local
|
||||
|
|
|
@ -8,26 +8,36 @@ import (
|
|||
|
||||
var Cfg Config
|
||||
|
||||
func ParseConfig() {
|
||||
cfg := getDefaults()
|
||||
file, err := os.Open("config.yaml")
|
||||
if os.Getenv("GIN_MODE") == "release" {
|
||||
file, err = os.Open("/etc/filegate/config.yaml")
|
||||
func init() {
|
||||
ParseConfig()
|
||||
if os.Getenv("GIN_MODE") != "release" {
|
||||
log.Print(Cfg)
|
||||
}
|
||||
if err != nil && err.Error() == "open config.yaml: no such file or directory" {
|
||||
createDefaultConfig()
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
}
|
||||
}(file)
|
||||
func ParseConfig() {
|
||||
log.Println("Reading Config")
|
||||
cfg := getDefaults()
|
||||
configFile, err := os.ReadFile(findConfigFile())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(configFile, &cfg)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
err = os.Mkdir(cfg.ScratchDisk, 600) // Directory to temporarily store files on host system
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
Cfg = cfg
|
||||
}
|
||||
|
||||
func createDefaultConfig() {
|
||||
log.Println("Creating default config file")
|
||||
file, err := os.Create("config.yaml")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create default config file: %v", err)
|
||||
|
@ -44,3 +54,24 @@ func createDefaultConfig() {
|
|||
log.Fatalf("failed to encode default Config YAML: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks first for a config file in the current directory, then in /etc/filegate/
|
||||
func findConfigFile() string {
|
||||
log.Println("Finding Config file")
|
||||
if file, err := os.Stat("config.yaml"); err == nil {
|
||||
log.Println(file)
|
||||
return "config.yaml"
|
||||
} else if file, err = os.Stat("config.yml"); err == nil {
|
||||
log.Println(file)
|
||||
return "config.yml"
|
||||
} else if file, err = os.Stat("/etc/filegate/config.yaml"); err == nil {
|
||||
log.Println(file)
|
||||
return "/etc/filegate/config.yaml"
|
||||
} else if file, err = os.Stat("/etc/filegate/config.yml"); err == nil {
|
||||
log.Println(file)
|
||||
return "/etc/filegate/config.yml"
|
||||
} else {
|
||||
createDefaultConfig()
|
||||
return "config.yaml"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@ func getDefaults() Config {
|
|||
ServerHostname: "localhost",
|
||||
ServerPort: 8080,
|
||||
LogLevel: "info",
|
||||
ScratchDisk: "/var/tmp/filegate",
|
||||
SessionSecret: uuid.NewString(),
|
||||
TrustedProxies: []string{"127.0.0.1"},
|
||||
DB: struct {
|
||||
Hostname string
|
||||
Port int
|
||||
|
|
|
@ -4,6 +4,9 @@ type Config struct {
|
|||
ServerHostname string
|
||||
ServerPort int
|
||||
LogLevel string
|
||||
ScratchDisk string
|
||||
SessionSecret string
|
||||
TrustedProxies []string
|
||||
DB struct {
|
||||
Hostname string
|
||||
Port int
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func CreateFile(name string, url string, comment string, blob string) (uint, error) {
|
||||
file := File{
|
||||
Name: name,
|
||||
|
@ -31,6 +37,7 @@ func SetSimpleAttributes(id uint, mime string, size int64, sha256 string, sha1 s
|
|||
file.Properties.Md5 = md5
|
||||
file.Properties.FileCmd = fileCmd
|
||||
file.Properties.Extension = extension
|
||||
DB.Save(&file)
|
||||
}
|
||||
|
||||
func CountFilesEntries() int64 {
|
||||
|
@ -58,11 +65,31 @@ func GetFileBlob(id uint) string {
|
|||
return file.Blob
|
||||
}
|
||||
|
||||
func GetFileList(page int, count int) []File {
|
||||
// TODO Implement paged queries to DB
|
||||
_ = page
|
||||
_ = count
|
||||
func GetFileList(page int, count int, c *gin.Context) ([]File, error) {
|
||||
query, err := searchQueryBuilder(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []File
|
||||
DB.Find(&list)
|
||||
return list
|
||||
DB.Limit(page * count).Where(query).Find(&list)
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func searchQueryBuilder(c *gin.Context) (*File, error) {
|
||||
// TODO Add parsing for all File properties
|
||||
var query File
|
||||
if c.Query("id") != "" {
|
||||
id, err := strconv.ParseUint(c.Query("id"), 10, 0)
|
||||
if err != nil {
|
||||
log.Printf("Could not Convert ID from filter to int: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
query.ID = uint(id)
|
||||
}
|
||||
if c.Query("name") != "" {
|
||||
query.Name = c.Query("name")
|
||||
}
|
||||
|
||||
return &query, nil
|
||||
}
|
||||
|
|
|
@ -59,6 +59,27 @@ type User struct {
|
|||
Name string `gorm:"unique"`
|
||||
DisplayName string
|
||||
Email string
|
||||
Oauth2Sub string
|
||||
Role string
|
||||
PassHash string
|
||||
// Oauth2Sub string
|
||||
Role UserRole
|
||||
}
|
||||
|
||||
type UserRole struct {
|
||||
gorm.Model
|
||||
ID uint
|
||||
Name string
|
||||
AccessLevel int
|
||||
}
|
||||
|
||||
type Pod struct {
|
||||
gorm.Model
|
||||
ID string
|
||||
Name string
|
||||
Type PodType
|
||||
BoundPorts []int
|
||||
}
|
||||
|
||||
type PodType struct {
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package database
|
|||
import (
|
||||
"fmt"
|
||||
"git.jmbit.de/filegate/filegate/config"
|
||||
gormsessions "github.com/gin-contrib/sessions/gorm"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
|
@ -10,13 +11,15 @@ import (
|
|||
|
||||
var dbconf = config.Cfg.DB
|
||||
var DB *gorm.DB
|
||||
var SessionStore gormsessions.Store
|
||||
|
||||
func Connect() {
|
||||
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable",
|
||||
dbconf.Hostname, dbconf.User, dbconf.Password, dbconf.Name)
|
||||
dbconf.Hostname, dbconf.User, dbconf.Password, dbconf.Name, dbconf.Port)
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("Can't connect to DB: %v", err)
|
||||
}
|
||||
DB = db
|
||||
SessionStore = gormsessions.NewStore(db, true, []byte(config.Cfg.SessionSecret))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package database
|
||||
|
||||
import "errors"
|
||||
|
||||
func CreateUser(name string, displayName string, email string, passHash string, role UserRole) error {
|
||||
user := User{
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
Email: email,
|
||||
PassHash: passHash,
|
||||
Role: role,
|
||||
}
|
||||
|
||||
if err := DB.Create(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteUser(name string) error {
|
||||
var user User
|
||||
DB.First(&user, name)
|
||||
if user.Name == "" {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
DB.Delete(&user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdatePassword(name string, passHash string) error {
|
||||
var user User
|
||||
DB.First(&user, name)
|
||||
if user.Name == "" {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
user.PassHash = passHash
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateEmail(name string, email string) error {
|
||||
var user User
|
||||
DB.First(&user, name)
|
||||
if user.Name == "" {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
user.Email = email
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUser(name string) (User, error) {
|
||||
var user User
|
||||
DB.First(&user, name)
|
||||
if user.Name == "" {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
return user, nil
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Enterprise Download Management Portal
|
||||
After=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=filegate
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
PrivateTmp=yes
|
||||
ExecStart=/usr/local/bin/filegate
|
||||
ProtectSystem=strict
|
||||
InaccessiblePaths=/root /home
|
||||
NoNewPrivileges=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -2,29 +2,12 @@ package files
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.jmbit.de/filegate/filegate/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func StorageDirectory() {
|
||||
if _, err := os.Stat("./filesstore"); os.IsNotExist(err) {
|
||||
err := os.Mkdir("./filestore", 770)
|
||||
if err != nil {
|
||||
log.Println(fmt.Sprintf("Could not create File directory: %s", err))
|
||||
}
|
||||
}
|
||||
if os.Getenv("GIN_MODE") != "release" {
|
||||
err := os.RemoveAll("./filestore/*")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UploadFile(id uint, blob string, c *gin.Context) error {
|
||||
ctx := context.Background()
|
||||
file, err := c.FormFile("file")
|
||||
|
@ -42,3 +25,11 @@ func UploadFile(id uint, blob string, c *gin.Context) error {
|
|||
go RunStaticAnalysis(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFile returns a pointer to a File stored in Minio by passing it the UUID ("Blob"-ID) of the file
|
||||
func GetFile(uuid string) (*minio.Object, error) {
|
||||
ctx := context.Background()
|
||||
object, err := MinioClient.GetObject(ctx, minioConfig.Bucket, uuid, minio.GetObjectOptions{})
|
||||
|
||||
return object, err
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ var MinioClient = minioConnect()
|
|||
|
||||
func minioConnect() *minio.Client {
|
||||
endpoint := fmt.Sprintf("%s:%d", minioConfig.Hostname, minioConfig.Port)
|
||||
log.Printf("Minio Endpoint: %s", endpoint)
|
||||
accessKeyID := minioConfig.AccessKeyID
|
||||
accessKeySecret := minioConfig.AccessKeySecret
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"git.jmbit.de/filegate/filegate/config"
|
||||
"git.jmbit.de/filegate/filegate/database"
|
||||
"io"
|
||||
"log"
|
||||
|
@ -70,9 +71,40 @@ func fileSize(path string) int64 {
|
|||
return filesize
|
||||
}
|
||||
|
||||
func RunStaticAnalysis(id uint) {
|
||||
//TODO: Use container instead of running on local disk
|
||||
|
||||
filepath := fmt.Sprintf("./filestore/%s", id)
|
||||
// RunStaticAnalysis populates the File Properties Table for a given file
|
||||
func RunStaticAnalysis(id uint) {
|
||||
file := database.GetFileByID(id)
|
||||
|
||||
filepath := fmt.Sprintf("%s/%d/%s", config.Cfg.ScratchDisk, file.ID, file.Properties.OriginalName)
|
||||
err := os.Mkdir(fmt.Sprintf("%s/%d", config.Cfg.ScratchDisk, file.ID), 700)
|
||||
if err != nil {
|
||||
|
||||
log.Printf("Error Creating analysis directory, %v", err)
|
||||
return
|
||||
}
|
||||
fileObject, err := GetFile(file.Blob)
|
||||
if err != nil {
|
||||
log.Printf("Error getting file from S3, %v", err)
|
||||
fileObject.Close()
|
||||
return
|
||||
}
|
||||
localFile, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
log.Printf("Error Creating analysis file, %v", err)
|
||||
fileObject.Close()
|
||||
localFile.Close()
|
||||
return
|
||||
}
|
||||
if _, err = io.Copy(localFile, fileObject); err != nil {
|
||||
log.Printf("Could not Copy S3 File to local disk: %v", err)
|
||||
fileObject.Close()
|
||||
localFile.Close()
|
||||
return
|
||||
}
|
||||
fileObject.Close()
|
||||
localFile.Close()
|
||||
mimeType := mimeType(filepath)
|
||||
fileCmd := fileCmd(filepath)
|
||||
fileExtension := fileExtension(filepath)
|
||||
|
|
8
go.mod
8
go.mod
|
@ -4,10 +4,11 @@ go 1.20
|
|||
|
||||
require (
|
||||
github.com/containers/podman/v4 v4.6.0
|
||||
github.com/gin-contrib/sessions v0.0.5
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
github.com/minio/minio-go/v7 v7.0.62
|
||||
golang.org/x/crypto v0.12.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/postgres v1.5.2
|
||||
gorm.io/gorm v1.25.4
|
||||
|
@ -71,8 +72,11 @@ require (
|
|||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-containerregistry v0.15.2 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/schema v1.2.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/sessions v1.2.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
|
@ -134,12 +138,12 @@ require (
|
|||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.4.0 // indirect
|
||||
github.com/wader/gormstore/v2 v2.0.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.3 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
|
|
23
main.go
23
main.go
|
@ -1,3 +1,5 @@
|
|||
//go:build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -5,13 +7,13 @@ import (
|
|||
"fmt"
|
||||
"git.jmbit.de/filegate/filegate/config"
|
||||
"git.jmbit.de/filegate/filegate/database"
|
||||
"git.jmbit.de/filegate/filegate/files"
|
||||
"git.jmbit.de/filegate/filegate/pods"
|
||||
"git.jmbit.de/filegate/filegate/web"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
//go:embed assets/jquery.min.js assets/custom.css assets/bootstrap/css/*.min.css assets/bootstrap/js/bootstrap.min.js assets/favicon.ico assets/logo.svg
|
||||
|
@ -21,29 +23,19 @@ var assetsFS embed.FS
|
|||
var templatesFS embed.FS
|
||||
|
||||
func main() {
|
||||
if runtime.GOOS == "windows" {
|
||||
fmt.Println("This Software is not supported on Windows")
|
||||
}
|
||||
config.ParseConfig()
|
||||
addr := fmt.Sprintf("%s:%d", config.Cfg.ServerHostname, config.Cfg.ServerPort)
|
||||
database.Connect()
|
||||
// podthing.TestPodman()
|
||||
files.StorageDirectory()
|
||||
pods.TestPodman()
|
||||
router := gin.Default()
|
||||
templates := template.Must(template.New("").ParseFS(templatesFS, "templates/*.gohtml"))
|
||||
router.ForwardedByClientIP = true
|
||||
err := router.SetTrustedProxies([]string{"127.0.0.1"})
|
||||
router.Use(sessions.Sessions("mySession", database.SessionStore))
|
||||
err := router.SetTrustedProxies(config.Cfg.TrustedProxies)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
router.SetHTMLTemplate(templates)
|
||||
router.StaticFS("/static", http.FS(assetsFS))
|
||||
router.GET("/", web.GetIndex)
|
||||
router.GET("/file/:id", web.GetFileView)
|
||||
router.GET("/file/", web.GetFileList)
|
||||
router.POST("/file/new", web.PostSubmitFileUrl)
|
||||
router.POST("/file/upload", web.PostUploadFile)
|
||||
router.GET("/browser/:id/")
|
||||
router.GET("favicon.ico", func(c *gin.Context) {
|
||||
file, _ := assetsFS.ReadFile("assets/favicon.ico")
|
||||
c.Data(
|
||||
|
@ -52,6 +44,7 @@ func main() {
|
|||
file,
|
||||
)
|
||||
})
|
||||
router = web.SetRoutes(router)
|
||||
err = router.Run(addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
62
pgsetup.sql
62
pgsetup.sql
|
@ -1,62 +0,0 @@
|
|||
DROP TABLE IF EXISTS status CASCADE ;
|
||||
DROP TABLE IF EXISTS file CASCADE ;
|
||||
DROP TABLE IF EXISTS file_properties CASCADE ;
|
||||
DROP TABLE IF EXISTS dynamic_analysis CASCADE ;
|
||||
DROP TABLE IF EXISTS file_attachment CASCADE ;
|
||||
-- DROP all tables (Only for development purposes
|
||||
|
||||
CREATE TABLE IF NOT EXISTS status
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
name VARCHAR(64),
|
||||
comment VARCHAR(2048)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS file_properties
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
sha256 VARCHAR(64),
|
||||
sha1 VARCHAR(40),
|
||||
md5 VARCHAR(32),
|
||||
extension VARCHAR(80),
|
||||
file_cmd VARCHAR(512),
|
||||
original_name VARCHAR(256),
|
||||
url VARCHAR(2048),
|
||||
size BIGINT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS file
|
||||
(
|
||||
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(64),
|
||||
mime VARCHAR(256),
|
||||
comment VARCHAR(2048),
|
||||
status INT,
|
||||
created BIGINT,
|
||||
properties INT,
|
||||
FOREIGN KEY (status)
|
||||
REFERENCES status (id),
|
||||
FOREIGN KEY (properties)
|
||||
REFERENCES file_properties (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS file_attachment
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
type VARCHAR(32),
|
||||
comment VARCHAR(2048),
|
||||
file_id VARCHAR(36),
|
||||
FOREIGN KEY (file_id)
|
||||
REFERENCES file (id)
|
||||
);
|
||||
|
||||
GRANT INSERT, UPDATE, DELETE ON file, file_attachment, file_properties, status TO fgwriter;
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO fgreader;
|
||||
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES (0, 'New', 'Freshly uploaded file');
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES (1, 'Processing', 'File is now being analyzed');
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES (2, 'Denied', 'File download was denied');
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES (3, 'Granted', 'File download was granted');
|
|
@ -0,0 +1,47 @@
|
|||
package pods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.jmbit.de/filegate/filegate/database"
|
||||
"git.jmbit.de/filegate/filegate/files"
|
||||
"github.com/containers/podman/v4/pkg/bindings/containers"
|
||||
"github.com/containers/podman/v4/pkg/bindings/images"
|
||||
"github.com/containers/podman/v4/pkg/specgen"
|
||||
"log"
|
||||
)
|
||||
|
||||
type AnalysisContainer struct {
|
||||
UUID string
|
||||
containerFile string
|
||||
fileID uint
|
||||
}
|
||||
|
||||
// CreateAutoAnalysisContainer creates a Static analysis Container with the file
|
||||
// returns the UUID of the container and the path of the file
|
||||
func CreateAutoAnalysisContainer(fileID uint) AnalysisContainer {
|
||||
image := "git.jmbit.de/filegate/utility-containers:staticanalysis"
|
||||
conn := socketConnection()
|
||||
_, err := images.Pull(conn, image, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
s := specgen.NewSpecGenerator(image, false)
|
||||
createResponse, err := containers.CreateWithSpec(conn, s, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if err := containers.Start(conn, createResponse.ID, nil); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
file := database.GetFileByID(fileID)
|
||||
fileObject, err := files.GetFile(file.Blob)
|
||||
_, err = containers.CopyFromArchive(conn, createResponse.ID, fmt.Sprintf("/mnt/%s", file.Properties.OriginalName), fileObject)
|
||||
if err != nil {
|
||||
return AnalysisContainer{}
|
||||
}
|
||||
return AnalysisContainer{
|
||||
UUID: createResponse.ID,
|
||||
containerFile: fmt.Sprintf("/mnt/%s", file.Properties.OriginalName),
|
||||
fileID: file.ID,
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package podthing
|
||||
package pods
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v4/pkg/bindings/containers"
|
|
@ -0,0 +1,24 @@
|
|||
package pods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/containers/podman/v4/pkg/bindings"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Socket = socketConnection()
|
||||
|
||||
func socketConnection() context.Context {
|
||||
uri := fmt.Sprintf("unix:///run/user/%d/podman/podman.sock", os.Getuid())
|
||||
conn, err := bindings.NewConnection(context.Background(), uri)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
func TestPodman() {
|
||||
_ = socketConnection()
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package podthing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/containers/podman/v4/pkg/bindings"
|
||||
"github.com/containers/podman/v4/pkg/bindings/containers"
|
||||
"github.com/containers/podman/v4/pkg/bindings/images"
|
||||
"github.com/containers/podman/v4/pkg/specgen"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func socketConnection() context.Context {
|
||||
uri := fmt.Sprintf("unix:///run/user/%d/podman/podman.sock", os.Getuid())
|
||||
conn, err := bindings.NewConnection(context.Background(), uri)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
func TestPodman() {
|
||||
conn := socketConnection()
|
||||
|
||||
_, err := images.Pull(conn, "quay.io/libpod/alpine_nginx", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s := specgen.NewSpecGenerator("quay.io/libpod/alpine_nginx", false)
|
||||
s.Name = "fg-test"
|
||||
createResponse, err := containers.CreateWithSpec(conn, s, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := containers.Start(conn, createResponse.ID, nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := containers.Kill(conn, createResponse.ID, nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err, _ := containers.Remove(conn, createResponse.ID, nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
DROP TABLE IF EXISTS status;
|
||||
DROP TABLE IF EXISTS files;
|
||||
CREATE TABLE IF NOT EXISTS status (
|
||||
id INTEGER PRIMARY KEY ,
|
||||
name TEXT,
|
||||
comment TEXT);
|
||||
CREATE TABLE IF NOT EXISTS files (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
url TEXT NOT NULL,
|
||||
-- blob TEXT,
|
||||
mime TEXT,
|
||||
original_name TEXT,
|
||||
comment TEXT,
|
||||
size INTEGER,
|
||||
sha256 TEXT,
|
||||
sha1 TEXT,
|
||||
md5 TEXT,
|
||||
status_id INTEGER,
|
||||
file_cmd TEXT,
|
||||
created INTEGER,
|
||||
extension TEXT,
|
||||
FOREIGN KEY (status_id)
|
||||
REFERENCES status (id)
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE NO ACTION
|
||||
);
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES(0, 'New', 'Freshly uploaded file');
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES(1, 'Processing', '');
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES(2, 'Denied', 'File download was denied');
|
||||
INSERT INTO status (id, name, comment)
|
||||
VALUES(3, 'Granted', 'File download was granted');
|
|
@ -2,11 +2,19 @@
|
|||
<div class="starter-template">
|
||||
<div class="page-header">
|
||||
<h1>Files</h1>
|
||||
<p>List of all files and their Status</p>
|
||||
<p>List of all files and their Status. Currently, can only search for ID and Name</p>
|
||||
<p>Total Files: {{.totalEntries}}</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<form action="/file/" method="GET">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" name="name" id="name" class="form-control" required>
|
||||
<label for="id">ID</label>
|
||||
<input type="number" name="id" id="id" class="form-control" required>
|
||||
<input type="submit" value="Search" class="btn btn-primary">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="pagination"></div>
|
||||
<table class="table table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
@ -35,43 +43,5 @@
|
|||
</table>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
// Generate pagination
|
||||
function generatePagination(itemsPerPage, totalItems) {
|
||||
|
||||
// Calculate total pages
|
||||
let totalPages = Math.ceil(totalItems / itemsPerPage);
|
||||
|
||||
let paginationHtml = '';
|
||||
|
||||
// Loop to generate page links
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
paginationHtml += `
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="#">${i}</a>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
|
||||
// Output pagination
|
||||
$('#pagination').html(paginationHtml);
|
||||
|
||||
}
|
||||
|
||||
// Usage
|
||||
let itemsPerPage = {{.count}};
|
||||
let totalItems = {{.totalEntries}};
|
||||
|
||||
generatePagination(itemsPerPage, totalItems);
|
||||
$('#pagination').on('click', 'a', function(e){
|
||||
|
||||
// Get clicked page
|
||||
let page = $(this).text();
|
||||
|
||||
// Load data for page
|
||||
loadProducts(page);
|
||||
|
||||
e.preventDefault();
|
||||
})
|
||||
</script>
|
||||
{{template "footer.gohtml"}}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<footer class="footer">
|
||||
<p>Copyright © Johannnes Bülow <a href="https://www.jmbit.de/">www.jmbit.de</a></p>
|
||||
|
||||
<p>Copyright © Johannes Bülow <a href="https://www.jmbit.de/">www.jmbit.de</a></p>
|
||||
</footer>
|
||||
|
||||
</div> <!-- End of main container -->
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{{template "header.gohtml"}}
|
||||
<div class="starter-template">
|
||||
<div class="page-header">
|
||||
<h1>{{ .title }}</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Login</h2>
|
||||
<div class="form-group">
|
||||
<form action="/login" method="POST">
|
||||
<label for="name">Enter your Username</label>
|
||||
<input type="text" name="name" id="name" class="form-control" required>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password" class="form-control" required>
|
||||
<input type="submit" value="Login" class="btn btn-primary">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "footer.gohtml"}}
|
|
@ -0,0 +1,57 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"git.jmbit.de/filegate/filegate/database"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// adapted from https://www.gregorygaines.com/blog/how-to-properly-hash-and-salt-passwords-in-golang-bcrypt/
|
||||
// hashPassword Hash password using bcrypt and
|
||||
func hashPassword(password string) (string, error) {
|
||||
// Convert password string to byte slice
|
||||
var passwordBytes = []byte(password)
|
||||
// Hash password with Bcrypt's default cost
|
||||
hashedPasswordBytes, err := bcrypt.
|
||||
GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
|
||||
|
||||
return string(hashedPasswordBytes), err
|
||||
|
||||
}
|
||||
|
||||
// doPasswordsMatch Check if two passwords match using Bcrypt's CompareHashAndPassword
|
||||
// which return nil on success and an error on failure.
|
||||
func doPasswordsMatch(hashedPassword, currPassword string) bool {
|
||||
err := bcrypt.CompareHashAndPassword(
|
||||
[]byte(hashedPassword), []byte(currPassword))
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func storePasswordHash(username string, hash string) error {
|
||||
err := database.UpdatePassword(username, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkPassword checks if the password correctly correlates to the password hash stored in the Database
|
||||
func checkPassword(username string, password string, session sessions.Session) error {
|
||||
var user database.User
|
||||
user, err := database.GetUser(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if doPasswordsMatch(user.PassHash, password) {
|
||||
session.Set("username", username)
|
||||
session.Set("accessLevel", user.Role.AccessLevel)
|
||||
session.Set("isLoggedIn", true)
|
||||
err := session.Save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetLogin(c *gin.Context) {
|
||||
content := gin.H{
|
||||
"title": "Home",
|
||||
}
|
||||
c.HTML(http.StatusOK, "index.gohtml", content)
|
||||
}
|
||||
|
||||
func PostLogin(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
name := c.PostForm("name")
|
||||
password := c.PostForm("password")
|
||||
err := checkPassword(name, password, session)
|
||||
if err != nil {
|
||||
c.HTML(http.StatusBadRequest, "error.gohtml", gin.H{"title": "Error", "message": err})
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// AuthMiddleware deals with checking authentication and authorization (Is the user logged in and permitted to see/do something)
|
||||
func AuthMiddleware(requiredLevel int) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
isLoggedIn := session.Get("isLoggedIn")
|
||||
accessLevel := session.Get("accessLevel")
|
||||
if isLoggedIn != true {
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
// Not logged in, abort
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if accessLevelValue, ok := accessLevel.(int); ok {
|
||||
if accessLevelValue < requiredLevel {
|
||||
c.HTML(http.StatusUnauthorized, "error.gohtml", gin.H{"title": "Not authorized", "message": "You are not authorized to do this action"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
// Logged in and authorized, continue
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
package web
|
||||
package browser
|
||||
|
||||
import (
|
||||
"git.jmbit.de/filegate/filegate/podthing"
|
||||
"git.jmbit.de/filegate/filegate/pods"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func NewBrowser() (string, string, string) {
|
||||
passwd := uuid.NewString()
|
||||
folderId := uuid.NewString()
|
||||
browserPod := podthing.CreateDownloadContainer("user", passwd, folderId)
|
||||
browserPod := pods.CreateDownloadContainer("user", passwd, folderId)
|
||||
return browserPod, folderId, passwd
|
||||
}
|
|
@ -17,14 +17,17 @@ func GetFileList(c *gin.Context) {
|
|||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
totalEntries := database.CountFilesEntries()
|
||||
fileList := database.GetFileList(page, count)
|
||||
fileList, err := database.GetFileList(page, count, c)
|
||||
lowestEntry := (page - 1) * count
|
||||
highestEntry := page * count
|
||||
fileListPage := fileList[lowestEntry:highestEntry]
|
||||
|
||||
content := gin.H{
|
||||
"fileList": fileList,
|
||||
"error": "",
|
||||
"fileList": fileListPage,
|
||||
"error": err,
|
||||
"page": page,
|
||||
"count": count,
|
||||
"totalEntries": totalEntries,
|
||||
"totalEntries": len(fileList),
|
||||
}
|
||||
c.HTML(http.StatusOK, "filelist.gohtml", content)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"git.jmbit.de/filegate/filegate/web/auth"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetRoutes(router *gin.Engine) *gin.Engine {
|
||||
router.GET("/", GetIndex)
|
||||
router.GET("/file/:id", GetFileView)
|
||||
router.GET("/file/", GetFileList)
|
||||
router.POST("/file/new", PostSubmitFileUrl)
|
||||
router.POST("/file/upload", PostUploadFile)
|
||||
router.GET("/browser/:id/")
|
||||
router.GET("/login", auth.GetLogin)
|
||||
return router
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package web
|
Loading…
Reference in New Issue