Finished with crash requests
Change Log to Count Added option to get user count Moved functions to VerifyHeader Added user delete
This commit is contained in:
@@ -6,8 +6,6 @@ This is a purposefully "simple" application backend made specifically for _my_ a
|
|||||||
|
|
||||||
### API Key
|
### API Key
|
||||||
|
|
||||||
The special appID "darkstormManagement" is used to manage all apps.
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
id: "API Key",
|
id: "API Key",
|
||||||
@@ -15,7 +13,7 @@ The special appID "darkstormManagement" is used to manage all apps.
|
|||||||
death: -1, // unix timestamp (seconds) when the key is no longer valid. -1 means there is not expected expiration (that can change in the future)
|
death: -1, // unix timestamp (seconds) when the key is no longer valid. -1 means there is not expected expiration (that can change in the future)
|
||||||
perm: {
|
perm: {
|
||||||
user: true, // create and login users
|
user: true, // create and login users
|
||||||
log: true, // log users
|
count: true, // count users
|
||||||
crash: true, // crash reports
|
crash: true, // crash reports
|
||||||
management: false, // managing
|
management: false, // managing
|
||||||
// further permissions can be added as needed
|
// further permissions can be added as needed
|
||||||
@@ -23,7 +21,9 @@ The special appID "darkstormManagement" is used to manage all apps.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### DB Log
|
Optionally you can set a special AppID to be a management key. Setting a management key enables management requests.
|
||||||
|
|
||||||
|
### Count log
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -111,16 +111,42 @@ If an error status code is returned then the body will be as follows.
|
|||||||
* API Key is invalid or does not have the needed permission for the request.
|
* API Key is invalid or does not have the needed permission for the request.
|
||||||
* invalidBody
|
* invalidBody
|
||||||
* Body of the request is malformed.
|
* Body of the request is malformed.
|
||||||
|
* badRequest
|
||||||
|
* Some part of your request is invalid
|
||||||
* internal
|
* internal
|
||||||
* Server-side issue.
|
* Server-side issue.
|
||||||
|
|
||||||
### Log
|
### Count
|
||||||
|
|
||||||
API Key must have the `log` permission.
|
API Key must have the `Count` permission.
|
||||||
|
|
||||||
Request:
|
Request:
|
||||||
|
|
||||||
> POST: /log/{uuid}
|
> POST: /count/{uuid}
|
||||||
|
|
||||||
|
### User Count
|
||||||
|
|
||||||
|
Get a count of users.
|
||||||
|
|
||||||
|
API Key must have the `management` permission.
|
||||||
|
|
||||||
|
`platform` query is optional (defaults to all).
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
> GET: /count?platform=all
|
||||||
|
|
||||||
|
With management key:
|
||||||
|
|
||||||
|
> GET: /{appID}/count?platform=all
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Users
|
### Users
|
||||||
|
|
||||||
@@ -128,6 +154,8 @@ Request:
|
|||||||
|
|
||||||
All requsests pertaining to users requires the `X-API-Key` header and the key must have the `users` permission.
|
All requsests pertaining to users requires the `X-API-Key` header and the key must have the `users` permission.
|
||||||
|
|
||||||
|
Enabled by using `Backend.AddUserAuth`.
|
||||||
|
|
||||||
#### Create User
|
#### Create User
|
||||||
|
|
||||||
> TODO: Email user to confirm.
|
> TODO: Email user to confirm.
|
||||||
@@ -164,11 +192,19 @@ If returned status is 401, the errorCode will be one of the following:
|
|||||||
* password
|
* password
|
||||||
* Password is to short or too long.
|
* Password is to short or too long.
|
||||||
|
|
||||||
|
#### Delete User
|
||||||
|
|
||||||
|
Requires either the `management` permission or a management key.
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
> DELETE: /user/{userID}
|
||||||
|
|
||||||
#### Login
|
#### Login
|
||||||
|
|
||||||
Request:
|
Request:
|
||||||
|
|
||||||
> POST: /user
|
> POST: /user/login
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -196,9 +232,21 @@ Possible `error` values:
|
|||||||
* invalid
|
* invalid
|
||||||
* Either the username or password is incorrect
|
* Either the username or password is incorrect
|
||||||
|
|
||||||
### Crash Report
|
#### Change Password
|
||||||
|
|
||||||
> TODO: Archive a crash to prevent it being reported again.
|
Request:
|
||||||
|
|
||||||
|
> POST: /user/changepassword
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
token: "JWT Token",
|
||||||
|
old: "Old Password",
|
||||||
|
new: "New Password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Crash Report
|
||||||
|
|
||||||
#### Report
|
#### Report
|
||||||
|
|
||||||
@@ -227,7 +275,7 @@ Request:
|
|||||||
|
|
||||||
> DELETE: /crash/{crashID}
|
> DELETE: /crash/{crashID}
|
||||||
|
|
||||||
With "darkstormManagement" key:
|
With management key:
|
||||||
|
|
||||||
> DELETE: /{appID}/crash/{crashID}
|
> DELETE: /{appID}/crash/{crashID}
|
||||||
|
|
||||||
@@ -239,16 +287,16 @@ Request:
|
|||||||
|
|
||||||
> POST: /crash/archive
|
> POST: /crash/archive
|
||||||
|
|
||||||
With "darkstormManagement" key:
|
With management key:
|
||||||
|
|
||||||
> POST: /{appID}/crash/{crashID}
|
> POST: /{appID}/crash/archive
|
||||||
|
|
||||||
Request Body:
|
Request Body:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
error: "error",
|
error: "error",
|
||||||
stack: "full stacktrace",
|
stack: "full stacktrace", // Archives will only match against a perfect match.
|
||||||
platform: "all", // Limit the archive to a specific platform, or use "all".
|
platform: "all", // Limit the archive to a specific platform, or use "all".
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import "net/http"
|
|||||||
// An application interface. Both LogTable and CrashTable are optional, if they return nil then requests will be forbidden.
|
// An application interface. Both LogTable and CrashTable are optional, if they return nil then requests will be forbidden.
|
||||||
type App interface {
|
type App interface {
|
||||||
AppID() string
|
AppID() string
|
||||||
LogTable() LogTable
|
CountTable() CountTable
|
||||||
CrashTable() CrashTable
|
CrashTable() CrashTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package darkstorm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@@ -65,55 +64,105 @@ func (b *Backend) reportCrash(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) deleteCrash(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) deleteCrash(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.VerifyHeader(w, r, "management", false)
|
||||||
if hdr.Key == nil || hdr.Key.Perm["management"] || errors.Is(err, ErrApiKeyUnauthorized) {
|
if hdr == nil {
|
||||||
ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application not authorized")
|
if err == nil {
|
||||||
return
|
log.Println("request key parsing error:", err)
|
||||||
} else if err != nil {
|
}
|
||||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO
|
crashID := r.PathValue("crashID")
|
||||||
|
if crashID == "" {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.actualCrashDelete(w, b.GetApp(hdr.Key), crashID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) managementDeleteCrash(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) managementDeleteCrash(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.VerifyHeader(w, r, "management", true)
|
||||||
if hdr.Key == nil || hdr.Key.Perm["management"] || errors.Is(err, ErrApiKeyUnauthorized) {
|
if hdr == nil {
|
||||||
ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application not authorized")
|
if err == nil {
|
||||||
return
|
log.Println("request key parsing error:", err)
|
||||||
} else if err != nil {
|
}
|
||||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO
|
crashID := r.PathValue("crashID")
|
||||||
|
if crashID == "" {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
appID := r.PathValue("appID")
|
||||||
|
ap := b.apps[appID]
|
||||||
|
if ap == nil || appID == "" {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.actualCrashDelete(w, ap, crashID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) actualCrashDelete(w http.ResponseWriter, ap App, crashID string) {}
|
func (b *Backend) actualCrashDelete(w http.ResponseWriter, ap App, crashID string) {
|
||||||
|
crash := ap.CrashTable()
|
||||||
|
if crash == nil {
|
||||||
|
ReturnError(w, http.StatusInternalServerError, "misconfigured", "Server Misconfigured")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := crash.Remove(crashID)
|
||||||
|
if err != nil && err != ErrNotFound {
|
||||||
|
log.Println("error when deleting crash:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Backend) archiveCrash(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) archiveCrash(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.VerifyHeader(w, r, "management", false)
|
||||||
if hdr.Key == nil || hdr.Key.Perm["management"] {
|
if hdr == nil {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
if err == nil {
|
||||||
|
log.Println("request key parsing error:", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
defer r.Body.Close()
|
||||||
//TODO
|
var toArchive ArchivedCrash
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&toArchive)
|
||||||
|
if err != nil || toArchive.Platform == "" || toArchive.Error == "" || toArchive.Stack == "" {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "invalidBody", "Bad request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO
|
b.actualCrashArchive(w, b.GetApp(hdr.Key), toArchive)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) managementArchiveCrash(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) managementArchiveCrash(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.VerifyHeader(w, r, "management", true)
|
||||||
if hdr.Key == nil || hdr.Key.Perm["management"] {
|
if hdr == nil {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
if err == nil {
|
||||||
|
log.Println("request key parsing error:", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
appID := r.PathValue("appID")
|
||||||
//TODO
|
ap := b.apps[appID]
|
||||||
|
if ap == nil || appID == "" {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO
|
defer r.Body.Close()
|
||||||
|
var toArchive ArchivedCrash
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&toArchive)
|
||||||
|
if err != nil || toArchive.Platform == "" || toArchive.Error == "" || toArchive.Stack == "" {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "invalidBody", "Bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.actualCrashArchive(w, ap, toArchive)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) actualCrashArchive(w http.ResponseWriter, ap App, toArchive ArchivedCrash) {}
|
func (b *Backend) actualCrashArchive(w http.ResponseWriter, ap App, toArchive ArchivedCrash) {
|
||||||
|
crash := ap.CrashTable()
|
||||||
|
if crash == nil {
|
||||||
|
ReturnError(w, http.StatusInternalServerError, "misconfigured", "Server Misconfigured")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := crash.Archive(toArchive)
|
||||||
|
if err != nil {
|
||||||
|
log.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
|
|||||||
if ext, is := apps[i].(ExtendedApp); is {
|
if ext, is := apps[i].(ExtendedApp); is {
|
||||||
b.m.HandleFunc("/"+apps[i].AppID()+"/", ext.Extension)
|
b.m.HandleFunc("/"+apps[i].AppID()+"/", ext.Extension)
|
||||||
}
|
}
|
||||||
if !hasLog && apps[i].LogTable() != nil {
|
if !hasLog && apps[i].CountTable() != nil {
|
||||||
hasLog = true
|
hasLog = true
|
||||||
}
|
}
|
||||||
if !hasCrash && apps[i].CrashTable() != nil {
|
if !hasCrash && apps[i].CrashTable() != nil {
|
||||||
@@ -45,7 +45,8 @@ func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if hasLog {
|
if hasLog {
|
||||||
b.m.HandleFunc("POST /log/{uuid}", b.log)
|
b.m.HandleFunc("POST /count/{uuid}", b.countLog)
|
||||||
|
b.m.HandleFunc("GET /count", b.getCount)
|
||||||
}
|
}
|
||||||
if hasCrash {
|
if hasCrash {
|
||||||
b.m.HandleFunc("POST /crash", b.reportCrash)
|
b.m.HandleFunc("POST /crash", b.reportCrash)
|
||||||
@@ -61,7 +62,7 @@ func (b *Backend) cleanupLoop() {
|
|||||||
oldTim := time.Now().Add(-30 * 24 * time.Hour)
|
oldTim := time.Now().Add(-30 * 24 * time.Hour)
|
||||||
old := (oldTim.Year() * 10000) + (int(oldTim.Month()) * 100) + oldTim.Day()
|
old := (oldTim.Year() * 10000) + (int(oldTim.Month()) * 100) + oldTim.Day()
|
||||||
for _, a := range b.apps {
|
for _, a := range b.apps {
|
||||||
tab := a.LogTable()
|
tab := a.CountTable()
|
||||||
if tab == nil {
|
if tab == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -74,12 +75,16 @@ func (b *Backend) EnableManagementKey(managementID string) {
|
|||||||
b.managementKeyID = managementID
|
b.managementKeyID = managementID
|
||||||
b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.managementDeleteCrash)
|
b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.managementDeleteCrash)
|
||||||
b.m.HandleFunc("POST /{appID}/crash/archive", b.managementArchiveCrash)
|
b.m.HandleFunc("POST /{appID}/crash/archive", b.managementArchiveCrash)
|
||||||
|
b.m.HandleFunc("GET /{appID}/count", b.getCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) AddUserAuth(userTable Table[User], privKey, pubKey []byte) {
|
func (b *Backend) AddUserAuth(userTable Table[User], privKey, pubKey []byte) {
|
||||||
b.userTable = userTable
|
b.userTable = userTable
|
||||||
b.jwtPriv = privKey
|
b.jwtPriv = privKey
|
||||||
b.jwtPub = pubKey
|
b.jwtPub = pubKey
|
||||||
|
b.m.HandleFunc("POST /user/create", b.createUser)
|
||||||
|
b.m.HandleFunc("DELETE /user/{userID}", b.deleteUser)
|
||||||
|
b.m.HandleFunc("POST /user/login", b.login)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) HandleFunc(pattern string, h http.HandlerFunc) {
|
func (b *Backend) HandleFunc(pattern string, h http.HandlerFunc) {
|
||||||
|
|||||||
@@ -19,17 +19,18 @@ type Table[T IDStruct] interface {
|
|||||||
PartUpdate(ID string, update map[string]any) error
|
PartUpdate(ID string, update map[string]any) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogTable interface {
|
type CountTable interface {
|
||||||
Table[Log]
|
Table[CountLog]
|
||||||
// Remove all Log items that have a Log.Date value less then the given value.
|
// Remove all Log items that have a CountLog.Date value less then the given value.
|
||||||
RemoveOldLogs(date int)
|
RemoveOldLogs(date int)
|
||||||
|
Count(platform string) int
|
||||||
}
|
}
|
||||||
|
|
||||||
type CrashTable interface {
|
type CrashTable interface {
|
||||||
Table[CrashReport]
|
Table[CrashReport]
|
||||||
// Move a crash type to archive. All instances that perfectly match that appear in CrashReport.Individual should be deleted.
|
// Move a crash type to archive. All instances that perfectly match that appear in CrashReport.Individual should be deleted.
|
||||||
// If a CrashReport ends up with an empty Individual array it should also be deleted.
|
// If a CrashReport ends up with an empty Individual array it should also be deleted.
|
||||||
Archive(ArchivedCrash)
|
Archive(ArchivedCrash) error
|
||||||
IsArchived(IndividualCrash) bool
|
IsArchived(IndividualCrash) bool
|
||||||
// Add the IndividualCrash report to the crash table. If a CrashReport exists that matches, then it gets added to CrashReport.Individual.
|
// Add the IndividualCrash report to the crash table. If a CrashReport exists that matches, then it gets added to CrashReport.Individual.
|
||||||
// If an IndividualCrash exists that is a perfect match, Count is incremented instead of adding it to the array.
|
// If an IndividualCrash exists that is a perfect match, Count is incremented instead of adding it to the array.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ParsedHeader struct {
|
type ParsedHeader struct {
|
||||||
User *ReqUser
|
User *ReqestUser
|
||||||
Key *ApiKey
|
Key *ApiKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,48 @@
|
|||||||
package darkstorm
|
package darkstorm
|
||||||
|
|
||||||
import "net/http"
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
type Log struct {
|
type CountLog struct {
|
||||||
ID string
|
ID string
|
||||||
Platform string
|
Platform string
|
||||||
Date int
|
Date int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Log) GetID() string {
|
func (c CountLog) GetID() string {
|
||||||
return l.ID
|
return c.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) log(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := b.VerifyHeader(w, r, "count", false)
|
||||||
|
if hdr == nil {
|
||||||
|
if err == nil {
|
||||||
|
log.Println("request key parsing error:", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) getCount(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := b.VerifyHeader(w, r, "management", true)
|
||||||
|
if hdr == nil {
|
||||||
|
if err == nil {
|
||||||
|
log.Println("request key parsing error:", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var ap App
|
||||||
|
if hdr.Key.AppID == b.managementKeyID {
|
||||||
|
ap = b.apps[r.PathValue("appID")]
|
||||||
|
if ap == nil {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "badRequest", "Bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ap = b.GetApp(hdr.Key)
|
||||||
|
}
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,24 @@ func generateSalt() (string, error) {
|
|||||||
return base64.RawStdEncoding.EncodeToString(out), err
|
return base64.RawStdEncoding.EncodeToString(out), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReqestUser struct {
|
||||||
|
Perm map[string]string
|
||||||
|
ID string
|
||||||
|
Username string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) GenerateJWT(r *ReqestUser) (string, error) {
|
||||||
|
if b.jwtPriv == nil || b.jwtPub == nil {
|
||||||
|
return "", errors.New("user management not enabled")
|
||||||
|
}
|
||||||
|
return jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.RegisteredClaims{
|
||||||
|
ID: r.ID,
|
||||||
|
Issuer: "darkstorm.tech",
|
||||||
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(12 * time.Hour)),
|
||||||
|
}).SignedString(b.jwtPriv)
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Perm map[string]string `json:"perm" bson:"perm"`
|
Perm map[string]string `json:"perm" bson:"perm"`
|
||||||
ID string `json:"id" bson:"_id"`
|
ID string `json:"id" bson:"_id"`
|
||||||
@@ -59,27 +77,12 @@ func NewUser(username, password, email string) (User, error) {
|
|||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReqUser struct {
|
|
||||||
Perm map[string]string
|
|
||||||
ID string
|
|
||||||
Username string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Backend) generateJWT(r *ReqUser) (string, error) {
|
|
||||||
return jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.RegisteredClaims{
|
|
||||||
ID: r.ID,
|
|
||||||
Issuer: "darkstorm.tech",
|
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(12 * time.Hour)),
|
|
||||||
}).SignedString(b.jwtPriv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u User) GetID() string {
|
func (u User) GetID() string {
|
||||||
return u.ID
|
return u.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) toReqUser() *ReqUser {
|
func (u User) toReqUser() *ReqestUser {
|
||||||
return &ReqUser{
|
return &ReqestUser{
|
||||||
Perm: u.Perm,
|
Perm: u.Perm,
|
||||||
ID: u.ID,
|
ID: u.ID,
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
@@ -114,7 +117,7 @@ type createUserReturn struct {
|
|||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) CreateUser(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) createUser(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.VerifyHeader(w, r, "user", false)
|
hdr, err := b.VerifyHeader(w, r, "user", false)
|
||||||
if hdr == nil {
|
if hdr == nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -164,7 +167,7 @@ func (b *Backend) CreateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
var ret createUserReturn
|
var ret createUserReturn
|
||||||
ret.Username = u.Username
|
ret.Username = u.Username
|
||||||
ret.Token, err = b.generateJWT(u.toReqUser())
|
ret.Token, err = b.GenerateJWT(u.toReqUser())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
return
|
return
|
||||||
@@ -173,6 +176,27 @@ func (b *Backend) CreateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(ret)
|
json.NewEncoder(w).Encode(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Backend) deleteUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := b.VerifyHeader(w, r, "management", true)
|
||||||
|
if hdr == nil {
|
||||||
|
if err == nil {
|
||||||
|
log.Println("request key parsing error:", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userID := r.PathValue("userID")
|
||||||
|
if userID == "" {
|
||||||
|
ReturnError(w, http.StatusBadRequest, "badRequest", "Bad Request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.userMutex.Lock()
|
||||||
|
defer b.userMutex.Unlock()
|
||||||
|
err = b.userTable.Remove(userID)
|
||||||
|
if err != nil && err != ErrNotFound {
|
||||||
|
log.Println("error deleting user:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type loginRequest struct {
|
type loginRequest struct {
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
@@ -184,13 +208,12 @@ type loginReturn struct {
|
|||||||
Timeout int64 `json:"timeout"`
|
Timeout int64 `json:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) Login(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) login(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.VerifyHeader(w, r, "user", false)
|
||||||
if hdr.Key == nil || !hdr.Key.Perm["user"] || errors.Is(err, ErrApiKeyUnauthorized) {
|
if hdr == nil {
|
||||||
ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application not authorized")
|
if err == nil {
|
||||||
return
|
log.Println("request key parsing error:", err)
|
||||||
} else if err != nil {
|
}
|
||||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
@@ -224,7 +247,7 @@ func (b *Backend) Login(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if u.Password == hash {
|
if u.Password == hash {
|
||||||
ret.Token, err = b.generateJWT(u.toReqUser())
|
ret.Token, err = b.GenerateJWT(u.toReqUser())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user