Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "ui/extra/simplerenderer/librm_lines"]
path = ui/extra/simplerenderer/librm_lines
url = https://github.com/RedTTGMoss/librm_lines
16 changes: 14 additions & 2 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/ddvk/rmfakecloud/internal/ui"

"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)

const (
Expand Down Expand Up @@ -116,7 +117,6 @@ func (app *App) Stop() {
}
}


// NewApp constructs an app
func NewApp(cfg *config.Config) App {
debugMode := log.GetLevel() >= log.DebugLevel
Expand Down Expand Up @@ -154,6 +154,7 @@ func NewApp(cfg *config.Config) App {

// Register the middleware
// router.Use(cors.New(corsConfig))
router.Use(userAgentLoggerMiddleware())

if debugMode {
router.Use(requestLoggerMiddleware())
Expand All @@ -180,7 +181,18 @@ func NewApp(cfg *config.Config) App {

app.registerRoutes(router)

uiApp := ui.New(cfg, fsStorage, codeConnector, ntfHub, pcStore, fsStorage, fsStorage, roomMgr, app.mqttBroker)
issueDeviceToken := func(uid, deviceID, deviceDesc string) (string, error) {
claims := &DeviceClaims{
UserID: uid,
DeviceID: deviceID,
DeviceDesc: deviceDesc,
StandardClaims: jwt.StandardClaims{
Audience: APIUsage,
},
}
return common.SignClaims(claims, cfg.JWTSecretKey)
}
uiApp := ui.New(cfg, fsStorage, codeConnector, ntfHub, pcStore, fsStorage, fsStorage, issueDeviceToken, roomMgr, app.mqttBroker)
uiApp.RegisterRoutes(router)

storageapp := fs.NewApp(cfg, fsStorage)
Expand Down
44 changes: 32 additions & 12 deletions internal/app/codeconnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,55 @@ import (
log "github.com/sirupsen/logrus"
)

type codeEntry struct {
uid string
expiresAt time.Time
}

type inMemoryCodeConnector struct {
dict map[string]string
dict map[string]codeEntry
uids map[string]string
lock sync.Mutex
codeValidity time.Duration
}

// CodeConnector matches a code to users
type CodeConnector interface {
//NewCode generates one time code for a user
// NewCode generates one time code for a user
NewCode(uid string) (code string, err error)

//ConsumeCode a code and returns the uid if ofound
// ConsumeCode consumes a code and returns the uid if found
ConsumeCode(code string) (uid string, err error)
// CodeStatus returns expiration and whether the user's code is still valid
CodeStatus(uid string) (expiresAt time.Time, valid bool)
}

// NewCodeConnector constructor
func NewCodeConnector() CodeConnector {
return &inMemoryCodeConnector{
dict: make(map[string]string),
dict: make(map[string]codeEntry),
uids: make(map[string]string),
codeValidity: time.Minute * 5,
}

}

func (conn *inMemoryCodeConnector) NewCode(uid string) (string, error) {
code, err := newUserCode()
if err != nil {
return "", err
}
expiresAt := time.Now().Add(conn.codeValidity)
conn.lock.Lock()
conn.dict[code] = uid
if oldcode, ok := conn.uids[uid]; ok {
delete(conn.dict, oldcode)
}
conn.dict[code] = codeEntry{uid: uid, expiresAt: expiresAt}
conn.uids[uid] = code
conn.lock.Unlock()
go func() {
<-time.After(conn.codeValidity)
if _, err := conn.ConsumeCode(code); err == nil {
log.Infof("removed unused code: %s for uid: %s ", code, uid)
}

}()
return code, nil
}
Expand Down Expand Up @@ -84,14 +89,29 @@ func newUserCode() (code string, err error) {
// return code, nil
}

// ConsumeCode return the userId matching the
// ConsumeCode returns the userId matching the code
func (conn *inMemoryCodeConnector) ConsumeCode(code string) (string, error) {
conn.lock.Lock()
defer conn.lock.Unlock()
if uid, ok := conn.dict[code]; ok {
if ent, ok := conn.dict[code]; ok {
delete(conn.dict, code)
delete(conn.uids, uid)
return uid, nil
delete(conn.uids, ent.uid)
return ent.uid, nil
}
return "", errors.New("code not found")
}

// CodeStatus returns the expiration time and whether the user's code is still valid
func (conn *inMemoryCodeConnector) CodeStatus(uid string) (expiresAt time.Time, valid bool) {
conn.lock.Lock()
defer conn.lock.Unlock()
code, ok := conn.uids[uid]
if !ok {
return time.Time{}, false
}
ent, ok := conn.dict[code]
if !ok {
return time.Time{}, false
}
return ent.expiresAt, true
}
45 changes: 43 additions & 2 deletions internal/app/handlers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app

import (
"bufio"
"bytes"
"crypto/rand"
"encoding/base64"
Expand Down Expand Up @@ -112,6 +113,15 @@ func (app *App) newDevice(c *gin.Context) {
return
}

if user, err := app.userStorer.GetUser(uid); err == nil && user != nil {
user.UpsertRegisteredDevice(tokenRequest.DeviceID, tokenRequest.DeviceDesc, tokenRequest.DeviceLink)
if err := app.userStorer.UpdateUser(user); err != nil {
log.Warn("could not persist registered device: ", err)
}
} else if err != nil {
log.Warn("could not load user for device registration: ", err)
}

c.String(http.StatusOK, tokenString)
}

Expand All @@ -123,6 +133,14 @@ func (app *App) deleteDevice(c *gin.Context) {
return
}
log.Info("Logging out: ", deviceToken.UserID)
if user, err := app.userStorer.GetUser(deviceToken.UserID); err == nil && user != nil {
user.RemoveRegisteredDevice(deviceToken.DeviceID)
if err := app.userStorer.UpdateUser(user); err != nil {
log.Warn("could not update user after device logout: ", err)
}
} else if err != nil {
log.Warn("could not load user on device logout: ", err)
}
c.Status(http.StatusNoContent)
}

Expand Down Expand Up @@ -899,7 +917,13 @@ func (app *App) blobStorageRead(c *gin.Context) {
defer reader.Close()
common.AddHashHeader(c, hash)

c.DataFromReader(http.StatusOK, size, "application/octet-stream", reader, nil)
br := bufio.NewReader(reader)
ct := "application/octet-stream"
if b, err := br.Peek(5); err == nil && string(b) == "%PDF-" {
ct = "application/pdf"
}
c.Header("X-Content-Type-Options", "nosniff")
c.DataFromReader(http.StatusOK, size, ct, br, nil)
}

func (app *App) blobStorageWrite(c *gin.Context) {
Expand Down Expand Up @@ -1032,6 +1056,17 @@ func (app *App) integrationsUpload(c *gin.Context) {
name := common.QueryS("name", c)
fileType := common.QueryS("fileType", c)

ro, err := integrations.IsReadOnly(app.userStorer, uid, integrationID)
if err != nil {
log.Error(fmt.Errorf("can't check integration mode, %v", err))
c.AbortWithStatus(http.StatusInternalServerError)
return
}
if ro {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "integration folder is read-only"})
return
}

integrationProvider, err := integrations.GetStorageIntegrationProvider(app.userStorer, uid, integrationID)

if err != nil {
Expand Down Expand Up @@ -1072,7 +1107,13 @@ func (app *App) integrationsGetFile(c *gin.Context) {

defer reader.Close()

c.DataFromReader(http.StatusOK, size, "application/octet-stream", reader, nil)
br := bufio.NewReader(reader)
ct := "application/octet-stream"
if b, err := br.Peek(5); err == nil && string(b) == "%PDF-" {
ct = "application/pdf"
}
c.Header("X-Content-Type-Options", "nosniff")
c.DataFromReader(http.StatusOK, size, ct, br, nil)
}

func (app *App) integrationsList(c *gin.Context) {
Expand Down
Loading