Skip to content

Commit 9bb8f08

Browse files
committed
Mux Playback Restriction ( Secure Streams )
Change-Id: I0c244295686c28e44173982684c300bb59a990b6
1 parent 7fa690a commit 9bb8f08

File tree

15 files changed

+471
-29
lines changed

15 files changed

+471
-29
lines changed

app/Http/Controllers/Apis/Protected/Summit/Factories/SummitValidationRulesFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use App\Http\ValidationRulesFactories\AbstractValidationRulesFactory;
1616
use App\Models\Foundation\Summit\ISummitExternalScheduleFeedType;
1717
use App\Models\Foundation\Summit\Registration\ISummitExternalRegistrationFeedType;
18+
use App\Rules\DomainArray;
19+
1820
/**
1921
* Class SummitValidationRulesFactory
2022
* @package App\Http\Controllers
@@ -100,6 +102,7 @@ public static function buildForAdd(array $payload = []): array
100102
'registration_slug_prefix' => 'sometimes|string|max:50',
101103
'mux_token_id' => 'nullable|string',
102104
'mux_token_secret' => 'nullable|string',
105+
'mux_allowed_domains' => ['sometimes', new DomainArray()],
103106
];
104107
}
105108

@@ -181,6 +184,7 @@ public static function buildForUpdate(array $payload = []): array
181184
'registration_slug_prefix' => 'sometimes|string|max:50',
182185
'mux_token_id' => 'nullable|string',
183186
'mux_token_secret' => 'nullable|string',
187+
'mux_allowed_domains' => ['sometimes', new DomainArray()],
184188
];
185189
}
186190
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php namespace App\Jobs;
2+
/*
3+
* Copyright 2023 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
use Illuminate\Bus\Queueable;
16+
use Illuminate\Contracts\Queue\ShouldQueue;
17+
use Illuminate\Foundation\Bus\Dispatchable;
18+
use Illuminate\Queue\InteractsWithQueue;
19+
use Illuminate\Queue\SerializesModels;
20+
use Illuminate\Support\Facades\Log;
21+
use services\model\ISummitService;
22+
23+
/**
24+
* Class CreateMUXPlaybackRestrictionForSummit
25+
* @package App\Jobs
26+
*/
27+
final class CreateMUXPlaybackRestrictionForSummit implements ShouldQueue
28+
{
29+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
30+
31+
public $tries = 2;
32+
/**
33+
* @var int
34+
*/
35+
private $summit_id;
36+
37+
/**
38+
* @param int $summit_id
39+
*/
40+
public function __construct(int $summit_id)
41+
{
42+
$this->summit_id = $summit_id;
43+
}
44+
45+
public function handle(ISummitService $service)
46+
{
47+
try {
48+
Log::debug("CreateMUXPlaybackRestrictionForSummit::handle summit id {$this->summit_id}");
49+
$service->generateMuxPlaybackRestriction($this->summit_id);
50+
} catch (\Exception $ex) {
51+
Log::error($ex);
52+
throw $ex;
53+
}
54+
}
55+
56+
public function failed(\Throwable $exception)
57+
{
58+
Log::debug
59+
(
60+
sprintf
61+
(
62+
"CreateMUXPlaybackRestrictionForSummit::failed summit id %s error %s",
63+
$this->summit_id,
64+
$exception->getMessage()
65+
)
66+
);
67+
Log::error($exception);
68+
}
69+
}

app/ModelSerializers/Summit/AdminSummitSerializer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ final class AdminSummitSerializer extends SummitSerializer
6767
// MUX
6868
'MuxTokenId' => 'mux_token_id:json_string',
6969
'MuxTokenSecret' => 'mux_token_secret:json_string',
70+
'MuxAllowedDomains' => 'mux_allowed_domains:json_string_array',
7071
];
7172

