POC restricted access

This commit is contained in:
Nogard 2025-05-21 21:12:23 +02:00
parent 76070ac882
commit 1958eb16fc
2 changed files with 197 additions and 55 deletions

View File

@ -16,6 +16,14 @@ const (
profiler_API_PROCESS_REQUEST string = "API Process Request" profiler_API_PROCESS_REQUEST string = "API Process Request"
) )
type apiUserDefinition struct {
Id string
Name string
Permission string
}
type apiHandleAdminFunction func(http.ResponseWriter, *http.Request, httprouter.Params, apiUserDefinition)
var apiRootPath string = "/api/v1" var apiRootPath string = "/api/v1"
func Init(router *httprouter.Router) { func Init(router *httprouter.Router) {
@ -34,7 +42,7 @@ func Init(router *httprouter.Router) {
router.GET(apiRootPath+"/page/:folder/:page/content", apiHandler(apiPageFolderPageContent)) router.GET(apiRootPath+"/page/:folder/:page/content", apiHandler(apiPageFolderPageContent))
router.GET(apiRootPath+"/user", apiHandler(apiUser)) router.GET(apiRootPath+"/user", apiHandler(apiUser))
router.POST(apiRootPath+"/user/:name", apiHandler(apiUser_Create)) router.POST(apiRootPath+"/user/:name", apiHandler(apiAdminRestricted(apiUser_Create)))
router.GET(apiRootPath+"/user/:name", apiHandler(apiUserName)) router.GET(apiRootPath+"/user/:name", apiHandler(apiUserName))
router.GET(apiRootPath+"/tag", apiHandler(apiTag)) router.GET(apiRootPath+"/tag", apiHandler(apiTag))
@ -58,6 +66,80 @@ func apiHandler(h httprouter.Handle) httprouter.Handle {
} }
} }
func apiAdminRestricted(h apiHandleAdminFunction) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var auth string = r.Header.Get("Authorization")
if auth == "" {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
const lenBearer int = len("Bearer ")
if auth[:lenBearer] != "Bearer " {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
var token string = auth[lenBearer:]
var result database.DatabaseResult = database.ExecuteStoredRoutine("CheckAuthToken", token)
database.DecodeDatabaseResult(&result)
if result.ParsedResultLength == 0 {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
var user apiUserDefinition = apiUserDefinition{
Id: result.ParsedResult["userId"][0].String,
Name: result.ParsedResult["userName"][0].String,
Permission: result.ParsedResult["userPermission"][0].String,
}
if user.Permission != "ADMIN" {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
h(w, r, ps, user)
}
}
func apiModeratorRestricted(h apiHandleAdminFunction) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var auth string = r.Header.Get("Authorization")
if auth == "" {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
const lenBearer int = len("Bearer ")
if auth[:lenBearer] != "Bearer " {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
var token string = auth[lenBearer:]
var result database.DatabaseResult = database.ExecuteStoredRoutine("CheckAuthToken", token)
database.DecodeDatabaseResult(&result)
if result.ParsedResultLength == 0 {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
var user apiUserDefinition = apiUserDefinition{
Id: result.ParsedResult["userId"][0].String,
Name: result.ParsedResult["userName"][0].String,
Permission: result.ParsedResult["userPermission"][0].String,
}
if (user.Permission != "ADMIN") && (user.Permission != "MODERATOR") {
writeErrorJSON(w, "", http.StatusForbidden)
return
}
h(w, r, ps, user)
}
}
// API ROOT // API ROOT
func apiRoot(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func apiRoot(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@ -68,11 +150,20 @@ func apiRoot(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
// API Auth // API Auth
func apiAuth(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func apiAuth(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprintf(w, "API Auth") var ok bool
var id, name, permission string
ok, id, name, permission = verifyToken(r)
if !ok {
writeErrorJSON(w, "Authentification required", http.StatusForbidden)
return
}
fmt.Fprintf(w, "{\"User\": {\"Id\": %s, \"Name\": \"%s\", \"Permission\": \"%s\"}}", id, name, permission)
} }
func apiAuthLogin(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func apiAuthLogin(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprintf(w, "API Auth Login") fmt.Fprintf(w, "API Auth Login\n")
r.ParseForm() r.ParseForm()
var username string = r.FormValue("username") var username string = r.FormValue("username")
var password string = r.FormValue("password") var password string = r.FormValue("password")
@ -118,7 +209,7 @@ func apiUser(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Println(result) fmt.Println(result)
} }
func apiUser_Create(w http.ResponseWriter, r *http.Request, params httprouter.Params) { func apiUser_Create(w http.ResponseWriter, r *http.Request, params httprouter.Params, _ apiUserDefinition) {
r.ParseForm() r.ParseForm()
var username string = params.ByName("name") var username string = params.ByName("name")
var password string = r.FormValue("password") var password string = r.FormValue("password")
@ -174,6 +265,9 @@ func apiDebugDatabaseReset(w http.ResponseWriter, r *http.Request, _ httprouter.
fmt.Fprintf(w, "Init Database...\n") fmt.Fprintf(w, "Init Database...\n")
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
database.UpdateDatabaseStructure() database.UpdateDatabaseStructure()
fmt.Fprintf(w, "Setup default conf...\n")
database.ExecuteStoredRoutine("CreateUser", "admin", "admin")
database.ExecuteStoredRoutine("UpdateUser", "1", nil, "ADMIN", "NULL")
fmt.Fprintf(w, "Done") fmt.Fprintf(w, "Done")
} }
@ -205,3 +299,30 @@ func setHeaderStream(w http.ResponseWriter) {
w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive") w.Header().Set("Connection", "keep-alive")
} }
func verifyToken(r *http.Request) (bool, string, string, string) {
var auth string = r.Header.Get("Authorization")
if auth == "" {
return false, "", "", ""
}
const lenBearer int = len("Bearer ")
if auth[:lenBearer] != "Bearer " {
return false, "", "", ""
}
var token string = auth[lenBearer:]
var result database.DatabaseResult = database.ExecuteStoredRoutine("CheckAuthToken", token)
database.DecodeDatabaseResult(&result)
if result.ParsedResultLength == 0 {
return false, "", "", ""
}
var id, name, permission string = result.ParsedResult["userId"][0].String, result.ParsedResult["userName"][0].String, result.ParsedResult["userPermission"][0].String
return true, id, name, permission
}
func writeErrorJSON(w http.ResponseWriter, message string, codeRet int) {
http.Error(w, fmt.Sprintf("{\"Error\": {\"Code\": %d, \"Message\": \"%s\"}}", codeRet, message), codeRet)
}

View File

@ -35,6 +35,7 @@ type DatabaseResult struct {
Error DatabaseError Error DatabaseError
SqlRows *sql.Rows SqlRows *sql.Rows
SqlResult sql.Result SqlResult sql.Result
ParsedResultLength int64
ParsedResult map[string][]sql.NullString ParsedResult map[string][]sql.NullString
} }
@ -256,6 +257,16 @@ var databaseStructure databaseDefinition = databaseDefinition{
Name: "user_id", Name: "user_id",
Type: "int(11)", Type: "int(11)",
}, },
{
Name: "creation",
Type: "DATE",
Default: "CURRENT_TIMESTAMP()",
},
{
Name: "valid_until",
Type: "DATE",
Default: "NOW()",
},
}, },
}, },
{ {
@ -281,51 +292,6 @@ var databaseStructure databaseDefinition = databaseDefinition{
}, },
}, },
Indexes: []indexDefinition{ Indexes: []indexDefinition{
// PRIMARY KEYS
/*
{
Name: "",
Type: database_ENUM_INDEX_PRIMARY,
Table: "user",
Columns: []string{
"id",
},
},
{
Name: "",
Type: database_ENUM_INDEX_PRIMARY,
Table: "folder",
Columns: []string{
"id",
},
},
{
Name: "",
Type: database_ENUM_INDEX_PRIMARY,
Table: "tag",
Columns: []string{
"id",
},
},
{
Name: "",
Type: database_ENUM_INDEX_PRIMARY,
Table: "website",
Columns: []string{
"id",
},
},
{
Name: "",
Type: database_ENUM_INDEX_PRIMARY,
Table: "tags_websites",
Columns: []string{
"tag_id",
"website_id",
},
},
*/
// UNIQUE KEYS // UNIQUE KEYS
{ {
@ -380,6 +346,58 @@ var databaseStructure databaseDefinition = databaseDefinition{
Name: "ListUsers", Name: "ListUsers",
Command: "SELECT id, name, permission_level FROM user;", Command: "SELECT id, name, permission_level FROM user;",
}, },
{
Name: "GetUserCount",
Command: "SELECT COUNT(user.id) as userCount FROM user;",
},
{
Name: "GetWebsiteCount",
Command: "SELECT COUNT(website.id) as websiteCount FROM website;",
},
{
Name: "CheckAuthToken",
Parameters: []storedRoutineParameterDefinition{
{
Name: "f_token",
Type: "VARCHAR(256)",
Charset: "utf8mb4",
Collation: "utf8mb4_bin",
},
},
Command: "SELECT user.id as userId, user.name as userName, user.permission_level as userPermission FROM loginToken INNER JOIN user ON user.id = loginToken.user_id WHERE f_token = loginToken.id LIMIT 1;",
},
{
Name: "UpdateUser",
Parameters: []storedRoutineParameterDefinition{
{
Name: "f_user_id",
Type: "int(11)",
IsNullable: true,
},
{
Name: "f_new_name",
Type: "VARCHAR(256)",
Charset: "utf8mb4",
Collation: "utf8mb4_unicode_ci",
IsNullable: true,
},
{
Name: "f_new_permission_level",
Type: "VARCHAR(32)",
Charset: "utf8mb4",
Collation: "utf8mb4_unicode_ci",
IsNullable: true,
},
{
Name: "f_new_email",
Type: "VARCHAR(256)",
Charset: "utf8mb4",
Collation: "utf8mb4_unicode_ci",
IsNullable: true,
},
},
Command: "UPDATE user SET name = IFNULL(f_new_name, name), permission_level = IFNULL(f_new_permission_level, permission_level), email = IFNULL(f_new_email, email);",
},
}, },
StoredFunctions: []storedFunctionDefinition{ StoredFunctions: []storedFunctionDefinition{
{ {
@ -484,7 +502,7 @@ func connect() {
var err error var err error
log.Printf("Connecting to database %s at %s:%d as %s\n", dbConfig.Database, dbConfig.Host, dbConfig.Port, dbConfig.User) log.Printf("Connecting to database %s at %s:%d as %s\n", dbConfig.Database, dbConfig.Host, dbConfig.Port, dbConfig.User)
tStart := time.Now() var tStart time.Time = time.Now()
for i := range dbConfig.RetriesOnError { for i := range dbConfig.RetriesOnError {
db, err = sql.Open("mysql", dbPath) db, err = sql.Open("mysql", dbPath)
if err == nil { if err == nil {
@ -568,6 +586,7 @@ func DecodeDatabaseResult(databaseResult *DatabaseResult) {
} }
databaseResult.ParsedResult = map[string][]sql.NullString{} databaseResult.ParsedResult = map[string][]sql.NullString{}
databaseResult.ParsedResultLength = 0
var numCol int = len(columns) var numCol int = len(columns)
for _, c := range columns { for _, c := range columns {
databaseResult.ParsedResult[c] = make([]sql.NullString, 0) databaseResult.ParsedResult[c] = make([]sql.NullString, 0)
@ -586,6 +605,7 @@ func DecodeDatabaseResult(databaseResult *DatabaseResult) {
for i, c := range columns { for i, c := range columns {
databaseResult.ParsedResult[c] = append(databaseResult.ParsedResult[c], strArr[i]) databaseResult.ParsedResult[c] = append(databaseResult.ParsedResult[c], strArr[i])
} }
databaseResult.ParsedResultLength++
} }
if err = rows.Err(); err != nil { if err = rows.Err(); err != nil {
@ -1024,7 +1044,7 @@ func UpdateDatabaseStructure() {
if i != 0 { if i != 0 {
command += ", " command += ", "
} }
command += fmt.Sprintf("IN '%s' %s", pp.Name, pp.Type) command += fmt.Sprintf("IN `%s` %s", pp.Name, pp.Type)
if ppc, ok := pp.Charset.(string); ok { if ppc, ok := pp.Charset.(string); ok {
command += fmt.Sprintf(" CHARACTER SET %s", ppc) command += fmt.Sprintf(" CHARACTER SET %s", ppc)
@ -1038,6 +1058,7 @@ func UpdateDatabaseStructure() {
fmt.Println(command) fmt.Println(command)
executeExec(command) executeExec(command)
} }
initDatabaseStructure()
} }
func DestroyDatabase() { func DestroyDatabase() {