Skip to content

Commit

Permalink
Refactoring, added ui elements to setup, TODO: assign user id for upl…
Browse files Browse the repository at this point in the history
…oad, process new setup input
  • Loading branch information
Forceu committed Dec 30, 2024
1 parent 115168d commit c316a4f
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 87 deletions.
1 change: 1 addition & 0 deletions cmd/gokapi/Main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func main() {
if !reconfigureServer(passedFlags) {
configuration.ConnectDatabase()
}
configuration.CreateAdminUserIfNoneExists()
setDeploymentPassword(passedFlags)
encryption.Init(*configuration.Get())
authentication.Init(configuration.Get().Authentication)
Expand Down
25 changes: 25 additions & 0 deletions internal/configuration/Configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ func ConnectDatabase() {
database.Upgrade()
}

func CreateAdminUserIfNoneExists() {
var adminName string
switch serverSettings.Authentication.Method {
case models.AuthenticationDisabled:
return
case models.AuthenticationInternal:
adminName = serverSettings.Authentication.Username
case models.AuthenticationOAuth2:
adminName = serverSettings.Authentication.OAuthAdminUser
case models.AuthenticationHeader:
adminName = serverSettings.Authentication.HeaderAdminUser
}
users := database.GetAllUsers()
if len(users) == 0 {
user := models.User{
Name: adminName,
Email: adminName,
Permissions: models.UserPermissionAll,
UserLevel: models.UserLevelSuperAdmin,
Password: serverSettings.Authentication.Password,
}
database.SaveUser(user, true)
}
}

// UsesHttps returns true if Gokapi URL is set to a secure URL
func UsesHttps() bool {
return usesHttps
Expand Down
44 changes: 22 additions & 22 deletions internal/configuration/configupgrade/Upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// CurrentConfigVersion is the version of the configuration structure. Used for upgrading
const CurrentConfigVersion = 21
const CurrentConfigVersion = 22

// DoUpgrade checks if an old version is present and updates it to the current version if required
func DoUpgrade(settings *models.Configuration, env *environment.Environment) bool {
Expand All @@ -23,31 +23,31 @@ func DoUpgrade(settings *models.Configuration, env *environment.Environment) boo

// Upgrades the settings if saved with a previous version
func updateConfig(settings *models.Configuration, env *environment.Environment) {

// < v1.8.0
if settings.ConfigVersion < 16 {
fmt.Println("Please update to version 1.8 before running this version,")
// < v1.9.0
if settings.ConfigVersion < 21 {
fmt.Println("Please update to version 1.9.6 before running this version.")
osExit(1)
return
}
// < v1.8.2
if settings.ConfigVersion < 18 {
if len(settings.Authentication.OAuthUsers) > 0 {
settings.Authentication.OAuthUserScope = "email"
}
settings.Authentication.OAuthRecheckInterval = 168
}
// < v1.8.5beta
if settings.ConfigVersion < 19 {
if settings.MaxMemory == 40 {
settings.MaxMemory = 50
// < v2.0.0
if settings.ConfigVersion < 22 {
if settings.Authentication.Method == models.AuthenticationOAuth2 || settings.Authentication.Method == models.AuthenticationHeader {
adminUser := os.Getenv("GOKAPI_ADMIN_USER")
if adminUser == "" {
fmt.Println("FAILED UPDATE")
fmt.Println("--> If using Oauth or Header authentication, please set the env variable GOKAPI_ADMIN_USER to the value of the expected user name / email")
fmt.Println("--> See the release notes for more information")
osExit(1)
return
} else {
fmt.Println("Setting admin user to " + adminUser)
if settings.Authentication.Method == models.AuthenticationOAuth2 {
settings.Authentication.OAuthAdminUser = adminUser
} else {
settings.Authentication.HeaderAdminUser = adminUser
}
}
}
settings.ChunkSize = env.ChunkSizeMB
settings.MaxParallelUploads = env.MaxParallelUploads
}
// < v1.9.0
if settings.ConfigVersion < 21 {
settings.DatabaseUrl = "sqlite://" + env.DataDir + "/" + env.DatabaseName
}
}

Expand Down
3 changes: 3 additions & 0 deletions internal/configuration/database/Database.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ func GetUserByEmail(email string) (models.User, bool) {

// SaveUser saves a user to the database. If isNewUser is true, a new Id will be generated
func SaveUser(user models.User, isNewUser bool) {
if user.Email == "" {
panic("email cannot be empty")
}
user.Email = strings.ToLower(user.Email)
db.SaveUser(user, isNewUser)
}
Expand Down
11 changes: 10 additions & 1 deletion internal/configuration/setup/Setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ var password string

// debugDisableAuth can be set to true for testing purposes. It will disable the
// password requirement for accessing the setup page
const debugDisableAuth = false
const debugDisableAuth = true

// RunIfFirstStart checks if config files exist and if not start a blocking webserver for setup
func RunIfFirstStart() {
Expand Down Expand Up @@ -412,6 +412,11 @@ func parseOAuthSettings(result *models.Configuration, formObjects *[]jsonFormObj
return err
}

result.Authentication.OAuthAdminUser, err = getFormValueString(formObjects, "oauth_admin_user")
if err != nil {
return err
}

oauthAllowedUsers, err := getFormValueString(formObjects, "oauth_allowed_users")
if err != nil {
return err
Expand Down Expand Up @@ -453,6 +458,10 @@ func parseHeaderAuthSettings(result *models.Configuration, formObjects *[]jsonFo
if err != nil {
return err
}
result.Authentication.HeaderAdminUser, err = getFormValueString(formObjects, "auth_header_admin")
if err != nil {
return err
}

headerAllowedUsers, err := getFormValueString(formObjects, "auth_header_users")
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/configuration/setup/Setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ var config = models.Configuration{
func TestToConfiguration(t *testing.T) {
output, cloudConfig, _, err := toConfiguration(&jsonForms)
test.IsNil(t, err)
test.IsEqualInt(t, output.Authentication.Method, authentication.Internal)
test.IsEqualInt(t, output.Authentication.Method, authentication.TypeInternal)
test.IsEqualString(t, cloudConfig.Aws.KeyId, "testapi")
test.IsEqualString(t, output.Authentication.Username, "admin")
test.IsNotEqualString(t, output.Authentication.Password, "adminadmin")
Expand Down
46 changes: 28 additions & 18 deletions internal/configuration/setup/templates/setup.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
</div>
<!-- Step 1 DB Location -->
<div class="wizard-card wizard-card-overlay" data-cardname="dblocation">
<h3>Database</h3>
<h3 style="display:none">Database</h3>

<div class="wizard-input-section">
<div class="form-group">
Expand Down Expand Up @@ -142,7 +142,7 @@

<!-- Step 2 Webserver -->
<div class="wizard-card wizard-card-overlay" data-cardname="webserver1">
<h3>Webserver 1/2</h3>
<h3 style="display:none">Webserver 1/2</h3>

<div class="wizard-input-section">
<div class="form-group">
Expand Down Expand Up @@ -182,7 +182,7 @@

<!-- Step 3 Webserver 2 -->
<div class="wizard-card wizard-card-overlay" data-cardname="webserver2">
<h3>Webserver 2/2</h3>
<h3 style="display:none">Webserver 2/2</h3>

<div class="wizard-input-section">
<div class="form-group">
Expand Down Expand Up @@ -222,7 +222,7 @@

<!-- Step 4 Auth -->
<div class="wizard-card wizard-card-overlay" data-cardname="authentication">
<h3>Authentication</h3>
<h3 style="display:none">Authentication</h3>

<div class="wizard-input-section">
<div class="form-group">
Expand All @@ -238,7 +238,7 @@
</optgroup>

<optgroup label="Reverse Proxy">
<option value="2">Header Authentication</option>
<option value="2">Trusted Header Authentication</option>
<option value="3">Disabled / Access Restriction</option>
</optgroup>

Expand All @@ -249,7 +249,7 @@

<!-- Step 5a Credentials PW -->
<div class="wizard-card wizard-card-overlay" data-cardname="credentials">
<h3>Credentials</h3>
<h3 style="display:none">Credentials</h3>


<div class="wizard-input-section">
Expand All @@ -275,7 +275,7 @@

<!-- Step 5b Credentials Oauth -->
<div class="wizard-card wizard-card-overlay" data-cardname="credentials-oauth">
<h3>Credentials</h3>
<h3 style="display:none">Credentials</h3>


<div class="wizard-input-section">
Expand All @@ -298,6 +298,10 @@
<label for="oauth_secret">Client Secret:</label>
<input type="text" class="form-control" id="oauth_secret" name="oauth_secret" placeholder="Client Secret" data-min="1" required data-validate="validateMinLength">
</div>
<div class="col-sm-8" style="width:90%">
<label for="oauth_admin_user">Admin email address:</label>
<input type="text" class="form-control" id="oauth_admin_user" name="oauth_admin_user" placeholder="Admin email address" data-min="1" required data-validate="validateMinLength">
</div>
<div class="col-sm-8" style="width:90%">
Recheck identity every
<select name="oauth_recheck_interval" id="oauth_recheck_interval" style="width:350px;" class="select form-control">
Expand Down Expand Up @@ -346,7 +350,7 @@

<!-- Step 5c Credentials Header -->
<div class="wizard-card wizard-card-overlay" data-cardname="credentials-header">
<h3>Credentials</h3>
<h3 style="display:none">Credentials</h3>


<div class="wizard-input-section">
Expand All @@ -355,21 +359,27 @@
Enter the key of the header that will be provided from your reverse proxy containing the username.<br>Add the allowed users to the list below (seperated by semicolon) or leave it blank if access is granted to all authenticated users.<br><br>
</p>

<div class="col-sm-8">
<p><div class="col-sm-8">
<label for="auth_headerkey">Header Key:</label>
<input type="text" class="form-control" id="auth_headerkey" name="auth_headerkey" required placeholder="Header Key" data-validate="validateMinLength">
</div><br><br>
<input type="text" class="form-control" id="auth_headerkey" name="auth_headerkey" data-min="1" required placeholder="Header Key" data-validate="validateMinLength">
</div></p>

<div class="col-sm-8">
<p><div class="col-sm-8">
<label for="auth_header_admin">Admin user name:</label>
<input type="text" class="form-control" id="auth_header_admin" name="auth_header_admin" data-min="1" data-validate="validateMinLength" required placeholder="Admin user name">
</div></p>

<p><div class="col-sm-8">
<label for="auth_header_users">Authorised users:</label>
<input type="text" class="form-control" id="auth_header_users" name="auth_header_users" placeholder="Authorised users">
</div>
</div></p>

</div>
</div>
</div>
<!-- Step 5d Credentials Disbabled -->
<div class="wizard-card wizard-card-overlay" data-cardname="credentials-disabled">
<h3>Credentials</h3>
<h3 style="display:none">Credentials</h3>


<div class="wizard-input-section">
Expand All @@ -392,7 +402,7 @@

<!-- Step 6 Storage -->
<div class="wizard-card wizard-card-overlay" data-cardname="storage">
<h3>Storage</h3>
<h3 style="display:none">Storage</h3>

<div class="wizard-input-section">
<p>
Expand Down Expand Up @@ -437,7 +447,7 @@

<!-- Step 7 S3 Credentials -->
<div class="wizard-card wizard-card-overlay" data-cardname="s3credentials">
<h3>S3 Credentials</h3>
<h3 style="display:none">S3 Credentials</h3>

<div class="wizard-input-section">

Expand Down Expand Up @@ -525,7 +535,7 @@ function TestAWS(button, isManual) {

<!-- Step 7 Encryption -->
<div class="wizard-card wizard-card-overlay" data-cardname="encryption">
<h3>Encryption Level</h3>
<h3 style="display:none">Encryption Level</h3>

<div class="wizard-input-section">
<div class="form-group">
Expand Down Expand Up @@ -655,7 +665,7 @@ function TestAWS(button, isManual) {

<!-- Step 8 Encryption PW -->
<div class="wizard-card wizard-card-overlay" data-cardname="encryptionpw">
<h3>Encryption Master Password</h3>
<h3 style="display:none">Encryption Master Password</h3>

<div class="wizard-input-section">
<div class="form-group">
Expand Down
3 changes: 0 additions & 3 deletions internal/environment/Environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ type Environment struct {
AwsKeySecret string `env:"AWS_KEY_SECRET"`
AwsEndpoint string `env:"AWS_ENDPOINT"`
AwsProxyDownload bool `env:"AWS_PROXY_DOWNLOAD" envDefault:"false"`
// deprecated
// Will be removed with version 1.10.0
DatabaseName string `env:"DB_NAME" envDefault:"gokapi.sqlite"`
}

// New parses the env variables
Expand Down
16 changes: 16 additions & 0 deletions internal/models/Authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,29 @@ type AuthenticationConfig struct {
Username string `json:"Username"`
Password string `json:"Password"`
HeaderKey string `json:"HeaderKey"`
HeaderAdminUser string `json:"HeaderAdminUser"`
OAuthProvider string `json:"OauthProvider"`
OAuthClientId string `json:"OAuthClientId"`
OAuthClientSecret string `json:"OAuthClientSecret"`
OAuthUserScope string `json:"OauthUserScope"`
OAuthGroupScope string `json:"OauthGroupScope"`
OAuthAdminUser string `json:"OAuthAdminUser"`
OAuthRecheckInterval int `json:"OAuthRecheckInterval"`
HeaderUsers []string `json:"HeaderUsers"`
OAuthGroups []string `json:"OAuthGroups"`
OAuthUsers []string `json:"OauthUsers"`
}

const (
// AuthenticationInternal authentication method uses a user / password combination handled by Gokapi
AuthenticationInternal = iota

// AuthenticationOAuth2 authentication retrieves the users email with Open Connect ID
AuthenticationOAuth2

// AuthenticationHeader authentication relies on a header from a reverse proxy to parse the username
AuthenticationHeader

// AuthenticationDisabled authentication ignores all internal authentication procedures. A reverse proxy needs to restrict access
AuthenticationDisabled
)
6 changes: 3 additions & 3 deletions internal/webserver/Webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func Start() {
mux.HandleFunc("/d/{id}/{filename}", redirectFromFilename)
mux.HandleFunc("/dh/{id}/{filename}", downloadFileWithNameInUrl)

if configuration.Get().Authentication.Method == authentication.OAuth2 {
if configuration.Get().Authentication.Method == models.AuthenticationOAuth2 {
oauth.Init(configuration.Get().ServerUrl, configuration.Get().Authentication)
mux.HandleFunc("/oauth-login", oauth.HandlerLogin)
mux.HandleFunc("/oauth-callback", oauth.HandlerCallback)
Expand Down Expand Up @@ -343,11 +343,11 @@ func showLogin(w http.ResponseWriter, r *http.Request) {
redirect(w, "admin")
return
}
if configuration.Get().Authentication.Method == authentication.Header {
if configuration.Get().Authentication.Method == models.AuthenticationHeader {
redirect(w, "error-header")
return
}
if configuration.Get().Authentication.Method == authentication.OAuth2 {
if configuration.Get().Authentication.Method == models.AuthenticationOAuth2 {
// If user clicked logout, force consent
if r.URL.Query().Has("consent") {
redirect(w, "oauth-login?consent=true")
Expand Down
Loading

0 comments on commit c316a4f

Please sign in to comment.