7273
protected static $allowed_relations = [

app/ModelSerializers/Summit/SummitEventSecureStreamSerializer.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*/
2525
final class SummitEventSecureStreamSerializer extends SilverStripeSerializer
2626
{
27-
const JWT_TTL = 60 * 60 * 24; // secs
27+
const JWT_TTL = 60 * 60 * 6; // secs
2828
const TTL_SKEW = 60; // secs
2929
/**
3030
* @param $expand
@@ -44,14 +44,16 @@ public function serialize($expand = null, array $fields = [], array $relations =
4444

4545
Log::debug(sprintf("SummitEventSecureStreamSerializer::serialize cache key %s", $key));
4646

47-
if(Cache::has($key)){
48-
$values = json_decode(Cache::get($key), true);
47+
$summit = $event->getSummit();
48+
49+
if(Cache::tags(sprintf('secure_streams_%s',$summit->getId()))->has($key)){
50+
$values = json_decode(Cache::tags(sprintf('secure_streams_%s',$summit->getId()))->get($key), true);
4951
Log::debug(sprintf("SummitEventSecureStreamSerializer::serialize cache hit for event %s", $event->getId()));
5052
return $values;
5153
}
5254

5355
$values = parent::serialize($expand, $fields, $relations, $params);
54-
$summit = $event->getSummit();
56+
5557

5658
if(!$event->IsSecureStream()){
5759
Log::debug
@@ -120,7 +122,7 @@ public function serialize($expand = null, array $fields = [], array $relations =
120122
$stream_duration = $event->getStreamDuration();
121123
if(!$stream_duration) $stream_duration = self::JWT_TTL;
122124
$exp = time() + $stream_duration;
123-
125+
$playback_restriction_id = $summit->getMuxPlaybackRestrictionId();
124126
foreach($tokenTypes as $type => $audience ) {
125127

126128
$payload = [
@@ -130,13 +132,17 @@ public function serialize($expand = null, array $fields = [], array $relations =
130132
"kid" => $key_id,
131133
];
132134

135+
if(!empty($playback_restriction_id))
136+
$payload['playback_restriction_id'] = $playback_restriction_id;
137+
138+
133139
$tokens[$type] = JWT::encode($payload, base64_decode($key_secret), 'RS256');
134140
}
135141

136142

137143
$values['tokens'] = $tokens;
138144

139-
Cache::put($key, json_encode($values), $stream_duration - self::TTL_SKEW);
145+
Cache::tags(sprintf('secure_streams_%s',$summit->getId()))->put($key, json_encode($values), $stream_duration - self::TTL_SKEW);
140146

141147
return $values;
142148
}

app/Models/Foundation/Summit/Events/SummitEvent.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,8 @@ public function setStreamingUrl(?string $streaming_url): void
12181218
{
12191219
$this->streaming_url = $streaming_url;
12201220
$key = $this->getSecureStreamCacheKey();
1221-
if(Cache::has($key)) Cache::forget($key);
1221+
if(Cache::tags(sprintf('secure_streams_%s',$this->summit->getId()))->has($key))
1222+
Cache::tags(sprintf('secure_streams_%s',$this->summit->getId()))->forget($key);
12221223
}
12231224

12241225
/**
@@ -1462,7 +1463,8 @@ public function setStreamingType(string $streaming_type): void
14621463
throw new ValidationException(sprintf("%s is not a valid streaming type", $streaming_type));
14631464
$this->streaming_type = $streaming_type;
14641465
$key = $this->getSecureStreamCacheKey();
1465-
if(Cache::has($key)) Cache::forget($key);
1466+
if(Cache::tags(sprintf('secure_streams_%s',$this->summit->getId()))->has($key))
1467+
Cache::tags(sprintf('secure_streams_%s',$this->summit->getId()))->forget($key);
14661468
}
14671469

14681470
/**
@@ -1588,7 +1590,8 @@ public function setStreamIsSecure(bool $stream_is_secure): void
15881590
$this->stream_is_secure = $stream_is_secure;
15891591

15901592
$key = $this->getSecureStreamCacheKey();
1591-
if(Cache::has($key)) Cache::forget($key);
1593+
if(Cache::tags(sprintf('secure_streams_%s',$this->summit->getId()))->has($key))
1594+
Cache::tags(sprintf('secure_streams_%s',$this->summit->getId()))->forget($key);
15921595

15931596
if($this->hasSummit() && $this->stream_is_secure && !$this->summit->hasMuxPrivateKey())
15941597
CreateMUXURLSigningKeyForSummit::dispatch($this->summit->getId());

app/Models/Foundation/Summit/Factories/SummitFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ public static function populate(Summit $summit, array $data){
484484
$summit->setMuxTokenSecret(trim($data['mux_token_secret']));
485485
}
486486

487+
if(isset($data['mux_allowed_domains'])){
488+
$summit->setMuxAllowedDomains($data['mux_allowed_domains']);
489+
}
490+
487491
return $summit;
488492
}
489493
}

app/Models/Foundation/Summit/Summit.php

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* limitations under the License.
1313
**/
1414

15+
use App\Jobs\CreateMUXPlaybackRestrictionForSummit;
1516
use App\Models\Foundation\Main\IGroup;
1617
use App\Models\Foundation\Main\OrderableChilds;
1718
use App\Models\Foundation\Summit\EmailFlows\SummitEmailEventFlowType;
@@ -34,6 +35,7 @@
3435
use DateTime;
3536
use Doctrine\Common\Collections\ArrayCollection;
3637
use Doctrine\Common\Collections\Criteria;
38+
use Illuminate\Support\Facades\Cache;
3739
use Illuminate\Support\Facades\Config;
3840
use Illuminate\Support\Facades\Log;
3941
use models\exceptions\ValidationException;
@@ -663,6 +665,18 @@ public function setMarketingSiteOauth2ClientScopes(string $marketing_site_oauth2
663665
*/
664666
private $mux_private_key;
665667

668+
/**
669+
* @ORM\Column(name="MUXPlaybackRestrictionId", type="string")
670+
* @var string
671+
*/
672+
private $mux_playback_restriction_id;
673+
674+
/**
675+
* @ORM\Column(name="MUXAllowedDomains", type="string")
676+
* @var string
677+
*/
678+
private $mux_allowed_domains;
679+
666680
/**
667681
* @ORM\OneToMany(targetEntity="models\summit\SummitEventType", mappedBy="summit", cascade={"persist","remove"}, orphanRemoval=true)
668682
*/
@@ -6489,7 +6503,6 @@ public function hasRelatedActivities(PresentationCategory $track): bool{
64896503
public function getQRCodesEncKey():?string {
64906504
return $this->qr_codes_enc_key;
64916505
}
6492-
64936506
public function hasQRCodesEncKey():bool{
64946507
return !empty($this->qr_codes_enc_key);
64956508
}
@@ -6573,4 +6586,58 @@ public function hasMuxPrivateKey():bool{
65736586
return !empty($this->mux_private_key);
65746587
}
65756588

6589+
/**
6590+
* @return string
6591+
*/
6592+
public function getMuxPlaybackRestrictionId(): ?string
6593+
{
6594+
return $this->mux_playback_restriction_id;
6595+
}
6596+
6597+
/**
6598+
* @param string $mux_playback_restriction_id
6599+
*/
6600+
public function setMuxPlaybackRestrictionId(string $mux_playback_restriction_id): void
6601+
{
6602+
$this->mux_playback_restriction_id = $mux_playback_restriction_id;
6603+
6604+
Cache::tags(sprintf('secure_streams_%s', $this->id))->flush();
6605+
}
6606+
6607+
public function clearMuxPlaybackRestrictionId(): void
6608+
{
6609+
$this->mux_playback_restriction_id = null;
6610+
6611+
Cache::tags(sprintf('secure_streams_%s', $this->id))->flush();
6612+
}
6613+
6614+
/**
6615+
* @return string
6616+
*/
6617+
public function getMuxAllowedDomains(): array
6618+
{
6619+
if(empty($this->mux_allowed_domains)) return [];
6620+
return explode('|', $this->mux_allowed_domains);
6621+
}
6622+
6623+
/**
6624+
* @param array $mux_allowed_domains
6625+
*/
6626+
public function setMuxAllowedDomains(array $mux_allowed_domains): void
6627+
{
6628+
Log::debug
6629+
(
6630+
sprintf
6631+
(
6632+
"Summit::setMuxAllowedDomains summit %s mux_allowed_domains %s",
6633+
$this->getId(),
6634+
json_encode($mux_allowed_domains)
6635+
)
6636+
);
6637+
6638+
$this->mux_allowed_domains = implode('|', $mux_allowed_domains);
6639+
if(!empty($this->mux_token_id) && !empty($this->mux_token_secret))
6640+
CreateMUXPlaybackRestrictionForSummit::dispatch($this->id);
6641+
}
6642+
65766643
}

app/Rules/Domain.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php namespace App\Rules;
2+
/*
3+
* Copyright 2023 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
use Illuminate\Contracts\Validation\Rule;
16+
use Illuminate\Support\Facades\Log;
17+
18+
/**
19+
* Class Domain
20+
* @package App\Rules
21+
*/
22+
final class Domain implements Rule
23+
{
24+
public static function validate($attribute, $value): bool
25+
{
26+
Log::debug(sprintf("Domain::validate %s", $value));
27+
28+
if (stripos($value, 'localhost') !== false) {
29+
return true;
30+
}
31+
32+
if(!preg_match('/^(?:[a-z0-9](?:[a-z0-9-æøå]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/isu', $value))
33+
return false;
34+
35+
return true;
36+
}
37+
38+
/**
39+
* Determine if the validation rule passes.
40+
*
41+
* @param string $attribute
42+
* @param mixed $value
43+
* @return bool
44+
*/
45+
public function passes($attribute, $value): bool
46+
{
47+
return self::validate($attribute, $value);
48+
}
49+
50+
/**
51+
* Get the validation error message.
52+
*
53+
* @return string
54+
*/
55+
public function message(): string
56+
{
57+
return trans('The :attribute is not a valid domain.');
58+
}
59+
}

0 commit comments

Comments
 (0)