(Probably) finished with docs and interfaces.
Starting to build out the actual logic.
This commit is contained in:
@@ -6,11 +6,13 @@ 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",
|
||||||
appID: "appID",
|
appID: "appID",
|
||||||
death: -1, // unix timestamp 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
|
log: true, // log users
|
||||||
@@ -37,12 +39,12 @@ Users are stored per backend and not per app.
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
id: "UUID",
|
id: "uuid",
|
||||||
username: "username",
|
username: "username",
|
||||||
password: "hashed password",
|
password: "hashed password",
|
||||||
salt: "password salt",
|
salt: "password salt",
|
||||||
email: "email",
|
email: "email",
|
||||||
passwordChange: 0, // unix timestamp of last password change
|
passwordChange: 0, // unix timestamp (seconds) of last password change
|
||||||
perm: {
|
perm: {
|
||||||
appID: "user", // Optional. Apps should have a default permission level if thier appID is not in perm.
|
appID: "user", // Optional. Apps should have a default permission level if thier appID is not in perm.
|
||||||
}
|
}
|
||||||
@@ -99,13 +101,18 @@ If an error status code is returned then the body will be as follows.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`errorCode`'s returned from the main library:
|
||||||
|
|
||||||
|
* invalidKey
|
||||||
|
* API Key is invalid or does not have the needed permission for the request.
|
||||||
|
|
||||||
### Log
|
### Log
|
||||||
|
|
||||||
API Key must have the `log` permission.
|
API Key must have the `log` permission.
|
||||||
|
|
||||||
Request:
|
Request:
|
||||||
|
|
||||||
> POST: /log
|
> POST: /log/{uuid}
|
||||||
|
|
||||||
### Users
|
### Users
|
||||||
|
|
||||||
@@ -171,10 +178,20 @@ Return:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
token: "JWT Token",
|
token: "JWT Token",
|
||||||
|
error: "Error",
|
||||||
timeout: 0, // login attempt timeout (in seconds). If non-zero, token will be empty.
|
timeout: 0, // login attempt timeout (in seconds). If non-zero, token will be empty.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`token` and `error` are mutually exclusive.
|
||||||
|
|
||||||
|
Possible `error` values:
|
||||||
|
|
||||||
|
* timeout
|
||||||
|
* Account is currently timed-out. The `timeout` value will be non-zero.
|
||||||
|
* invalid
|
||||||
|
* Either the username or password is incorrect
|
||||||
|
|
||||||
### Crash Report
|
### Crash Report
|
||||||
|
|
||||||
> TODO: Archive a crash to prevent it being reported again.
|
> TODO: Archive a crash to prevent it being reported again.
|
||||||
@@ -187,6 +204,8 @@ Request:
|
|||||||
|
|
||||||
> POST: /crash
|
> POST: /crash
|
||||||
|
|
||||||
|
Request Body:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
id: "UUID", // This is an ignored value, but it is highly recommended to include it to prevent reporting the same crash multiple times.
|
id: "UUID", // This is an ignored value, but it is highly recommended to include it to prevent reporting the same crash multiple times.
|
||||||
@@ -203,3 +222,29 @@ API Key must have the `management` permission.
|
|||||||
Request:
|
Request:
|
||||||
|
|
||||||
> DELETE: /crash/{crashID}
|
> DELETE: /crash/{crashID}
|
||||||
|
|
||||||
|
With "darkstormManagement" key:
|
||||||
|
|
||||||
|
> DELETE: /{appID}/crash/{crashID}
|
||||||
|
|
||||||
|
#### Archive
|
||||||
|
|
||||||
|
Archive an error, preventing error with these values to be ignored in the future. API Key must have the `management` permission.
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
> POST: /crash/archive
|
||||||
|
|
||||||
|
With "darkstormManagement" key:
|
||||||
|
|
||||||
|
> POST: /{appID}/crash/{crashID}
|
||||||
|
|
||||||
|
Request Body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
error: "error",
|
||||||
|
stack: "full stacktrace",
|
||||||
|
platform: "all", // Limit the archive to a specific platform, or use "all".
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
package darkstorm
|
package darkstorm
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// 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
|
||||||
LogTable() Table[Log]
|
LogTable() Table[Log]
|
||||||
CrashTable() CrashTable
|
CrashTable() CrashTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExtendedApp interface {
|
||||||
|
// Extension is called for any calls to /{appID}/
|
||||||
|
// Alternatively, use Backend.HandleFunc for more customizability
|
||||||
|
Extension(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ package darkstorm
|
|||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
|
type ArchivedCrash struct {
|
||||||
|
Error string
|
||||||
|
Stack string
|
||||||
|
Platform string
|
||||||
|
}
|
||||||
|
|
||||||
type IndividualCrash struct {
|
type IndividualCrash struct {
|
||||||
Platform string
|
Platform string
|
||||||
Error string
|
Error string
|
||||||
@@ -27,7 +33,7 @@ type crashReq struct {
|
|||||||
Stack string
|
Stack string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) ReportCrash(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) reportCrash(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.ParseHeader(r)
|
||||||
if hdr.k == nil || hdr.k.Perm["crash"] {
|
if hdr.k == nil || hdr.k.Perm["crash"] {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
@@ -40,7 +46,20 @@ func (b *Backend) ReportCrash(w http.ResponseWriter, r *http.Request) {
|
|||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
if hdr.k == nil || hdr.k.Perm["management"] {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
//TODO
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) archiveCrash(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.ParseHeader(r)
|
hdr, err := b.ParseHeader(r)
|
||||||
if hdr.k == nil || hdr.k.Perm["management"] {
|
if hdr.k == nil || hdr.k.Perm["management"] {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
|||||||
@@ -2,39 +2,66 @@ package darkstorm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrApiKeyUnauthorized = errors.New("api key invalid")
|
|
||||||
ErrTokenUnauthorized = errors.New("token invalid")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
userTable Table[User]
|
userTable Table[User]
|
||||||
keyTable Table[Key]
|
keyTable Table[ApiKey]
|
||||||
m *http.ServeMux
|
m *http.ServeMux
|
||||||
jwtPriv ed25519.PrivateKey
|
jwtPriv ed25519.PrivateKey
|
||||||
jwtPub ed25519.PublicKey
|
jwtPub ed25519.PublicKey
|
||||||
apps []App
|
apps map[string]App
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBackend(keyTable Table[Key], apps ...App) (*Backend, error) {
|
func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
|
||||||
b := &Backend{
|
b := &Backend{
|
||||||
keyTable: keyTable,
|
keyTable: keyTable,
|
||||||
m: &http.ServeMux{},
|
m: &http.ServeMux{},
|
||||||
apps: apps,
|
apps: make(map[string]App),
|
||||||
|
}
|
||||||
|
var hasLog, hasCrash bool
|
||||||
|
for i := range apps {
|
||||||
|
_, has := b.apps[apps[i].AppID()]
|
||||||
|
if has {
|
||||||
|
return nil, errors.New("duplicate AppIDs found")
|
||||||
|
}
|
||||||
|
b.apps[apps[i].AppID()] = apps[i]
|
||||||
|
if ext, is := apps[i].(ExtendedApp); is {
|
||||||
|
b.m.HandleFunc("/"+apps[i].AppID()+"/", ext.Extension)
|
||||||
|
}
|
||||||
|
if !hasLog && apps[i].LogTable() != nil {
|
||||||
|
hasLog = true
|
||||||
|
}
|
||||||
|
if !hasCrash && apps[i].CrashTable() != nil {
|
||||||
|
hasCrash = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasLog {
|
||||||
|
b.m.HandleFunc("POST /log/{uuid}", b.log)
|
||||||
|
}
|
||||||
|
if hasCrash {
|
||||||
|
b.m.HandleFunc("POST /crash", b.reportCrash)
|
||||||
|
b.m.HandleFunc("DELETE /crash/{crashID}", b.deleteCrash)
|
||||||
|
b.m.HandleFunc("POST /crash/archive", b.archiveCrash)
|
||||||
|
b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.deleteCrash)
|
||||||
|
b.m.HandleFunc("POST /{appID}/crash/archive", b.archiveCrash)
|
||||||
}
|
}
|
||||||
//TODO: register paths to the mux
|
|
||||||
b.startCleanupLoop()
|
b.startCleanupLoop()
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Backend) startCleanupLoop() {
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(24 * time.Hour) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@@ -45,46 +72,19 @@ func (b *Backend) HandleFunc(pattern string, h http.HandlerFunc) {
|
|||||||
b.m.HandleFunc(pattern, h)
|
b.m.HandleFunc(pattern, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) startCleanupLoop() {
|
func (b *Backend) GetApp(a *ApiKey) App {
|
||||||
go func() {
|
return b.apps[a.AppID]
|
||||||
for range time.Tick(6 * time.Hour) {
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParsedHeader struct {
|
type retError struct {
|
||||||
u *ReqUser
|
ErrorCode string `json:"errorCode"`
|
||||||
k *Key
|
ErrorMsg string `json:"errorMsg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) ParseHeader(r *http.Request) (ParsedHeader, error) {
|
func ReturnError(w http.ResponseWriter, status int, code, msg string) {
|
||||||
out := ParsedHeader{}
|
w.WriteHeader(status)
|
||||||
key := r.Header.Get("X-API-Key")
|
json.NewEncoder(w).Encode(retError{
|
||||||
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
ErrorCode: code,
|
||||||
if key != "" {
|
ErrorMsg: msg,
|
||||||
apiKey, err := b.keyTable.Get(key)
|
})
|
||||||
if err != nil {
|
|
||||||
return out, errors.Join(ErrApiKeyUnauthorized, err)
|
|
||||||
}
|
|
||||||
out.k = &apiKey
|
|
||||||
}
|
|
||||||
if token != "" && b.userTable != nil {
|
|
||||||
t, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
|
||||||
return b.jwtPub, nil
|
|
||||||
}, jwt.WithIssuer("darkstorm.tech"), jwt.WithExpirationRequired())
|
|
||||||
if err != nil {
|
|
||||||
return out, errors.Join(ErrTokenUnauthorized, err)
|
|
||||||
}
|
|
||||||
sub, err := t.Claims.GetSubject()
|
|
||||||
if err != nil {
|
|
||||||
return out, errors.Join(ErrTokenUnauthorized, err)
|
|
||||||
}
|
|
||||||
usr, err := b.userTable.Get(sub)
|
|
||||||
if err != nil{
|
|
||||||
return out, errors.Join(ErrTokenUnauthorized, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package darkstorm
|
|||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrIDNotFound = errors.New("id not found in table")
|
ErrNotFound = errors.New("no matches found in table")
|
||||||
)
|
)
|
||||||
|
|
||||||
type IDStruct interface {
|
type IDStruct interface {
|
||||||
@@ -12,12 +12,14 @@ type IDStruct interface {
|
|||||||
|
|
||||||
type Table[T IDStruct] interface {
|
type Table[T IDStruct] interface {
|
||||||
Get(ID string) (data T, err error)
|
Get(ID string) (data T, err error)
|
||||||
|
Find(values map[string]any) ([]T, error)
|
||||||
Insert(data T) error
|
Insert(data T) error
|
||||||
Update(data T) error
|
Remove(ID string) error
|
||||||
Remove(ID string)
|
FullUpdate(ID string, data T) error
|
||||||
|
PartUpdate(ID string, update map[string]any) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type CrashTable interface {
|
type CrashTable interface {
|
||||||
Table[CrashReport]
|
Table[CrashReport]
|
||||||
InsertCrash(report IndividualCrash) error
|
InsertCrash(ID string, report IndividualCrash) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package darkstorm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrApiKeyUnauthorized = errors.New("api key present but invalid")
|
||||||
|
ErrTokenUnauthorized = errors.New("token present but invalid")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ParsedHeader struct {
|
||||||
|
u *ReqUser
|
||||||
|
k *ApiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) ParseHeader(r *http.Request) (ParsedHeader, error) {
|
||||||
|
out := ParsedHeader{}
|
||||||
|
key := r.Header.Get("X-API-Key")
|
||||||
|
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
||||||
|
if key != "" {
|
||||||
|
apiKey, err := b.keyTable.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return out, errors.Join(ErrApiKeyUnauthorized, err)
|
||||||
|
}
|
||||||
|
if apiKey.Death > 0 && time.Unix(apiKey.Death, 0).Before(time.Now()) {
|
||||||
|
return out, ErrApiKeyUnauthorized
|
||||||
|
}
|
||||||
|
out.k = &apiKey
|
||||||
|
}
|
||||||
|
if token != "" && b.userTable != nil {
|
||||||
|
t, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||||
|
return b.jwtPub, nil
|
||||||
|
}, jwt.WithIssuer("darkstorm.tech"), jwt.WithExpirationRequired())
|
||||||
|
if err != nil {
|
||||||
|
return out, errors.Join(ErrTokenUnauthorized, err)
|
||||||
|
}
|
||||||
|
exp, _ := t.Claims.GetExpirationTime()
|
||||||
|
if exp.Time.Before(time.Now()) {
|
||||||
|
return out, ErrTokenUnauthorized
|
||||||
|
}
|
||||||
|
sub, err := t.Claims.GetSubject()
|
||||||
|
if err != nil {
|
||||||
|
return out, errors.Join(ErrTokenUnauthorized, err)
|
||||||
|
}
|
||||||
|
usr, err := b.userTable.Get(sub)
|
||||||
|
if err != nil {
|
||||||
|
return out, errors.Join(ErrTokenUnauthorized, err)
|
||||||
|
}
|
||||||
|
iss, err := t.Claims.GetIssuedAt()
|
||||||
|
if err != nil {
|
||||||
|
return out, errors.Join(ErrTokenUnauthorized, err)
|
||||||
|
}
|
||||||
|
if usr.PasswordChange > 0 && iss.Time.Before(time.Unix(usr.PasswordChange, 0)) {
|
||||||
|
return out, ErrTokenUnauthorized
|
||||||
|
}
|
||||||
|
out.u = usr.toReqUser()
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package darkstorm
|
package darkstorm
|
||||||
|
|
||||||
type Key struct {
|
type ApiKey struct {
|
||||||
Perm map[string]bool
|
Perm map[string]bool
|
||||||
ID string
|
ID string
|
||||||
AppID string
|
AppID string
|
||||||
Death int
|
Death int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Key) GetID() string {
|
func (k ApiKey) GetID() string {
|
||||||
return k.ID
|
return k.ID
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package darkstorm
|
package darkstorm
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
type Log struct {
|
type Log struct {
|
||||||
ID string
|
ID string
|
||||||
Platform string
|
Platform string
|
||||||
@@ -9,3 +11,7 @@ type Log struct {
|
|||||||
func (l Log) GetID() string {
|
func (l Log) GetID() string {
|
||||||
return l.ID
|
return l.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Backend) log(w http.ResponseWriter, r *http.Request) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"golang.org/x/crypto/argon2"
|
"golang.org/x/crypto/argon2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,13 +22,13 @@ func generateSalt() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Perm map[string]string
|
Perm map[string]string `json:"perm" bson:"perm"`
|
||||||
ID string
|
ID string `json:"id" bson:"_id"`
|
||||||
Username string
|
Username string `json:"username" bson:"username"`
|
||||||
Password string
|
Password string `json:"password" bson:"password"`
|
||||||
Salt string
|
Salt string `json:"salt" bson:"salt"`
|
||||||
Email string
|
Email string `json:"email" bson:"email"`
|
||||||
PasswordChange uint64
|
PasswordChange int64 `json:"passwordChange" bson:"passwordChange"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReqUser struct {
|
type ReqUser struct {
|
||||||
@@ -36,35 +37,21 @@ type ReqUser struct {
|
|||||||
Username string
|
Username string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUser(username, password, email string) (*User, error) {
|
func (b *Backend) generateJWT(r *ReqUser) (string, error) {
|
||||||
if len(password) < 12 || len(password) > 128 {
|
return jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.RegisteredClaims{
|
||||||
return nil, ErrPasswordLength
|
ID: r.ID,
|
||||||
}
|
Issuer: "darkstorm.tech",
|
||||||
id, err := uuid.NewV7()
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
if err != nil {
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(12 * time.Hour)),
|
||||||
return nil, err
|
}).SignedString(b.jwtPriv)
|
||||||
}
|
|
||||||
salt, err := generateSalt()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out := &User{
|
|
||||||
Perm: make(map[string]string),
|
|
||||||
ID: id.String(),
|
|
||||||
Username: username,
|
|
||||||
Salt: salt,
|
|
||||||
Email: email,
|
|
||||||
}
|
|
||||||
out.Password, err = out.HashPassword(password)
|
|
||||||
return out, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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() *ReqUser {
|
||||||
return ReqUser{
|
return &ReqUser{
|
||||||
Perm: u.Perm,
|
Perm: u.Perm,
|
||||||
ID: u.ID,
|
ID: u.ID,
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
@@ -114,5 +101,10 @@ type loginReturn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) Login(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) Login(w http.ResponseWriter, r *http.Request) {
|
||||||
//TODO
|
hdr, err := b.ParseHeader(r)
|
||||||
|
if hdr.k == nil || !hdr.k.Perm["user"] || errors.Is(err, ErrApiKeyUnauthorized) {
|
||||||
|
ReturnError(w, http.StatusUnauthorized, "invalidKey", "Application not authorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user