added login page and authentication middleware, altought not yet implemented
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				ci/woodpecker/push/woodpecker Pipeline failed
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	ci/woodpecker/push/woodpecker Pipeline failed
				
			This commit is contained in:
		
							parent
							
								
									f6ab0bb4e2
								
							
						
					
					
						commit
						ddc95f49cd
					
				
					 39 changed files with 772 additions and 5911 deletions
				
			
		
							
								
								
									
										2
									
								
								.idea/dataSources.local.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/dataSources.local.xml
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5182
									
								
								.idea/dataSources/28e7aa7f-2fe3-45ee-997f-c35496a4a3eb.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										5182
									
								
								.idea/dataSources/28e7aa7f-2fe3-45ee-997f-c35496a4a3eb.xml
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										7
									
								
								.idea/sqldialects.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								.idea/sqldialects.xml
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -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>
 | 
			
		||||
							
								
								
									
										133
									
								
								.idea/workspace.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										133
									
								
								.idea/workspace.xml
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										19
									
								
								Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Makefile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								config.yaml
									
										
									
									
									
								
							
							
						
						
									
										20
									
								
								config.yaml
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,19 +1,19 @@
 | 
			
		|||
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
 | 
			
		||||
  hostname: localhost
 | 
			
		||||
  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
 | 
			
		||||
  name: filegate
 | 
			
		||||
  user: filegate
 | 
			
		||||
  password: 80f9859c-d2fe-4864-a962-a58adbdcb5f3
 | 
			
		||||
minio:
 | 
			
		||||
  accesskeyid: lZvkgrfXNbEMye6BSf6s
 | 
			
		||||
  accesskeysecret: U109MtkE1jcc6qm3SIGk3IEZsq1cl8vTxqIRr3ZH
 | 
			
		||||
    hostname: filegate.dev.jmbit.de
 | 
			
		||||
  hostname: 127.0.0.1
 | 
			
		||||
  port: 9000
 | 
			
		||||
  bucket: filegate
 | 
			
		||||
  usessl: false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
	}
 | 
			
		||||
	}(file)
 | 
			
		||||
 | 
			
		||||
	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))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										57
									
								
								database/user.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								database/user.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								filegate.service
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								filegate.service
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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');
 | 
			
		||||
							
								
								
									
										47
									
								
								pods/autoAnalysis.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								pods/autoAnalysis.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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"
 | 
			
		||||
							
								
								
									
										24
									
								
								pods/podmanager.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								pods/podmanager.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								templates/login.gohtml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								templates/login.gohtml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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"}}
 | 
			
		||||
							
								
								
									
										57
									
								
								web/auth/hashPassword.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								web/auth/hashPassword.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								web/auth/login.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								web/auth/login.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								web/auth/middleware.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								web/auth/middleware.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								web/routes.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								web/routes.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								web/session.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/session.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
package web
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue