mirror of
https://github.com/Forceu/Gokapi.git
synced 2026-04-26 08:30:20 -05:00
Check that env can only be positive, add UR limites for non-admin users, refactoring
This commit is contained in:
@@ -299,7 +299,7 @@ func DeleteUser(id int) {
|
||||
func GetSuperAdmin() (models.User, bool) {
|
||||
users := db.GetAllUsers()
|
||||
for _, user := range users {
|
||||
if user.UserLevel == models.UserLevelSuperAdmin {
|
||||
if user.IsSuperAdmin() {
|
||||
return user, true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +124,9 @@ func (p DatabaseProvider) Upgrade(currentDbVersion int) {
|
||||
}
|
||||
// < v2.2.0
|
||||
if currentDbVersion < 6 {
|
||||
if environment.New().PermRequestGrantedByDefault {
|
||||
for _, user := range p.GetAllUsers() {
|
||||
grantUploadPerm := environment.New().PermRequestGrantedByDefault
|
||||
for _, user := range p.GetAllUsers() {
|
||||
if grantUploadPerm || user.IsAdmin() {
|
||||
user.GrantPermission(models.UserPermGuestUploads)
|
||||
p.SaveUser(user, false)
|
||||
}
|
||||
|
||||
@@ -71,8 +71,9 @@ func (p DatabaseProvider) Upgrade(currentDbVersion int) {
|
||||
PRIMARY KEY("id")
|
||||
);`)
|
||||
helper.Check(err)
|
||||
if environment.New().PermRequestGrantedByDefault {
|
||||
for _, user := range p.GetAllUsers() {
|
||||
grantUploadPerm := environment.New().PermRequestGrantedByDefault
|
||||
for _, user := range p.GetAllUsers() {
|
||||
if grantUploadPerm || user.IsAdmin() {
|
||||
user.GrantPermission(models.UserPermGuestUploads)
|
||||
p.SaveUser(user, false)
|
||||
}
|
||||
@@ -82,12 +83,6 @@ func (p DatabaseProvider) Upgrade(currentDbVersion int) {
|
||||
p.DeleteApiKey(apiKey.Id)
|
||||
}
|
||||
}
|
||||
for _, user := range p.GetAllUsers() {
|
||||
if user.UserLevel != models.UserLevelUser {
|
||||
user.GrantPermission(models.UserPermGuestUploads)
|
||||
}
|
||||
p.SaveUser(user, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
envParser "github.com/caarlos0/env/v6"
|
||||
"github.com/forceu/gokapi/internal/environment/deprecation"
|
||||
@@ -20,15 +22,17 @@ type Environment struct {
|
||||
ConfigFile string `env:"CONFIG_FILE" envDefault:"config.json"`
|
||||
ConfigPath string
|
||||
DataDir string `env:"DATA_DIR" envDefault:"data"`
|
||||
ChunkSizeMB int `env:"CHUNK_SIZE_MB" envDefault:"45"`
|
||||
LengthId int `env:"LENGTH_ID" envDefault:"15"`
|
||||
LengthHotlinkId int `env:"LENGTH_HOTLINK_ID" envDefault:"40"`
|
||||
MaxFileSize int `env:"MAX_FILESIZE" envDefault:"102400"` // 102400==100GB
|
||||
MaxMemory int `env:"MAX_MEMORY_UPLOAD" envDefault:"50"`
|
||||
MaxParallelUploads int `env:"MAX_PARALLEL_UPLOADS" envDefault:"3"`
|
||||
MinFreeSpaceMB int `env:"MIN_FREE_SPACE" envDefault:"400"`
|
||||
MinLengthPassword int `env:"MIN_LENGTH_PASSWORD" envDefault:"8"`
|
||||
WebserverPort int `env:"PORT" envDefault:"53842"`
|
||||
ChunkSizeMB int `env:"CHUNK_SIZE_MB" envDefault:"45" onlyPositive:"true"`
|
||||
LengthId int `env:"LENGTH_ID" envDefault:"15" onlyPositive:"true"`
|
||||
LengthHotlinkId int `env:"LENGTH_HOTLINK_ID" envDefault:"40" onlyPositive:"true"`
|
||||
MaxFileSize int `env:"MAX_FILESIZE" envDefault:"102400" onlyPositive:"true"` // 102400 = 100GB
|
||||
MaxMemory int `env:"MAX_MEMORY_UPLOAD" envDefault:"50" onlyPositive:"true"`
|
||||
MaxParallelUploads int `env:"MAX_PARALLEL_UPLOADS" envDefault:"3" onlyPositive:"true"`
|
||||
MaxFilesGuestUpload int `env:"MAX_FILES_GUESTUPLOAD" envDefault:"100" onlyPositive:"true"`
|
||||
MaxSizeGuestUploadMb int `env:"MAX_SIZE_GUESTUPLOAD" envDefault:"10240" onlyPositive:"true"` // 10240 = 10GB
|
||||
MinFreeSpaceMB int `env:"MIN_FREE_SPACE" envDefault:"400" onlyPositive:"true"`
|
||||
MinLengthPassword int `env:"MIN_LENGTH_PASSWORD" envDefault:"8" onlyPositive:"true"`
|
||||
WebserverPort int `env:"PORT" envDefault:"53842" onlyPositive:"true"`
|
||||
DisableCorsCheck bool `env:"DISABLE_CORS_CHECK" envDefault:"false"`
|
||||
LogToStdout bool `env:"LOG_STDOUT" envDefault:"false"`
|
||||
HotlinkVideos bool `env:"ENABLE_HOTLINK_VIDEOS" envDefault:"false"`
|
||||
@@ -54,6 +58,11 @@ func New() Environment {
|
||||
}
|
||||
|
||||
result = parseEnvVars(result)
|
||||
err := enforceOnlyPositiveDefaults(&result)
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing env variables:", err)
|
||||
osExit(1)
|
||||
}
|
||||
result = parseFlags(result)
|
||||
result.ActiveDeprecations = deprecation.GetActive()
|
||||
|
||||
@@ -87,9 +96,55 @@ func parseEnvVars(result Environment) Environment {
|
||||
return result
|
||||
}
|
||||
|
||||
func enforceOnlyPositiveDefaults(result *Environment) error {
|
||||
v := reflect.ValueOf(result)
|
||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
||||
return fmt.Errorf("env must be a pointer to a struct")
|
||||
}
|
||||
|
||||
v = v.Elem()
|
||||
t := v.Type()
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fieldVal := v.Field(i)
|
||||
fieldType := t.Field(i)
|
||||
|
||||
if fieldType.Tag.Get("onlyPositive") != "true" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only handle signed integers
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if fieldVal.Int() >= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
defaultStr := fieldType.Tag.Get("envDefault")
|
||||
defaultVal, err := strconv.ParseInt(defaultStr, 10, fieldVal.Type().Bits())
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid envDefault for field %s: %w", fieldType.Name, err)
|
||||
}
|
||||
|
||||
if fieldVal.CanSet() {
|
||||
fieldVal.SetInt(defaultVal)
|
||||
} else {
|
||||
return fmt.Errorf("cannot set fieldval %s", fieldType.Name)
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseFlags(result Environment) Environment {
|
||||
flags := flagparser.ParseFlags()
|
||||
if flags.IsPortSet {
|
||||
if flags.Port < 1 {
|
||||
flags.Port = DefaultPort
|
||||
}
|
||||
result.WebserverPort = flags.Port
|
||||
}
|
||||
if flags.IsConfigDirSet {
|
||||
|
||||
@@ -58,6 +58,11 @@ func (u *User) IsSuperAdmin() bool {
|
||||
return u.UserLevel == UserLevelSuperAdmin
|
||||
}
|
||||
|
||||
// IsAdmin returns true if the user has the Rank UserLevelSuperAdmin or UserLevelAdmin
|
||||
func (u *User) IsAdmin() bool {
|
||||
return u.UserLevel == UserLevelAdmin || u.UserLevel == UserLevelSuperAdmin
|
||||
}
|
||||
|
||||
// IsSameUser returns true, if the user has the same ID
|
||||
func (u *User) IsSameUser(userId int) bool {
|
||||
return u.Id == userId
|
||||
|
||||
@@ -642,7 +642,7 @@ func showAdminMenu(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
view := (&AdminView{}).convertGlobalConfig(ViewMain, user)
|
||||
if len(configuration.GetEnvironment().ActiveDeprecations) > 0 {
|
||||
if user.UserLevel == models.UserLevelSuperAdmin {
|
||||
if user.IsSuperAdmin() {
|
||||
view.ShowDeprecationNotice = true
|
||||
}
|
||||
}
|
||||
@@ -736,6 +736,8 @@ type AdminView struct {
|
||||
ChunkSize int
|
||||
MaxParallelUploads int
|
||||
MinLengthPassword int
|
||||
FileRequestMaxFiles int
|
||||
FileRequestMaxSize int
|
||||
TimeNow int64
|
||||
CustomContent customStatic
|
||||
}
|
||||
@@ -832,6 +834,10 @@ func (u *AdminView) convertGlobalConfig(view int, user models.User) *AdminView {
|
||||
}
|
||||
fileRequest.Files = sortMetaData(fileRequest.Files)
|
||||
u.FileRequests = append(u.FileRequests, fileRequest)
|
||||
if !user.IsAdmin() {
|
||||
u.FileRequestMaxFiles = configuration.GetEnvironment().MaxFilesGuestUpload
|
||||
u.FileRequestMaxSize = configuration.GetEnvironment().MaxSizeGuestUploadMb
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ func Process(w http.ResponseWriter, r *http.Request) {
|
||||
sendError(w, http.StatusUnauthorized, "Unauthorized")
|
||||
return
|
||||
}
|
||||
if routing.AdminOnly && (user.UserLevel != models.UserLevelAdmin && user.UserLevel != models.UserLevelSuperAdmin) {
|
||||
if routing.AdminOnly && !user.IsAdmin() {
|
||||
sendError(w, http.StatusUnauthorized, "Unauthorized")
|
||||
return
|
||||
}
|
||||
@@ -375,7 +375,7 @@ func apiChunkReserve(w http.ResponseWriter, r requestParser, _ models.User) {
|
||||
|
||||
}
|
||||
|
||||
func apiChunkUploadRequestAdd(w http.ResponseWriter, r requestParser, _ models.User) {
|
||||
func apiChunkUploadRequestAdd(w http.ResponseWriter, r requestParser, user models.User) {
|
||||
request, ok := r.(*paramChunkUploadRequestAdd)
|
||||
if !ok {
|
||||
panic("invalid parameter passed")
|
||||
@@ -386,10 +386,11 @@ func apiChunkUploadRequestAdd(w http.ResponseWriter, r requestParser, _ models.U
|
||||
return
|
||||
}
|
||||
maxUpload := configuration.Get().MaxFileSizeMB
|
||||
if !user.IsAdmin() && configuration.GetEnvironment().MaxSizeGuestUploadMb != 0 {
|
||||
maxUpload = min(maxUpload, configuration.GetEnvironment().MaxSizeGuestUploadMb)
|
||||
}
|
||||
if !fileRequest.IsUnlimitedSize() {
|
||||
if (fileRequest.MaxSize) < maxUpload {
|
||||
maxUpload = fileRequest.MaxSize
|
||||
}
|
||||
maxUpload = min(maxUpload, fileRequest.MaxSize)
|
||||
}
|
||||
statusCode, errString := processNewChunk(w, request, maxUpload, fileRequest.Id)
|
||||
if statusCode != http.StatusOK {
|
||||
@@ -997,6 +998,28 @@ func apiURequestDelete(w http.ResponseWriter, r requestParser, user models.User)
|
||||
_, _ = w.Write([]byte("{\"result\":\"OK\"}"))
|
||||
}
|
||||
|
||||
func isUserAllowedUnlimited(request *paramURequestSave, isNewRequest bool, user models.User) bool {
|
||||
if user.IsAdmin() {
|
||||
return true
|
||||
}
|
||||
isServerLimitMaxSize := configuration.GetEnvironment().MaxSizeGuestUploadMb != 0
|
||||
isServerLimitMaxFiles := configuration.GetEnvironment().MaxFilesGuestUpload != 0
|
||||
if isServerLimitMaxSize {
|
||||
if (request.IsMaxSizeSet || isNewRequest) &&
|
||||
(request.MaxSizeMb == 0 || request.MaxSizeMb > configuration.GetEnvironment().MaxSizeGuestUploadMb) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if isServerLimitMaxFiles {
|
||||
if (request.IsMaxFilesSet || isNewRequest) &&
|
||||
(request.MaxFiles == 0 || request.MaxFiles > configuration.GetEnvironment().MaxFilesGuestUpload) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func apiURequestSave(w http.ResponseWriter, r requestParser, user models.User) {
|
||||
request, ok := r.(*paramURequestSave)
|
||||
if !ok {
|
||||
@@ -1005,6 +1028,12 @@ func apiURequestSave(w http.ResponseWriter, r requestParser, user models.User) {
|
||||
uploadRequest := models.FileRequest{}
|
||||
isNewRequest := request.Id == ""
|
||||
|
||||
if !isUserAllowedUnlimited(request, isNewRequest, user) {
|
||||
sendError(w, http.StatusBadRequest, "Only admin users can create requests with unlimited size / file count"+
|
||||
" or values larger than the server's max size / file count")
|
||||
return
|
||||
}
|
||||
|
||||
if !isNewRequest {
|
||||
uploadRequest, ok = database.GetFileRequest(request.Id)
|
||||
if !ok {
|
||||
@@ -1035,7 +1064,7 @@ func apiURequestSave(w http.ResponseWriter, r requestParser, user models.User) {
|
||||
uploadRequest.MaxFiles = request.MaxFiles
|
||||
}
|
||||
if request.IsMaxSizeSet {
|
||||
uploadRequest.MaxSize = request.MaxSize
|
||||
uploadRequest.MaxSize = request.MaxSizeMb
|
||||
}
|
||||
if request.IsNotesSet {
|
||||
uploadRequest.Notes = request.Notes
|
||||
|
||||
@@ -719,7 +719,7 @@ type paramURequestSave struct {
|
||||
Notes string `header:"notes" supportBase64:"true"`
|
||||
Expiry int64 `header:"expiry"`
|
||||
MaxFiles int `header:"maxfiles"`
|
||||
MaxSize int `header:"maxsize"`
|
||||
MaxSizeMb int `header:"maxsize"`
|
||||
IsNameSet bool
|
||||
IsExpirySet bool
|
||||
IsMaxFilesSet bool
|
||||
|
||||
@@ -1280,7 +1280,7 @@ func (p *paramURequestSave) ParseRequest(r *http.Request) error {
|
||||
}
|
||||
p.foundHeaders["maxsize"] = exists
|
||||
if exists {
|
||||
p.MaxSize, err = parseHeaderInt(r, "maxsize")
|
||||
p.MaxSizeMb, err = parseHeaderInt(r, "maxsize")
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid value in header maxsize supplied")
|
||||
}
|
||||
|
||||
@@ -101,6 +101,41 @@ function setModalValues(id, name, maxFiles, maxSize, expiry, notes) {
|
||||
document.getElementById("mFriendlyName").value = name;
|
||||
}
|
||||
|
||||
|
||||
if (limitMaxFiles != 0) {
|
||||
let checkbox = document.getElementById("mc_maxfiles");
|
||||
if (maxFiles === null || maxFiles == 0) {
|
||||
maxFiles = limitMaxFiles;
|
||||
}
|
||||
checkbox.checked = true;
|
||||
checkbox.disabled = true;
|
||||
checkbox.title = "Only admins can set this to unlimited";
|
||||
checkbox.value = "1";
|
||||
document.getElementById("mi_maxfiles").setAttribute("max", limitMaxFiles);
|
||||
} else {
|
||||
let checkbox = document.getElementById("mc_maxfiles");
|
||||
checkbox.disabled = false;
|
||||
checkbox.title = "";
|
||||
document.getElementById("mi_maxfiles").setAttribute("max", "");
|
||||
}
|
||||
|
||||
if (limitMaxSize != 0) {
|
||||
let checkbox = document.getElementById("mc_maxsize");
|
||||
if (maxSize === null || maxSize == 0) {
|
||||
maxSize = limitMaxSize;
|
||||
}
|
||||
checkbox.checked = true;
|
||||
checkbox.disabled = true;
|
||||
checkbox.title = "Only admins can set this to unlimited";
|
||||
checkbox.value = "1";
|
||||
document.getElementById("mi_maxsize").setAttribute("max", limitMaxSize);
|
||||
} else {
|
||||
let checkbox = document.getElementById("mc_maxsize");
|
||||
checkbox.disabled = false;
|
||||
checkbox.title = "";
|
||||
document.getElementById("mi_maxsize").setAttribute("max", "");
|
||||
}
|
||||
|
||||
if (maxFiles === null || maxFiles == 0) {
|
||||
document.getElementById("mi_maxfiles").value = "1";
|
||||
document.getElementById("mi_maxfiles").disabled = true;
|
||||
@@ -111,6 +146,7 @@ function setModalValues(id, name, maxFiles, maxSize, expiry, notes) {
|
||||
document.getElementById("mc_maxfiles").checked = true;
|
||||
}
|
||||
|
||||
|
||||
if (maxSize === null || maxSize == 0) {
|
||||
document.getElementById("mi_maxsize").value = "10";
|
||||
document.getElementById("mi_maxsize").disabled = true;
|
||||
@@ -133,7 +169,7 @@ function setModalValues(id, name, maxFiles, maxSize, expiry, notes) {
|
||||
document.getElementById("mc_expiry").checked = true;
|
||||
createCalendar("mi_expiry", expiry);
|
||||
}
|
||||
document.getElementById("mNotes").value = notes;
|
||||
document.getElementById("mNotes").value = notes;
|
||||
}
|
||||
|
||||
function editFileRequest(id, name, maxFiles, maxSize, expiry, notes) {
|
||||
@@ -180,6 +216,20 @@ function saveFileRequest() {
|
||||
});
|
||||
}
|
||||
|
||||
function checkMaxNumber(element) {
|
||||
if (element.value == "") {
|
||||
element.value = "1";
|
||||
return;
|
||||
}
|
||||
let maxVal = element.getAttribute("max");
|
||||
if (maxVal == "") {
|
||||
return;
|
||||
}
|
||||
if (element.value > maxVal) {
|
||||
element.value = maxVal;
|
||||
}
|
||||
}
|
||||
|
||||
function insertOrReplaceFileRequest(jsonResult) {
|
||||
const tbody = document.getElementById("filerequesttable");
|
||||
let row = document.getElementById(`row-${jsonResult.id}`);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -110,7 +110,10 @@
|
||||
<script>
|
||||
var userName = "{{.ActiveUser.Name}}";
|
||||
var baseUrl = "{{.ServerUrl}}";
|
||||
var canViewOtherRequests = {{.ActiveUser.HasPermissionListOtherUploads }};
|
||||
var canViewOtherRequests = {{.ActiveUser.HasPermissionListOtherUploads}};
|
||||
var limitMaxSize = {{.FileRequestMaxSize}};
|
||||
var limitMaxFiles = {{.FileRequestMaxFiles}};
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -166,7 +169,7 @@
|
||||
<input type="checkbox" id="mc_maxfiles" aria-label="Max files" title="Max files" data-toggle-target="mi_maxfiles" onchange="handleEditCheckboxChange(this)">
|
||||
</div>
|
||||
<span class="input-group-text" id="mMaxFiles">Max Files</span>
|
||||
<input type="number" min="1" id="mi_maxfiles" disabled class="form-control" aria-label="Max Files" aria-describedby="mMaxFiles" data-allow-regular-paste>
|
||||
<input type="number" min="1" id="mi_maxfiles" onChange="checkMaxNumber(this)" disabled class="form-control" aria-label="Max Files" aria-describedby="mMaxFiles" data-allow-regular-paste>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
@@ -175,7 +178,7 @@
|
||||
data-toggle-target="mi_maxsize" onchange="handleEditCheckboxChange(this)">
|
||||
</div>
|
||||
<span class="input-group-text" id="tMaxSize">Max Size </span>
|
||||
<input type="number" min="1" id="mi_maxsize" disabled class="form-control" aria-label="Max Size" aria-describedby="tMaxSize" data-allow-regular-paste>
|
||||
<input type="number" min="1" id="mi_maxsize" onChange="checkMaxNumber(this)" disabled class="form-control" aria-label="Max Size" aria-describedby="tMaxSize" data-allow-regular-paste>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
|
||||
Reference in New Issue
Block a user