Skip to content

Commit c4b8da5

Browse files
committed
feat: Sofie Core Groups with Trusted header SOFIE-95
1 parent 5f77a10 commit c4b8da5

File tree

125 files changed

+1780
-4584
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+1780
-4584
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ meteor/.coverage/
44
node_modules
55
**/yarn-error.log
66
scratch/
7+
meteor-settings.json
78

89
# Exclude JetBrains IDE specific files
910
.idea

meteor/.meteor/packages

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,4 @@ [email protected] # Enable TypeScript syntax in .ts and .tsx modules
1919

2020
[email protected] # Meteor's client-side reactive programming library
2121

22-
23-
2422
zodern:types

meteor/__mocks__/_setupMocks.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ jest.mock('meteor/meteor', (...args) => require('./meteor').setup(args), { virtu
1414
jest.mock('meteor/random', (...args) => require('./random').setup(args), { virtual: true })
1515
jest.mock('meteor/check', (...args) => require('./check').setup(args), { virtual: true })
1616
jest.mock('meteor/tracker', (...args) => require('./tracker').setup(args), { virtual: true })
17-
jest.mock('meteor/accounts-base', (...args) => require('./accounts-base').setup(args), { virtual: true })
1817
jest.mock('meteor/ejson', (...args) => require('./ejson').setup(args), { virtual: true })
1918

2019
jest.mock('meteor/mdg:validated-method', (...args) => require('./validated-method').setup(args), { virtual: true })

meteor/__mocks__/accounts-base.ts

Lines changed: 0 additions & 81 deletions
This file was deleted.

meteor/__mocks__/meteor.ts

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MongoMock } from './mongo'
1+
import { USER_PERMISSIONS_HEADER } from '@sofie-automation/meteor-lib/dist/userPermissions'
22

33
let controllableDefer = false
44

@@ -9,27 +9,14 @@ export function useNextTickDefer(): void {
99
controllableDefer = false
1010
}
1111

12-
namespace Meteor {
12+
export namespace Meteor {
1313
export interface Settings {
1414
public: {
1515
[id: string]: any
1616
}
1717
[id: string]: any
1818
}
1919

20-
export interface UserEmail {
21-
address: string
22-
verified: boolean
23-
}
24-
export interface User {
25-
_id?: string
26-
username?: string
27-
emails?: UserEmail[]
28-
createdAt?: number
29-
profile?: any
30-
services?: any
31-
}
32-
3320
export interface ErrorStatic {
3421
new (error: string | number, reason?: string, details?: string): Error
3522
}
@@ -103,22 +90,18 @@ export namespace MeteorMock {
10390
export const settings: any = {}
10491

10592
export const mockMethods: { [name: string]: Function } = {}
106-
export let mockUser: Meteor.User | undefined = undefined
10793
export const mockStartupFunctions: Function[] = []
10894

10995
export const absolutePath = process.cwd()
11096

111-
export function user(): Meteor.User | undefined {
112-
return mockUser
113-
}
114-
export function userId(): string | undefined {
115-
return mockUser ? mockUser._id : undefined
116-
}
11797
function getMethodContext() {
11898
return {
119-
userId: mockUser ? mockUser._id : undefined,
12099
connection: {
121100
clientAddress: '1.1.1.1',
101+
httpHeaders: {
102+
// Default to full permissions for tests
103+
[USER_PERMISSIONS_HEADER]: 'admin',
104+
},
122105
},
123106
unblock: () => {
124107
// noop
@@ -256,7 +239,6 @@ export namespace MeteorMock {
256239
return fcn(...args)
257240
}
258241
}
259-
export let users: MongoMock.Collection<any> | undefined = undefined
260242

261243
// -- Mock functions: --------------------------
262244
/**
@@ -269,12 +251,6 @@ export namespace MeteorMock {
269251

270252
await waitTimeNoFakeTimers(10) // So that any observers or defers has had time to run.
271253
}
272-
export function mockLoginUser(newUser: Meteor.User): void {
273-
mockUser = newUser
274-
}
275-
export function mockSetUsersCollection(usersCollection: MongoMock.Collection<any>): void {
276-
users = usersCollection
277-
}
278254
export function mockSetClientEnvironment(): void {
279255
mockIsClient = true
280256
}

meteor/__mocks__/mongo.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,5 +453,3 @@ export function setup(): any {
453453
Mongo: MongoMock,
454454
}
455455
}
456-
457-
MeteorMock.mockSetUsersCollection(new MongoMock.Collection('Meteor.users'))

meteor/server/Connections.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { logger } from './logging'
44
import { sendTrace } from './api/integration/influx'
55
import { PeripheralDevices } from './collections'
66
import { MetricsGauge } from '@sofie-automation/corelib/dist/prometheus'
7+
import { parseUserPermissions, USER_PERMISSIONS_HEADER } from '@sofie-automation/meteor-lib/dist/userPermissions'
8+
import { Settings } from './Settings'
79

810
const connections = new Set<string>()
911
const connectionsGauge = new MetricsGauge({
@@ -14,6 +16,24 @@ const connectionsGauge = new MetricsGauge({
1416
Meteor.onConnection((conn: Meteor.Connection) => {
1517
// This is called whenever a new ddp-connection is opened (ie a web-client or a peripheral-device)
1618

19+
if (Settings.enableHeaderAuth) {
20+
const userLevel = parseUserPermissions(conn.httpHeaders[USER_PERMISSIONS_HEADER])
21+
22+
// HACK: force the userId of the connection before it can be used.
23+
// This ensures we know the permissions of the connection before it can try to do anything
24+
// This could probably be safely done inside a meteor method, as we only need it when directly modifying a collection in the client,
25+
// but that will cause all the publications to restart when changing the userId.
26+
const connSession = (Meteor as any).server.sessions.get(conn.id)
27+
if (!connSession) {
28+
logger.error(`Failed to find session for ddp connection! "${conn.id}"`)
29+
// Close the connection, it won't be secure
30+
conn.close()
31+
return
32+
} else {
33+
connSession.userId = JSON.stringify(userLevel)
34+
}
35+
}
36+
1737
const connectionId: string = conn.id
1838
// var clientAddress = conn.clientAddress; // ip-adress
1939

meteor/server/__tests__/cronjobs.test.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,8 @@ describe('cronjobs', () => {
466466
expect(await Snapshots.findOneAsync(snapshot1)).toBeUndefined()
467467
})
468468
async function insertPlayoutDevice(
469-
props: Pick<PeripheralDevice, 'subType' | 'deviceName' | 'lastSeen' | 'parentDeviceId'>
469+
props: Pick<PeripheralDevice, 'subType' | 'deviceName' | 'lastSeen' | 'parentDeviceId'> &
470+
Partial<Pick<PeripheralDevice, 'token'>>
470471
): Promise<PeripheralDeviceId> {
471472
const deviceId = protectString<PeripheralDeviceId>(getRandomString())
472473
await PeripheralDevices.insertAsync({
@@ -495,37 +496,43 @@ describe('cronjobs', () => {
495496
}
496497

497498
async function createMockPlayoutGatewayAndDevices(lastSeen: number): Promise<{
499+
deviceToken: string
498500
mockPlayoutGw: PeripheralDeviceId
499501
mockCasparCg: PeripheralDeviceId
500502
mockAtem: PeripheralDeviceId
501503
}> {
504+
const deviceToken = 'token1'
502505
const mockPlayoutGw = await insertPlayoutDevice({
503506
deviceName: 'Playout Gateway',
504507
lastSeen: lastSeen,
505508
subType: PERIPHERAL_SUBTYPE_PROCESS,
509+
token: deviceToken,
506510
})
507511
const mockCasparCg = await insertPlayoutDevice({
508512
deviceName: 'CasparCG',
509513
lastSeen: lastSeen,
510514
subType: TSR.DeviceType.CASPARCG,
511515
parentDeviceId: mockPlayoutGw,
516+
token: deviceToken,
512517
})
513518
const mockAtem = await insertPlayoutDevice({
514519
deviceName: 'ATEM',
515520
lastSeen: lastSeen,
516521
subType: TSR.DeviceType.ATEM,
517522
parentDeviceId: mockPlayoutGw,
523+
token: deviceToken,
518524
})
519525

520526
return {
527+
deviceToken,
521528
mockPlayoutGw,
522529
mockCasparCg,
523530
mockAtem,
524531
}
525532
}
526533

527534
test('Attempts to restart CasparCG when job is enabled', async () => {
528-
const { mockCasparCg } = await createMockPlayoutGatewayAndDevices(Date.now()) // Some time after the threshold
535+
const { mockCasparCg, deviceToken } = await createMockPlayoutGatewayAndDevices(Date.now()) // Some time after the threshold
529536

530537
;(logger.info as jest.Mock).mockClear()
531538
// set time to 2020/07/{date} 04:05 Local Time, should be more than 24 hours after 2020/07/19 00:00 UTC
@@ -548,7 +555,7 @@ describe('cronjobs', () => {
548555
Meteor.callAsync(
549556
'peripheralDevice.functionReply',
550557
cmd.deviceId, // deviceId
551-
'', // deviceToken
558+
deviceToken, // deviceToken
552559
cmd._id, // commandId
553560
null, // err
554561
null // result

0 commit comments

Comments
 (0)