Channels style UUID (#1369)
* server channels style uuids * webapp channels style uuids
This commit is contained in:
parent
c30c17f684
commit
4feafb9806
28 changed files with 204 additions and 102 deletions
|
@ -1170,7 +1170,7 @@ func (a *API) handlePostWorkspaceRegenerateSignupToken(w http.ResponseWriter, r
|
|||
auditRec := a.makeAuditRecord(r, "regenerateSignupToken", audit.Fail)
|
||||
defer a.audit.LogRecord(audit.LevelModify, auditRec)
|
||||
|
||||
workspace.SignupToken = utils.CreateGUID()
|
||||
workspace.SignupToken = utils.NewID(utils.IDTypeToken)
|
||||
|
||||
err = a.app.UpsertWorkspaceSignupToken(*workspace)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
"github.com/mattermost/focalboard/server/services/auth"
|
||||
"github.com/mattermost/focalboard/server/services/store"
|
||||
"github.com/mattermost/focalboard/server/utils"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
|
||||
|
@ -102,8 +102,8 @@ func (a *App) Login(username, email, password, mfaToken string) (string, error)
|
|||
}
|
||||
|
||||
session := model.Session{
|
||||
ID: uuid.New().String(),
|
||||
Token: uuid.New().String(),
|
||||
ID: utils.NewID(utils.IDTypeSession),
|
||||
Token: utils.NewID(utils.IDTypeToken),
|
||||
UserID: user.ID,
|
||||
AuthService: authService,
|
||||
Props: map[string]interface{}{},
|
||||
|
@ -149,7 +149,7 @@ func (a *App) RegisterUser(username, email, password string) error {
|
|||
}
|
||||
|
||||
err = a.store.CreateUser(&model.User{
|
||||
ID: uuid.New().String(),
|
||||
ID: utils.NewID(utils.IDTypeUser),
|
||||
Username: username,
|
||||
Email: email,
|
||||
Password: auth.HashPassword(password),
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
var mockUser = &model.User{
|
||||
ID: utils.CreateGUID(),
|
||||
ID: utils.NewID(utils.IDTypeUser),
|
||||
Username: "testUsername",
|
||||
Email: "testEmail",
|
||||
Password: auth.HashPassword("testPassword"),
|
||||
|
|
|
@ -20,7 +20,7 @@ func (a *App) SaveFile(reader io.Reader, workspaceID, rootID, filename string) (
|
|||
fileExtension = ".jpg"
|
||||
}
|
||||
|
||||
createdFilename := fmt.Sprintf(`%s%s`, utils.CreateGUID(), fileExtension)
|
||||
createdFilename := fmt.Sprintf(`%s%s`, utils.NewID(utils.IDTypeNone), fileExtension)
|
||||
filePath := filepath.Join(workspaceID, rootID, createdFilename)
|
||||
|
||||
_, appErr := a.filesBackend.WriteFile(reader, filePath)
|
||||
|
|
|
@ -18,12 +18,12 @@ func TestGetSharing(t *testing.T) {
|
|||
defer tearDown()
|
||||
|
||||
container := st.Container{
|
||||
WorkspaceID: utils.CreateGUID(),
|
||||
WorkspaceID: utils.NewID(utils.IDTypeWorkspace),
|
||||
}
|
||||
|
||||
t.Run("should get a sharing successfully", func(t *testing.T) {
|
||||
want := &model.Sharing{
|
||||
ID: utils.CreateGUID(),
|
||||
ID: utils.NewID(utils.IDTypeBlock),
|
||||
Enabled: true,
|
||||
Token: "token",
|
||||
ModifiedBy: "otherid",
|
||||
|
@ -67,10 +67,10 @@ func TestUpsertSharing(t *testing.T) {
|
|||
defer tearDown()
|
||||
|
||||
container := st.Container{
|
||||
WorkspaceID: utils.CreateGUID(),
|
||||
WorkspaceID: utils.NewID(utils.IDTypeWorkspace),
|
||||
}
|
||||
sharing := model.Sharing{
|
||||
ID: utils.CreateGUID(),
|
||||
ID: utils.NewID(utils.IDTypeBlock),
|
||||
Enabled: true,
|
||||
Token: "token",
|
||||
ModifiedBy: "otherid",
|
||||
|
|
|
@ -16,7 +16,7 @@ func (a *App) GetRootWorkspace() (*model.Workspace, error) {
|
|||
if workspace == nil {
|
||||
workspace = &model.Workspace{
|
||||
ID: workspaceID,
|
||||
SignupToken: utils.CreateGUID(),
|
||||
SignupToken: utils.NewID(utils.IDTypeToken),
|
||||
}
|
||||
err := a.store.UpsertWorkspaceSignupToken(*workspace)
|
||||
if err != nil {
|
||||
|
|
|
@ -22,7 +22,7 @@ type TestHelper struct {
|
|||
}
|
||||
|
||||
var mockSession = &model.Session{
|
||||
ID: utils.CreateGUID(),
|
||||
ID: utils.NewID(utils.IDTypeSession),
|
||||
Token: "goodToken",
|
||||
UserID: "12345",
|
||||
CreateAt: time.Now().Unix() - 2000,
|
||||
|
|
|
@ -7,7 +7,6 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/golang-migrate/migrate/v4 v4.14.1
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/lib/pq v1.10.2
|
||||
|
|
|
@ -17,8 +17,8 @@ func TestGetBlocks(t *testing.T) {
|
|||
require.NoError(t, resp.Error)
|
||||
initialCount := len(blocks)
|
||||
|
||||
blockID1 := utils.CreateGUID()
|
||||
blockID2 := utils.CreateGUID()
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
newBlocks := []model.Block{
|
||||
{
|
||||
ID: blockID1,
|
||||
|
@ -58,9 +58,9 @@ func TestPostBlock(t *testing.T) {
|
|||
require.NoError(t, resp.Error)
|
||||
initialCount := len(blocks)
|
||||
|
||||
blockID1 := utils.CreateGUID()
|
||||
blockID2 := utils.CreateGUID()
|
||||
blockID3 := utils.CreateGUID()
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
blockID3 := utils.NewID(utils.IDTypeBlock)
|
||||
|
||||
t.Run("Create a single block", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
|
@ -152,7 +152,7 @@ func TestPatchBlock(t *testing.T) {
|
|||
th := SetupTestHelper().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
blockID := utils.CreateGUID()
|
||||
blockID := utils.NewID(utils.IDTypeBlock)
|
||||
|
||||
block := model.Block{
|
||||
ID: blockID,
|
||||
|
@ -253,7 +253,7 @@ func TestDeleteBlock(t *testing.T) {
|
|||
require.NoError(t, resp.Error)
|
||||
initialCount := len(blocks)
|
||||
|
||||
blockID := utils.CreateGUID()
|
||||
blockID := utils.NewID(utils.IDTypeBlock)
|
||||
t.Run("Create a block", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
ID: blockID,
|
||||
|
@ -298,9 +298,10 @@ func TestGetSubtree(t *testing.T) {
|
|||
require.NoError(t, resp.Error)
|
||||
initialCount := len(blocks)
|
||||
|
||||
parentBlockID := utils.CreateGUID()
|
||||
childBlockID1 := utils.CreateGUID()
|
||||
childBlockID2 := utils.CreateGUID()
|
||||
parentBlockID := utils.NewID(utils.IDTypeBlock)
|
||||
childBlockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
childBlockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
|
||||
t.Run("Create the block structure", func(t *testing.T) {
|
||||
newBlocks := []model.Block{
|
||||
{
|
||||
|
|
|
@ -12,8 +12,8 @@ func TestSharing(t *testing.T) {
|
|||
th := SetupTestHelper().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
rootID := utils.CreateGUID()
|
||||
token := utils.CreateGUID()
|
||||
rootID := utils.NewID(utils.IDTypeBlock)
|
||||
token := utils.NewID(utils.IDTypeToken)
|
||||
|
||||
t.Run("Check no initial sharing", func(t *testing.T) {
|
||||
sharing, resp := th.Client.GetSharing(rootID)
|
||||
|
|
|
@ -23,7 +23,7 @@ func TestUserRegister(t *testing.T) {
|
|||
registerRequest := &api.RegisterRequest{
|
||||
Username: fakeUsername,
|
||||
Email: fakeEmail,
|
||||
Password: utils.CreateGUID(),
|
||||
Password: utils.NewID(utils.IDTypeNone),
|
||||
}
|
||||
success, resp := th.Client.Register(registerRequest)
|
||||
require.NoError(t, resp.Error)
|
||||
|
@ -44,7 +44,7 @@ func TestUserLogin(t *testing.T) {
|
|||
Type: "normal",
|
||||
Username: "nonexistuser",
|
||||
Email: "",
|
||||
Password: utils.CreateGUID(),
|
||||
Password: utils.NewID(utils.IDTypeNone),
|
||||
}
|
||||
data, resp := th.Client.Login(loginRequest)
|
||||
require.Error(t, resp.Error)
|
||||
|
@ -52,7 +52,7 @@ func TestUserLogin(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("with registered user", func(t *testing.T) {
|
||||
password := utils.CreateGUID()
|
||||
password := utils.NewID(utils.IDTypeNone)
|
||||
// register
|
||||
registerRequest := &api.RegisterRequest{
|
||||
Username: fakeUsername,
|
||||
|
@ -89,7 +89,7 @@ func TestGetMe(t *testing.T) {
|
|||
|
||||
t.Run("logged in", func(t *testing.T) {
|
||||
// register
|
||||
password := utils.CreateGUID()
|
||||
password := utils.NewID(utils.IDTypeNone)
|
||||
registerRequest := &api.RegisterRequest{
|
||||
Username: fakeUsername,
|
||||
Email: fakeEmail,
|
||||
|
@ -124,7 +124,7 @@ func TestGetUser(t *testing.T) {
|
|||
defer th.TearDown()
|
||||
|
||||
// register
|
||||
password := utils.CreateGUID()
|
||||
password := utils.NewID(utils.IDTypeNone)
|
||||
registerRequest := &api.RegisterRequest{
|
||||
Username: fakeUsername,
|
||||
Email: fakeEmail,
|
||||
|
@ -169,7 +169,7 @@ func TestUserChangePassword(t *testing.T) {
|
|||
defer th.TearDown()
|
||||
|
||||
// register
|
||||
password := utils.CreateGUID()
|
||||
password := utils.NewID(utils.IDTypeNone)
|
||||
registerRequest := &api.RegisterRequest{
|
||||
Username: fakeUsername,
|
||||
Email: fakeEmail,
|
||||
|
@ -197,7 +197,7 @@ func TestUserChangePassword(t *testing.T) {
|
|||
// change password
|
||||
success, resp = th.Client.UserChangePassword(originalMe.ID, &api.ChangePasswordRequest{
|
||||
OldPassword: password,
|
||||
NewPassword: utils.CreateGUID(),
|
||||
NewPassword: utils.NewID(utils.IDTypeNone),
|
||||
})
|
||||
require.NoError(t, resp.Error)
|
||||
require.True(t, success)
|
||||
|
@ -216,7 +216,7 @@ func TestWorkspaceUploadFile(t *testing.T) {
|
|||
defer th.TearDown()
|
||||
|
||||
workspaceID := "0"
|
||||
rootID := utils.CreateGUID()
|
||||
rootID := utils.NewID(utils.IDTypeBlock)
|
||||
data := randomBytes(t, 1024)
|
||||
result, resp := th.Client.WorkspaceUploadFile(workspaceID, rootID, bytes.NewReader(data))
|
||||
require.Error(t, resp.Error)
|
||||
|
@ -228,7 +228,7 @@ func TestWorkspaceUploadFile(t *testing.T) {
|
|||
defer th.TearDown()
|
||||
|
||||
workspaceID := "0"
|
||||
rootID := utils.CreateGUID()
|
||||
rootID := utils.NewID(utils.IDTypeBlock)
|
||||
data := randomBytes(t, 1024)
|
||||
result, resp := th.Client.WorkspaceUploadFile(workspaceID, rootID, bytes.NewReader(data))
|
||||
require.NoError(t, resp.Error)
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
@ -30,6 +29,7 @@ import (
|
|||
"github.com/mattermost/focalboard/server/services/store/sqlstore"
|
||||
"github.com/mattermost/focalboard/server/services/telemetry"
|
||||
"github.com/mattermost/focalboard/server/services/webhook"
|
||||
"github.com/mattermost/focalboard/server/utils"
|
||||
"github.com/mattermost/focalboard/server/web"
|
||||
"github.com/mattermost/focalboard/server/ws"
|
||||
"github.com/oklog/run"
|
||||
|
@ -37,7 +37,6 @@ import (
|
|||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/shared/filestore"
|
||||
"github.com/mattermost/mattermost-server/v6/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -170,8 +169,8 @@ func New(params Params) (*Server, error) {
|
|||
// Init telemetry
|
||||
telemetryID := settings["TelemetryID"]
|
||||
if len(telemetryID) == 0 {
|
||||
telemetryID = uuid.New().String()
|
||||
if err = params.DBStore.SetSystemSetting("TelemetryID", uuid.New().String()); err != nil {
|
||||
telemetryID = utils.NewID(utils.IDTypeNone)
|
||||
if err = params.DBStore.SetSystemSetting("TelemetryID", telemetryID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +283,7 @@ func (s *Server) Start() error {
|
|||
s.metricsUpdaterTask = scheduler.CreateRecurringTask("updateMetrics", metricsUpdater, updateMetricsTaskFrequency)
|
||||
|
||||
if s.config.Telemetry {
|
||||
firstRun := utils.MillisFromTime(time.Now())
|
||||
firstRun := utils.GetMillis()
|
||||
s.telemetry.RunTelemetryJob(firstRun)
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ func (s *SQLStore) UpsertWorkspaceSignupToken(workspace model.Workspace) error {
|
|||
|
||||
func (s *SQLStore) UpsertWorkspaceSettings(workspace model.Workspace) error {
|
||||
now := time.Now().Unix()
|
||||
signupToken := utils.CreateGUID()
|
||||
signupToken := utils.NewID(utils.IDTypeToken)
|
||||
|
||||
settingsJSON, err := json.Marshal(workspace.Settings)
|
||||
if err != nil {
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
"github.com/mattermost/focalboard/server/services/store"
|
||||
"github.com/mattermost/focalboard/server/utils"
|
||||
)
|
||||
|
||||
func StoreTestUserStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) {
|
||||
|
@ -47,7 +47,7 @@ func testGetWorkspaceUsers(t *testing.T, store store.Store) {
|
|||
require.Equal(t, 0, len(users))
|
||||
require.Equal(t, sql.ErrNoRows, err)
|
||||
|
||||
userID := uuid.New().String()
|
||||
userID := utils.NewID(utils.IDTypeUser)
|
||||
|
||||
err = store.CreateUser(&model.User{
|
||||
ID: userID,
|
||||
|
@ -71,7 +71,7 @@ func testGetWorkspaceUsers(t *testing.T, store store.Store) {
|
|||
|
||||
func testCreateAndGetUser(t *testing.T, store store.Store) {
|
||||
user := &model.User{
|
||||
ID: uuid.New().String(),
|
||||
ID: utils.NewID(utils.IDTypeUser),
|
||||
Username: "damao",
|
||||
Email: "mock@email.com",
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func testCreateAndGetUser(t *testing.T, store store.Store) {
|
|||
|
||||
func testCreateAndUpdateUser(t *testing.T, store store.Store) {
|
||||
user := &model.User{
|
||||
ID: uuid.New().String(),
|
||||
ID: utils.NewID(utils.IDTypeUser),
|
||||
}
|
||||
err := store.CreateUser(user)
|
||||
require.NoError(t, err)
|
||||
|
@ -129,7 +129,7 @@ func testCreateAndUpdateUser(t *testing.T, store store.Store) {
|
|||
})
|
||||
|
||||
t.Run("UpdateUserPassword", func(t *testing.T) {
|
||||
newPassword := uuid.New().String()
|
||||
newPassword := utils.NewID(utils.IDTypeNone)
|
||||
err := store.UpdateUserPassword(user.Username, newPassword)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -140,7 +140,7 @@ func testCreateAndUpdateUser(t *testing.T, store store.Store) {
|
|||
})
|
||||
|
||||
t.Run("UpdateUserPasswordByID", func(t *testing.T) {
|
||||
newPassword := uuid.New().String()
|
||||
newPassword := utils.NewID(utils.IDTypeNone)
|
||||
err := store.UpdateUserPasswordByID(user.ID, newPassword)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -155,7 +155,7 @@ func testCreateAndGetRegisteredUserCount(t *testing.T, store store.Store) {
|
|||
randomN := int(time.Now().Unix() % 10)
|
||||
for i := 0; i < randomN; i++ {
|
||||
err := store.CreateUser(&model.User{
|
||||
ID: uuid.New().String(),
|
||||
ID: utils.NewID(utils.IDTypeUser),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func testUpsertWorkspaceSignupToken(t *testing.T, store store.Store) {
|
|||
workspaceID := "0"
|
||||
workspace := &model.Workspace{
|
||||
ID: workspaceID,
|
||||
SignupToken: utils.CreateGUID(),
|
||||
SignupToken: utils.NewID(utils.IDTypeToken),
|
||||
}
|
||||
|
||||
// insert
|
||||
|
@ -55,7 +55,7 @@ func testUpsertWorkspaceSignupToken(t *testing.T, store store.Store) {
|
|||
require.Equal(t, workspace.SignupToken, got.SignupToken)
|
||||
|
||||
// update signup token
|
||||
workspace.SignupToken = utils.CreateGUID()
|
||||
workspace.SignupToken = utils.NewID(utils.IDTypeToken)
|
||||
err = store.UpsertWorkspaceSignupToken(*workspace)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -108,7 +108,7 @@ func testGetWorkspaceCount(t *testing.T, store store.Store) {
|
|||
workspaceID := fmt.Sprintf("%d", i)
|
||||
workspace := &model.Workspace{
|
||||
ID: workspaceID,
|
||||
SignupToken: utils.CreateGUID(),
|
||||
SignupToken: utils.NewID(utils.IDTypeToken),
|
||||
}
|
||||
|
||||
err := store.UpsertWorkspaceSignupToken(*workspace)
|
||||
|
|
|
@ -1,28 +1,52 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
// CreateGUID returns a random GUID.
|
||||
func CreateGUID() string {
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
type IDType byte
|
||||
|
||||
return uuid
|
||||
const (
|
||||
IDTypeNone IDType = '7'
|
||||
IDTypeWorkspace IDType = 'w'
|
||||
IDTypeBoard IDType = 'b'
|
||||
IDTypeCard IDType = 'c'
|
||||
IDTypeView IDType = 'v'
|
||||
IDTypeSession IDType = 's'
|
||||
IDTypeUser IDType = 'u'
|
||||
IDTypeToken IDType = 'k'
|
||||
IDTypeBlock IDType = 'a'
|
||||
)
|
||||
|
||||
// NewId is a globally unique identifier. It is a [A-Z0-9] string 27
|
||||
// characters long. It is a UUID version 4 Guid that is zbased32 encoded
|
||||
// with the padding stripped off, and a one character alpha prefix indicating the
|
||||
// type of entity or a `7` if unknown type.
|
||||
func NewID(idType IDType) string {
|
||||
return string(idType) + mm_model.NewId()
|
||||
}
|
||||
|
||||
// GetMillis is a convenience method to get milliseconds since epoch.
|
||||
func GetMillis() int64 {
|
||||
return time.Now().UnixNano() / int64(time.Millisecond)
|
||||
return mm_model.GetMillis()
|
||||
}
|
||||
|
||||
// GetMillisForTime is a convenience method to get milliseconds since epoch for provided Time.
|
||||
func GetMillisForTime(thisTime time.Time) int64 {
|
||||
return mm_model.GetMillisForTime(thisTime)
|
||||
}
|
||||
|
||||
// GetTimeForMillis is a convenience method to get time.Time for milliseconds since epoch.
|
||||
func GetTimeForMillis(millis int64) time.Time {
|
||||
return mm_model.GetTimeForMillis(millis)
|
||||
}
|
||||
|
||||
// SecondsToMillis is a convenience method to convert seconds to milliseconds.
|
||||
func SecondsToMillis(seconds int64) int64 {
|
||||
return seconds * 1000
|
||||
}
|
||||
|
||||
func StructToMap(v interface{}) (m map[string]interface{}) {
|
||||
|
|
|
@ -41,7 +41,7 @@ interface Block {
|
|||
function createBlock(block?: Block): Block {
|
||||
const now = Date.now()
|
||||
return {
|
||||
id: block?.id || Utils.createGuid(),
|
||||
id: block?.id || Utils.createGuid(Utils.blockTypeToIDType(block?.type)),
|
||||
schema: 1,
|
||||
workspaceId: block?.workspaceId || '',
|
||||
parentId: block?.parentId || '',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Utils} from '../utils'
|
||||
import {Utils, IDType} from '../utils'
|
||||
|
||||
import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../telemetry/telemetryClient'
|
||||
|
||||
|
@ -40,7 +40,7 @@ function createBoard(block?: Block): Board {
|
|||
const selectProperties = cardProperties.find((o) => o.type === 'select')
|
||||
if (!selectProperties) {
|
||||
const property: IPropertyTemplate = {
|
||||
id: Utils.createGuid(),
|
||||
id: Utils.createGuid(IDType.BlockID),
|
||||
name: 'Status',
|
||||
type: 'select',
|
||||
options: [],
|
||||
|
|
|
@ -8,7 +8,7 @@ import {Board, IPropertyOption, IPropertyTemplate, BoardGroup} from '../../block
|
|||
import {Card} from '../../blocks/card'
|
||||
import {BoardView} from '../../blocks/boardView'
|
||||
import mutator from '../../mutator'
|
||||
import {Utils} from '../../utils'
|
||||
import {Utils, IDType} from '../../utils'
|
||||
import Button from '../../widgets/buttons/button'
|
||||
|
||||
import KanbanCard from './kanbanCard'
|
||||
|
@ -55,7 +55,7 @@ const Kanban = (props: Props) => {
|
|||
Utils.log('onAddGroupClicked')
|
||||
|
||||
const option: IPropertyOption = {
|
||||
id: Utils.createGuid(),
|
||||
id: Utils.createGuid(IDType.BlockID),
|
||||
value: 'New group',
|
||||
color: 'propColorDefault',
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import {Editor} from 'codemirror'
|
|||
import SimpleMDE from 'easymde'
|
||||
import 'easymde/dist/easymde.min.css'
|
||||
|
||||
import {Utils} from '../utils'
|
||||
import {Utils, IDType} from '../utils'
|
||||
import './markdownEditor.scss'
|
||||
|
||||
type Props = {
|
||||
|
@ -25,7 +25,7 @@ type Props = {
|
|||
const MarkdownEditor = (props: Props): JSX. Element => {
|
||||
const {placeholderText, onFocus, onBlur, onChange, text, id} = props
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [uniqueId] = useState(id || Utils.createGuid())
|
||||
const [uniqueId] = useState(id || Utils.createGuid(IDType.None))
|
||||
|
||||
const [active, setActive] = useState(false)
|
||||
const [editorInstance, setEditorInstance] = useState<SimpleMDE>()
|
||||
|
|
|
@ -10,7 +10,7 @@ import {ContentBlock} from '../blocks/contentBlock'
|
|||
import {CommentBlock} from '../blocks/commentBlock'
|
||||
import mutator from '../mutator'
|
||||
import {OctoUtils} from '../octoUtils'
|
||||
import {Utils} from '../utils'
|
||||
import {Utils, IDType} from '../utils'
|
||||
import Editable from '../widgets/editable'
|
||||
import Switch from '../widgets/switch'
|
||||
|
||||
|
@ -110,7 +110,7 @@ const PropertyValueElement = (props:Props): JSX.Element => {
|
|||
onCreate={
|
||||
async (newValue, currentValues) => {
|
||||
const option: IPropertyOption = {
|
||||
id: Utils.createGuid(),
|
||||
id: Utils.createGuid(IDType.BlockID),
|
||||
value: newValue,
|
||||
color: 'propColorDefault',
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ const PropertyValueElement = (props:Props): JSX.Element => {
|
|||
onCreate={
|
||||
async (newValue) => {
|
||||
const option: IPropertyOption = {
|
||||
id: Utils.createGuid(),
|
||||
id: Utils.createGuid(IDType.BlockID),
|
||||
value: newValue,
|
||||
color: 'propColorDefault',
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {ISharing} from '../blocks/sharing'
|
|||
|
||||
import client from '../octoClient'
|
||||
|
||||
import {Utils} from '../utils'
|
||||
import {Utils, IDType} from '../utils'
|
||||
import {sendFlashMessage} from '../components/flashMessages'
|
||||
|
||||
import Button from '../widgets/buttons/button'
|
||||
|
@ -38,7 +38,7 @@ const ShareBoardComponent = React.memo((props: Props): JSX.Element => {
|
|||
const newSharing: ISharing = {
|
||||
id: props.boardId,
|
||||
enabled: true,
|
||||
token: Utils.createGuid(),
|
||||
token: Utils.createGuid(IDType.Token),
|
||||
}
|
||||
return newSharing
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ const ShareBoardComponent = React.memo((props: Props): JSX.Element => {
|
|||
const accept = window.confirm(intl.formatMessage({id: 'ShareBoard.confirmRegenerateToken', defaultMessage: 'This will invalidate previously shared links. Continue?'}))
|
||||
if (accept) {
|
||||
const newSharing: ISharing = sharing || createSharingInfo()
|
||||
newSharing.token = Utils.createGuid()
|
||||
newSharing.token = Utils.createGuid(IDType.Token)
|
||||
await client.setSharing(newSharing)
|
||||
await loadData()
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import {BoardView} from '../../blocks/boardView'
|
|||
|
||||
import {IUser} from '../../user'
|
||||
|
||||
import {Utils} from '../../utils'
|
||||
import {Utils, IDType} from '../../utils'
|
||||
|
||||
import {wrapDNDIntl} from '../../testUtils'
|
||||
|
||||
|
@ -179,7 +179,7 @@ describe('components/table/Table extended', () => {
|
|||
test('should match snapshot with CreatedBy', async () => {
|
||||
const board = TestBlockFactory.createBoard()
|
||||
|
||||
const dateCreatedId = Utils.createGuid()
|
||||
const dateCreatedId = Utils.createGuid(IDType.User)
|
||||
board.fields.cardProperties.push({
|
||||
id: dateCreatedId,
|
||||
name: 'Date Created',
|
||||
|
@ -236,7 +236,7 @@ describe('components/table/Table extended', () => {
|
|||
test('should match snapshot with UpdatedAt', async () => {
|
||||
const board = TestBlockFactory.createBoard()
|
||||
|
||||
const dateUpdatedId = Utils.createGuid()
|
||||
const dateUpdatedId = Utils.createGuid(IDType.User)
|
||||
board.fields.cardProperties.push({
|
||||
id: dateUpdatedId,
|
||||
name: 'Date Updated',
|
||||
|
@ -315,7 +315,7 @@ describe('components/table/Table extended', () => {
|
|||
test('should match snapshot with CreatedBy', async () => {
|
||||
const board = TestBlockFactory.createBoard()
|
||||
|
||||
const createdById = Utils.createGuid()
|
||||
const createdById = Utils.createGuid(IDType.User)
|
||||
board.fields.cardProperties.push({
|
||||
id: createdById,
|
||||
name: 'Created By',
|
||||
|
@ -373,7 +373,7 @@ describe('components/table/Table extended', () => {
|
|||
test('should match snapshot with UpdatedBy', async () => {
|
||||
const board = TestBlockFactory.createBoard()
|
||||
|
||||
const modifiedById = Utils.createGuid()
|
||||
const modifiedById = Utils.createGuid(IDType.User)
|
||||
board.fields.cardProperties.push({
|
||||
id: modifiedById,
|
||||
name: 'Last Modified By',
|
||||
|
|
|
@ -8,7 +8,7 @@ import {Board, IPropertyTemplate} from '../blocks/board'
|
|||
import {IViewType, BoardView, createBoardView} from '../blocks/boardView'
|
||||
import {Constants} from '../constants'
|
||||
import mutator from '../mutator'
|
||||
import {Utils} from '../utils'
|
||||
import {Utils, IDType} from '../utils'
|
||||
import AddIcon from '../widgets/icons/add'
|
||||
import BoardIcon from '../widgets/icons/board'
|
||||
import DeleteIcon from '../widgets/icons/delete'
|
||||
|
@ -43,7 +43,7 @@ const ViewMenu = React.memo((props: Props) => {
|
|||
const currentViewId = activeView.id
|
||||
const newView = createBoardView(activeView)
|
||||
newView.title = `${activeView.title} copy`
|
||||
newView.id = Utils.createGuid()
|
||||
newView.id = Utils.createGuid(IDType.View)
|
||||
mutator.insertBlock(
|
||||
newView,
|
||||
'duplicate view',
|
||||
|
|
|
@ -9,7 +9,7 @@ import {FilterGroup} from './blocks/filterGroup'
|
|||
import octoClient, {OctoClient} from './octoClient'
|
||||
import {OctoUtils} from './octoUtils'
|
||||
import undoManager from './undomanager'
|
||||
import {Utils} from './utils'
|
||||
import {Utils, IDType} from './utils'
|
||||
import {UserSettings} from './userSettings'
|
||||
import TelemetryClient, {TelemetryCategory, TelemetryActions} from './telemetry/telemetryClient'
|
||||
|
||||
|
@ -25,7 +25,7 @@ class Mutator {
|
|||
Utils.assertFailure('UndoManager does not support nested groups')
|
||||
return undefined
|
||||
}
|
||||
this.undoGroupId = Utils.createGuid()
|
||||
this.undoGroupId = Utils.createGuid(IDType.None)
|
||||
return this.undoGroupId
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ class Mutator {
|
|||
}
|
||||
|
||||
const newTemplate = template || {
|
||||
id: Utils.createGuid(),
|
||||
id: Utils.createGuid(IDType.BlockID),
|
||||
name: 'New Property',
|
||||
type: 'text',
|
||||
options: [],
|
||||
|
@ -276,7 +276,7 @@ class Mutator {
|
|||
}
|
||||
const srcTemplate = newBoard.fields.cardProperties[index]
|
||||
const newTemplate: IPropertyTemplate = {
|
||||
id: Utils.createGuid(),
|
||||
id: Utils.createGuid(IDType.BlockID),
|
||||
name: `${srcTemplate.name} copy`,
|
||||
type: srcTemplate.type,
|
||||
options: srcTemplate.options.slice(),
|
||||
|
@ -462,7 +462,7 @@ class Mutator {
|
|||
let option = newTemplate.options.find((o: IPropertyOption) => o.value === oldValue)
|
||||
if (!option) {
|
||||
option = {
|
||||
id: Utils.createGuid(),
|
||||
id: Utils.createGuid(IDType.None),
|
||||
value: oldValue,
|
||||
color: 'propColorDefault',
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ class OctoUtils {
|
|||
const now = Date.now()
|
||||
const newBlocks = blocks.map((block) => {
|
||||
const newBlock = this.hydrateBlock(block)
|
||||
newBlock.id = Utils.createGuid()
|
||||
newBlock.id = Utils.createGuid(Utils.blockTypeToIDType(newBlock.type))
|
||||
newBlock.createAt = now
|
||||
newBlock.updateAt = now
|
||||
idMap[block.id] = newBlock.id
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {createIntl} from 'react-intl'
|
||||
|
||||
import {Utils} from './utils'
|
||||
import {Utils, IDType} from './utils'
|
||||
|
||||
describe('utils', () => {
|
||||
describe('assureProtocol', () => {
|
||||
|
@ -23,6 +23,21 @@ describe('utils', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('createGuid', () => {
|
||||
test('should create 27 char random id for workspace', () => {
|
||||
expect(Utils.createGuid(IDType.Workspace)).toMatch(/^w[ybndrfg8ejkmcpqxot1uwisza345h769]{26}$/)
|
||||
})
|
||||
test('should create 27 char random id for board', () => {
|
||||
expect(Utils.createGuid(IDType.Board)).toMatch(/^b[ybndrfg8ejkmcpqxot1uwisza345h769]{26}$/)
|
||||
})
|
||||
test('should create 27 char random id for card', () => {
|
||||
expect(Utils.createGuid(IDType.Card)).toMatch(/^c[ybndrfg8ejkmcpqxot1uwisza345h769]{26}$/)
|
||||
})
|
||||
test('should create 27 char random id', () => {
|
||||
expect(Utils.createGuid(IDType.None)).toMatch(/^7[ybndrfg8ejkmcpqxot1uwisza345h769]{26}$/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('htmlFromMarkdown', () => {
|
||||
test('should not allow XSS on links href on the webapp', () => {
|
||||
expect(Utils.htmlFromMarkdown('[]("xss-attack="true"other="whatever)')).toBe('<p><a target="_blank" rel="noreferrer" href="%22xss-attack=%22true%22other=%22whatever" title="" onclick="event.stopPropagation();"></a></p>')
|
||||
|
|
|
@ -19,20 +19,84 @@ const IconClass = 'octo-icon'
|
|||
const OpenButtonClass = 'open-button'
|
||||
const SpacerClass = 'octo-spacer'
|
||||
const HorizontalGripClass = 'HorizontalGrip'
|
||||
const base32Alphabet = 'ybndrfg8ejkmcpqxot1uwisza345h769'
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
enum IDType {
|
||||
None = '7',
|
||||
Workspace = 'w',
|
||||
Board = 'b',
|
||||
Card = 'c',
|
||||
View = 'v',
|
||||
Session = 's',
|
||||
User = 'u',
|
||||
Token = 'k',
|
||||
BlockID = 'a',
|
||||
}
|
||||
|
||||
class Utils {
|
||||
static createGuid(): string {
|
||||
const crypto = window.crypto || window.msCrypto
|
||||
function randomDigit() {
|
||||
if (crypto && crypto.getRandomValues) {
|
||||
const rands = new Uint8Array(1)
|
||||
crypto.getRandomValues(rands)
|
||||
return (rands[0] % 16).toString(16)
|
||||
}
|
||||
static createGuid(idType: IDType): string {
|
||||
const data = Utils.randomArray(16)
|
||||
return idType + this.base32encode(data, false)
|
||||
}
|
||||
|
||||
return (Math.floor((Math.random() * 16))).toString(16)
|
||||
static blockTypeToIDType(blockType: string | undefined): IDType {
|
||||
let ret: IDType = IDType.None
|
||||
switch (blockType) {
|
||||
case 'workspace':
|
||||
ret = IDType.Workspace
|
||||
break
|
||||
case 'board':
|
||||
ret = IDType.Board
|
||||
break
|
||||
case 'card':
|
||||
ret = IDType.Card
|
||||
break
|
||||
case 'view':
|
||||
ret = IDType.View
|
||||
break
|
||||
}
|
||||
return 'xxxxxxxx-xxxx-4xxx-8xxx-xxxxxxxxxxxx'.replace(/x/g, randomDigit)
|
||||
return ret
|
||||
}
|
||||
|
||||
static randomArray(size: number): Uint8Array {
|
||||
const crypto = window.crypto || window.msCrypto
|
||||
const rands = new Uint8Array(size)
|
||||
if (crypto && crypto.getRandomValues) {
|
||||
crypto.getRandomValues(rands)
|
||||
} else {
|
||||
for (let i = 0; i < size; i++) {
|
||||
rands[i] = Math.floor((Math.random() * 255))
|
||||
}
|
||||
}
|
||||
return rands
|
||||
}
|
||||
|
||||
static base32encode(data: Int8Array | Uint8Array | Uint8ClampedArray, pad: boolean): string {
|
||||
const dview = new DataView(data.buffer, data.byteOffset, data.byteLength)
|
||||
let bits = 0
|
||||
let value = 0
|
||||
let output = ''
|
||||
|
||||
// adapted from https://github.com/LinusU/base32-encode
|
||||
for (let i = 0; i < dview.byteLength; i++) {
|
||||
value = (value << 8) | dview.getUint8(i)
|
||||
bits += 8
|
||||
|
||||
while (bits >= 5) {
|
||||
output += base32Alphabet[(value >>> (bits - 5)) & 31]
|
||||
bits -= 5
|
||||
}
|
||||
}
|
||||
if (bits > 0) {
|
||||
output += base32Alphabet[(value << (5 - bits)) & 31]
|
||||
}
|
||||
if (pad) {
|
||||
while ((output.length % 8) !== 0) {
|
||||
output += '='
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
static htmlToElement(html: string): HTMLElement {
|
||||
|
@ -512,4 +576,4 @@ class Utils {
|
|||
}
|
||||
}
|
||||
|
||||
export {Utils}
|
||||
export {Utils, IDType}
|
||||
|
|
Loading…
Reference in a new issue