Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Sofie Core Groups with Trusted header SOFIE-95 #11

Merged
merged 1 commit into from
Nov 26, 2024
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ meteor/.coverage/
node_modules
**/yarn-error.log
scratch/
meteor-settings.json

# Exclude JetBrains IDE specific files
.idea
Expand Down
2 changes: 0 additions & 2 deletions meteor/.meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@ [email protected] # Enable TypeScript syntax in .ts and .tsx modules

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

[email protected]

zodern:types
1 change: 0 additions & 1 deletion meteor/__mocks__/_setupMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ jest.mock('meteor/meteor', (...args) => require('./meteor').setup(args), { virtu
jest.mock('meteor/random', (...args) => require('./random').setup(args), { virtual: true })
jest.mock('meteor/check', (...args) => require('./check').setup(args), { virtual: true })
jest.mock('meteor/tracker', (...args) => require('./tracker').setup(args), { virtual: true })
jest.mock('meteor/accounts-base', (...args) => require('./accounts-base').setup(args), { virtual: true })
jest.mock('meteor/ejson', (...args) => require('./ejson').setup(args), { virtual: true })

jest.mock('meteor/mdg:validated-method', (...args) => require('./validated-method').setup(args), { virtual: true })
Expand Down
81 changes: 0 additions & 81 deletions meteor/__mocks__/accounts-base.ts

This file was deleted.

36 changes: 6 additions & 30 deletions meteor/__mocks__/meteor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MongoMock } from './mongo'
import { USER_PERMISSIONS_HEADER } from '@sofie-automation/meteor-lib/dist/userPermissions'

let controllableDefer = false

Expand All @@ -9,27 +9,14 @@ export function useNextTickDefer(): void {
controllableDefer = false
}

namespace Meteor {
export namespace Meteor {
export interface Settings {
public: {
[id: string]: any
}
[id: string]: any
}

export interface UserEmail {
address: string
verified: boolean
}
export interface User {
_id?: string
username?: string
emails?: UserEmail[]
createdAt?: number
profile?: any
services?: any
}

export interface ErrorStatic {
new (error: string | number, reason?: string, details?: string): Error
}
Expand Down Expand Up @@ -103,22 +90,18 @@ export namespace MeteorMock {
export const settings: any = {}

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

export const absolutePath = process.cwd()

export function user(): Meteor.User | undefined {
return mockUser
}
export function userId(): string | undefined {
return mockUser ? mockUser._id : undefined
}
function getMethodContext() {
return {
userId: mockUser ? mockUser._id : undefined,
connection: {
clientAddress: '1.1.1.1',
httpHeaders: {
// Default to full permissions for tests
[USER_PERMISSIONS_HEADER]: 'admin',
},
},
unblock: () => {
// noop
Expand Down Expand Up @@ -253,7 +236,6 @@ export namespace MeteorMock {
return fcn(...args)
}
}
export let users: MongoMock.Collection<any> | undefined = undefined

// -- Mock functions: --------------------------
/**
Expand All @@ -266,12 +248,6 @@ export namespace MeteorMock {

await waitTimeNoFakeTimers(10) // So that any observers or defers has had time to run.
}
export function mockLoginUser(newUser: Meteor.User): void {
mockUser = newUser
}
export function mockSetUsersCollection(usersCollection: MongoMock.Collection<any>): void {
users = usersCollection
}
export function mockSetClientEnvironment(): void {
mockIsClient = true
}
Expand Down
2 changes: 0 additions & 2 deletions meteor/__mocks__/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,5 +453,3 @@ export function setup(): any {
Mongo: MongoMock,
}
}

MeteorMock.mockSetUsersCollection(new MongoMock.Collection('Meteor.users'))
20 changes: 20 additions & 0 deletions meteor/server/Connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { logger } from './logging'
import { sendTrace } from './api/integration/influx'
import { PeripheralDevices } from './collections'
import { MetricsGauge } from '@sofie-automation/corelib/dist/prometheus'
import { parseUserPermissions, USER_PERMISSIONS_HEADER } from '@sofie-automation/meteor-lib/dist/userPermissions'
import { Settings } from './Settings'

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

if (Settings.enableHeaderAuth) {
const userLevel = parseUserPermissions(conn.httpHeaders[USER_PERMISSIONS_HEADER])

// HACK: force the userId of the connection before it can be used.
// This ensures we know the permissions of the connection before it can try to do anything
// This could probably be safely done inside a meteor method, as we only need it when directly modifying a collection in the client,
// but that will cause all the publications to restart when changing the userId.
const connSession = (Meteor as any).server.sessions.get(conn.id)
if (!connSession) {
logger.error(`Failed to find session for ddp connection! "${conn.id}"`)
// Close the connection, it won't be secure
conn.close()
return
} else {
connSession.userId = JSON.stringify(userLevel)
}
}

const connectionId: string = conn.id
// var clientAddress = conn.clientAddress; // ip-adress

Expand Down
13 changes: 10 additions & 3 deletions meteor/server/__tests__/cronjobs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,8 @@ describe('cronjobs', () => {
expect(await Snapshots.findOneAsync(snapshot1)).toBeUndefined()
})
async function insertPlayoutDevice(
props: Pick<PeripheralDevice, 'subType' | 'deviceName' | 'lastSeen' | 'parentDeviceId'>
props: Pick<PeripheralDevice, 'subType' | 'deviceName' | 'lastSeen' | 'parentDeviceId'> &
Partial<Pick<PeripheralDevice, 'token'>>
): Promise<PeripheralDeviceId> {
const deviceId = protectString<PeripheralDeviceId>(getRandomString())
await PeripheralDevices.insertAsync({
Expand Down Expand Up @@ -505,37 +506,43 @@ describe('cronjobs', () => {
}

async function createMockPlayoutGatewayAndDevices(lastSeen: number): Promise<{
deviceToken: string
mockPlayoutGw: PeripheralDeviceId
mockCasparCg: PeripheralDeviceId
mockAtem: PeripheralDeviceId
}> {
const deviceToken = 'token1'
const mockPlayoutGw = await insertPlayoutDevice({
deviceName: 'Playout Gateway',
lastSeen: lastSeen,
subType: PERIPHERAL_SUBTYPE_PROCESS,
token: deviceToken,
})
const mockCasparCg = await insertPlayoutDevice({
deviceName: 'CasparCG',
lastSeen: lastSeen,
subType: TSR.DeviceType.CASPARCG,
parentDeviceId: mockPlayoutGw,
token: deviceToken,
})
const mockAtem = await insertPlayoutDevice({
deviceName: 'ATEM',
lastSeen: lastSeen,
subType: TSR.DeviceType.ATEM,
parentDeviceId: mockPlayoutGw,
token: deviceToken,
})

return {
deviceToken,
mockPlayoutGw,
mockCasparCg,
mockAtem,
}
}

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

;(logger.info as jest.Mock).mockClear()
// set time to 2020/07/{date} 04:05 Local Time, should be more than 24 hours after 2020/07/19 00:00 UTC
Expand All @@ -558,7 +565,7 @@ describe('cronjobs', () => {
Meteor.callAsync(
'peripheralDevice.functionReply',
cmd.deviceId, // deviceId
'', // deviceToken
deviceToken, // deviceToken
cmd._id, // commandId
null, // err
null // result
Expand Down
Loading
Loading