github.com/livekit/protocol@v1.39.3/auth/grants_test.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package auth 16 17 import ( 18 "reflect" 19 "strconv" 20 "testing" 21 22 "github.com/stretchr/testify/require" 23 24 "github.com/livekit/protocol/livekit" 25 ) 26 27 func TestGrants(t *testing.T) { 28 t.Parallel() 29 30 t.Run("clone default grant", func(t *testing.T) { 31 grants := &ClaimGrants{} 32 clone := grants.Clone() 33 require.NotSame(t, grants, clone) 34 require.Same(t, grants.Video, clone.Video) 35 require.True(t, reflect.DeepEqual(grants, clone)) 36 require.True(t, reflect.DeepEqual(grants.Video, clone.Video)) 37 }) 38 39 t.Run("clone nil video", func(t *testing.T) { 40 grants := &ClaimGrants{ 41 Identity: "identity", 42 Name: "name", 43 Sha256: "sha256", 44 Metadata: "metadata", 45 } 46 clone := grants.Clone() 47 require.NotSame(t, grants, clone) 48 require.Same(t, grants.Video, clone.Video) 49 require.True(t, reflect.DeepEqual(grants, clone)) 50 require.True(t, reflect.DeepEqual(grants.Video, clone.Video)) 51 }) 52 53 t.Run("clone with video", func(t *testing.T) { 54 tr := true 55 fa := false 56 video := &VideoGrant{ 57 RoomCreate: true, 58 RoomList: false, 59 RoomRecord: true, 60 RoomAdmin: false, 61 RoomJoin: true, 62 Room: "room", 63 CanPublish: &tr, 64 CanSubscribe: &fa, 65 CanPublishData: nil, 66 Hidden: true, 67 Recorder: false, 68 CanSubscribeMetrics: &tr, 69 } 70 grants := &ClaimGrants{ 71 Identity: "identity", 72 Name: "name", 73 Kind: "kind", 74 Video: video, 75 Sha256: "sha256", 76 Metadata: "metadata", 77 } 78 clone := grants.Clone() 79 require.NotSame(t, grants, clone) 80 require.NotSame(t, grants.Video, clone.Video) 81 require.NotSame(t, grants.Video.CanPublish, clone.Video.CanPublish) 82 require.NotSame(t, grants.Video.CanSubscribe, clone.Video.CanSubscribe) 83 require.Same(t, grants.Video.CanPublishData, clone.Video.CanPublishData) 84 require.True(t, reflect.DeepEqual(grants, clone)) 85 require.True(t, reflect.DeepEqual(grants.Video, clone.Video)) 86 }) 87 } 88 89 func TestParticipantKind(t *testing.T) { 90 const kindMin, kindMax = livekit.ParticipantInfo_STANDARD, livekit.ParticipantInfo_AGENT 91 for k := kindMin; k <= kindMax; k++ { 92 k := k 93 t.Run(k.String(), func(t *testing.T) { 94 require.Equal(t, k, kindToProto(kindFromProto(k))) 95 }) 96 } 97 const kindNext = kindMax + 1 98 if _, err := strconv.Atoi(kindNext.String()); err != nil { 99 t.Errorf("Please update kindMax to match protobuf. Missing value: %s", kindNext) 100 } 101 } 102 103 func TestRoomConfiguration_CheckCredentials(t *testing.T) { 104 t.Parallel() 105 106 t.Run("nil egress returns nil", func(t *testing.T) { 107 config := &RoomConfiguration{} 108 require.NoError(t, config.CheckCredentials()) 109 }) 110 111 t.Run("empty egress returns nil", func(t *testing.T) { 112 config := &RoomConfiguration{ 113 Egress: &livekit.RoomEgress{}, 114 } 115 require.NoError(t, config.CheckCredentials()) 116 }) 117 118 t.Run("participant file output with S3 secret fails", func(t *testing.T) { 119 config := &RoomConfiguration{ 120 Egress: &livekit.RoomEgress{ 121 Participant: &livekit.AutoParticipantEgress{ 122 FileOutputs: []*livekit.EncodedFileOutput{ 123 { 124 Output: &livekit.EncodedFileOutput_S3{ 125 S3: &livekit.S3Upload{ 126 AccessKey: "access", 127 Secret: "secret", // This should trigger error 128 Bucket: "bucket", 129 }, 130 }, 131 }, 132 }, 133 }, 134 }, 135 } 136 require.ErrorIs(t, config.CheckCredentials(), ErrSensitiveCredentials) 137 }) 138 139 t.Run("participant file output with S3 but no secret passes", func(t *testing.T) { 140 config := &RoomConfiguration{ 141 Egress: &livekit.RoomEgress{ 142 Participant: &livekit.AutoParticipantEgress{ 143 FileOutputs: []*livekit.EncodedFileOutput{ 144 { 145 Output: &livekit.EncodedFileOutput_S3{ 146 S3: &livekit.S3Upload{ 147 AccessKey: "access", 148 Secret: "", // No secret 149 Bucket: "bucket", 150 Region: "us-west-2", 151 }, 152 }, 153 }, 154 }, 155 }, 156 }, 157 } 158 require.NoError(t, config.CheckCredentials()) 159 }) 160 161 t.Run("participant segment output with GCP credentials fails", func(t *testing.T) { 162 config := &RoomConfiguration{ 163 Egress: &livekit.RoomEgress{ 164 Participant: &livekit.AutoParticipantEgress{ 165 SegmentOutputs: []*livekit.SegmentedFileOutput{ 166 { 167 Output: &livekit.SegmentedFileOutput_Gcp{ 168 Gcp: &livekit.GCPUpload{ 169 Credentials: "credentials", // This should trigger error 170 Bucket: "bucket", 171 }, 172 }, 173 }, 174 }, 175 }, 176 }, 177 } 178 require.ErrorIs(t, config.CheckCredentials(), ErrSensitiveCredentials) 179 }) 180 181 t.Run("participant segment output with GCP but no credentials passes", func(t *testing.T) { 182 config := &RoomConfiguration{ 183 Egress: &livekit.RoomEgress{ 184 Participant: &livekit.AutoParticipantEgress{ 185 SegmentOutputs: []*livekit.SegmentedFileOutput{ 186 { 187 Output: &livekit.SegmentedFileOutput_Gcp{ 188 Gcp: &livekit.GCPUpload{ 189 Credentials: "", // No credentials 190 Bucket: "bucket", 191 }, 192 }, 193 }, 194 }, 195 }, 196 }, 197 } 198 require.NoError(t, config.CheckCredentials()) 199 }) 200 201 t.Run("room file output with Azure account key fails", func(t *testing.T) { 202 config := &RoomConfiguration{ 203 Egress: &livekit.RoomEgress{ 204 Room: &livekit.RoomCompositeEgressRequest{ 205 FileOutputs: []*livekit.EncodedFileOutput{ 206 { 207 Output: &livekit.EncodedFileOutput_Azure{ 208 Azure: &livekit.AzureBlobUpload{ 209 AccountName: "account", 210 AccountKey: "key", // This should trigger error 211 ContainerName: "container", 212 }, 213 }, 214 }, 215 }, 216 }, 217 }, 218 } 219 require.ErrorIs(t, config.CheckCredentials(), ErrSensitiveCredentials) 220 }) 221 222 t.Run("room segment output with AliOSS secret fails", func(t *testing.T) { 223 config := &RoomConfiguration{ 224 Egress: &livekit.RoomEgress{ 225 Room: &livekit.RoomCompositeEgressRequest{ 226 SegmentOutputs: []*livekit.SegmentedFileOutput{ 227 { 228 Output: &livekit.SegmentedFileOutput_AliOSS{ 229 AliOSS: &livekit.AliOSSUpload{ 230 AccessKey: "access", 231 Secret: "secret", // This should trigger error 232 Bucket: "bucket", 233 }, 234 }, 235 }, 236 }, 237 }, 238 }, 239 } 240 require.ErrorIs(t, config.CheckCredentials(), ErrSensitiveCredentials) 241 }) 242 243 t.Run("room image output with valid config passes", func(t *testing.T) { 244 config := &RoomConfiguration{ 245 Egress: &livekit.RoomEgress{ 246 Room: &livekit.RoomCompositeEgressRequest{ 247 ImageOutputs: []*livekit.ImageOutput{ 248 { 249 CaptureInterval: 5, 250 Width: 1920, 251 Height: 1080, 252 Output: &livekit.ImageOutput_S3{ 253 S3: &livekit.S3Upload{ 254 AccessKey: "access", 255 Secret: "", // No secret 256 Bucket: "bucket", 257 }, 258 }, 259 }, 260 }, 261 }, 262 }, 263 } 264 require.NoError(t, config.CheckCredentials()) 265 }) 266 267 t.Run("room stream outputs always fail", func(t *testing.T) { 268 config := &RoomConfiguration{ 269 Egress: &livekit.RoomEgress{ 270 Room: &livekit.RoomCompositeEgressRequest{ 271 StreamOutputs: []*livekit.StreamOutput{ 272 { 273 Protocol: livekit.StreamProtocol_RTMP, 274 Urls: []string{"rtmp://example.com/live"}, 275 }, 276 }, 277 }, 278 }, 279 } 280 require.ErrorIs(t, config.CheckCredentials(), ErrSensitiveCredentials) 281 }) 282 283 t.Run("tracks output with S3 secret fails", func(t *testing.T) { 284 config := &RoomConfiguration{ 285 Egress: &livekit.RoomEgress{ 286 Tracks: &livekit.AutoTrackEgress{ 287 Filepath: "output.mp4", 288 Output: &livekit.AutoTrackEgress_S3{ 289 S3: &livekit.S3Upload{ 290 AccessKey: "access", 291 Secret: "secret", // This should trigger error 292 Bucket: "bucket", 293 }, 294 }, 295 }, 296 }, 297 } 298 require.ErrorIs(t, config.CheckCredentials(), ErrSensitiveCredentials) 299 }) 300 301 t.Run("tracks output without credentials passes", func(t *testing.T) { 302 config := &RoomConfiguration{ 303 Egress: &livekit.RoomEgress{ 304 Tracks: &livekit.AutoTrackEgress{ 305 Filepath: "output.mp4", 306 Output: &livekit.AutoTrackEgress_Gcp{ 307 Gcp: &livekit.GCPUpload{ 308 Credentials: "", // No credentials 309 Bucket: "bucket", 310 }, 311 }, 312 }, 313 }, 314 } 315 require.NoError(t, config.CheckCredentials()) 316 }) 317 318 t.Run("multiple outputs with mixed credentials", func(t *testing.T) { 319 config := &RoomConfiguration{ 320 Egress: &livekit.RoomEgress{ 321 Participant: &livekit.AutoParticipantEgress{ 322 FileOutputs: []*livekit.EncodedFileOutput{ 323 { 324 Output: &livekit.EncodedFileOutput_S3{ 325 S3: &livekit.S3Upload{ 326 AccessKey: "access", 327 Secret: "", // No secret - OK 328 Bucket: "bucket1", 329 }, 330 }, 331 }, 332 { 333 Output: &livekit.EncodedFileOutput_Gcp{ 334 Gcp: &livekit.GCPUpload{ 335 Credentials: "credentials", // Has credentials - should fail 336 Bucket: "bucket2", 337 }, 338 }, 339 }, 340 }, 341 }, 342 }, 343 } 344 require.ErrorIs(t, config.CheckCredentials(), ErrSensitiveCredentials) 345 }) 346 347 t.Run("all cloud providers without credentials pass", func(t *testing.T) { 348 config := &RoomConfiguration{ 349 Egress: &livekit.RoomEgress{ 350 Room: &livekit.RoomCompositeEgressRequest{ 351 FileOutputs: []*livekit.EncodedFileOutput{ 352 { 353 Output: &livekit.EncodedFileOutput_S3{ 354 S3: &livekit.S3Upload{ 355 AccessKey: "access", 356 Secret: "", // No secret 357 Bucket: "s3bucket", 358 }, 359 }, 360 }, 361 { 362 Output: &livekit.EncodedFileOutput_Gcp{ 363 Gcp: &livekit.GCPUpload{ 364 Credentials: "", // No credentials 365 Bucket: "gcpbucket", 366 }, 367 }, 368 }, 369 { 370 Output: &livekit.EncodedFileOutput_Azure{ 371 Azure: &livekit.AzureBlobUpload{ 372 AccountName: "account", 373 AccountKey: "", // No key 374 ContainerName: "container", 375 }, 376 }, 377 }, 378 { 379 Output: &livekit.EncodedFileOutput_AliOSS{ 380 AliOSS: &livekit.AliOSSUpload{ 381 AccessKey: "access", 382 Secret: "", // No secret 383 Bucket: "alibucket", 384 }, 385 }, 386 }, 387 }, 388 }, 389 }, 390 } 391 require.NoError(t, config.CheckCredentials()) 392 }) 393 }