amazon-chime-sdk-rails 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a4ee22a82debfa8f285e8516c9b6f80bc82be4cd26ed45dd65d7ea2e05170f8
4
- data.tar.gz: fcf6109a79b1ca6535b10fbcf51994708a80409601daade46b2fa10cfd9a22e0
3
+ metadata.gz: 3d5ebcc29d74104d1eec93c7ac094be54b52083edc4bf835a0ad7196a8dc0dfd
4
+ data.tar.gz: b28f4fdcef7a52500fdfa32f7dfafb83455ae2827f3691627bc1423bfa0ec33e
5
5
  SHA512:
6
- metadata.gz: 3a153f6055287f2eb6d20e44012afb50ed0a6554be21d2563dc1a0955f943cf053e1d64b73ac086721dec83b9bdda3473dfeef51b5d817f1f347548b90c7c1c9
7
- data.tar.gz: 99466f46ccd52dcce9c71dcaca1422b96af51ff68023bc41b529f3401a09c8b5368e0db93df2c05dd5ad25d8b9b87d67d19469a113017bbdc2d054b238a92942
6
+ metadata.gz: 859941de7300fca365390ec91df87b96a5ef4e8b573b282516d596715e39f4e22966f0146f090632dce3b36e446ab0ba93b974f0d3b9be9c512aef42402b7cd2
7
+ data.tar.gz: 745c35af2bd1bd43f082caed8867724246f6b2b8c119c99a57866ed73243252efded8249c4ccdfe2f53ad1425571e5b32ff05381a1292facfd92b43f73b05665
@@ -0,0 +1,7 @@
1
+ version: 2
2
+ updates:
3
+ # Actions are pinned to commit SHAs; this keeps the pins current.
4
+ - package-ecosystem: github-actions
5
+ directory: /
6
+ schedule:
7
+ interval: weekly
@@ -15,6 +15,9 @@ env:
15
15
  # https://nodejs.org/en/about/previous-releases
16
16
  node-version: 24.x
17
17
 
18
+ permissions:
19
+ contents: read
20
+
18
21
  jobs:
19
22
  build:
20
23
  runs-on: ubuntu-latest
@@ -48,14 +51,14 @@ jobs:
48
51
  BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
49
52
 
50
53
  steps:
51
- - uses: actions/checkout@v5
54
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
52
55
  - name: Set up Ruby
53
- uses: ruby/setup-ruby@v1
56
+ uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
54
57
  with:
55
58
  ruby-version: ${{ matrix.ruby-version }}
56
59
  bundler-cache: true
57
60
  - name: Set up Node.js
58
- uses: actions/setup-node@v5
61
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
59
62
  with:
60
63
  node-version: ${{ env.node-version }}
61
64
  - name: Prepare test application
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## 2.2.1 / 2026-10-28
2
+ [Full Changelog](http://github.com/simukappu/amazon-chime-sdk-rails/compare/v2.2.0...v2.2.1)
3
+
4
+ Security Fixes:
5
+
6
+ * Return only `id` and `name` of users and rooms in the application metadata examples of the generated controller templates and the docs, instead of serializing whole records such as email addresses to all meeting participants
7
+ * Resolve user and room IDs in the application metadata examples by removing `config.prefix` from `ExternalUserId` and `ExternalMeetingId`, instead of `split('-')[3]`. Attendees and meetings created with other prefixes, such as other applications or environments, are no longer resolved to local records
8
+
9
+ ## 2.2.0 / 2026-10-28
10
+ [Full Changelog](http://github.com/simukappu/amazon-chime-sdk-rails/compare/v2.1.0...v2.2.0)
11
+
12
+ Security Fixes:
13
+
14
+ * Verify that requested meetings belong to the requesting resource. Meetings and attendees controllers now respond with 404 when the meeting's `ExternalMeetingId` is not `config.prefix + meeting_request_id`, so users can no longer get, delete, or join meetings of other resources (such as other private rooms) by meeting ID
15
+ * Attendees controllers must now define `meeting_request_id` with the same value as the meetings controller. The generated attendees controller template includes it
16
+ * Set `config.verify_meeting_request_id = false`, or override `verify_meeting_request_id?` in a controller, to restore the previous behavior
17
+ * Do not return `JoinToken` from attendees `index` and `show` actions. A join token is the credential to join a meeting as that attendee, so other participants could impersonate the attendee
18
+ * Do not create meetings or attendees by HTTP GET request parameters. `create_meeting` in meetings `index` and `create_attendee_from_meeting` in meetings `show` can now only disable the creation enabled by `config.create_meeting_by_get_request` and `config.create_attendee_from_meeting`, since GET requests are not protected from CSRF
19
+ * Raise `NotImplementedError` from `attendee_request_id` unless it is defined in your controllers. The previous default `"default"` made every user join meetings as the same attendee with the same join token. The generated controller templates also raise it until you define `attendee_request_id`
20
+ * Build the attendee list in the generated meeting view with `textContent` instead of `innerHTML`, so attendee names from application metadata cannot inject HTML
21
+ * Remove the unpinned axios script loaded from unpkg in the generated meeting view and use `fetch` instead
22
+ * Pin the aws-samples/amazon-chime-sdk commit that `rails g chime_sdk:js` clones and runs npm scripts in
23
+
1
24
  ## 2.1.0 / 2026-04-11
2
25
  [Full Changelog](http://github.com/simukappu/amazon-chime-sdk-rails/compare/v2.0.1...v2.1.0)
3
26
 
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # amazon-chime-sdk-rails
2
2
 
3
- [![Build Status](https://github.com/simukappu/amazon-chime-sdk-rails/actions/workflows/build.yml/badge.svg)](https://github.com/simukappu/amazon-chime-sdk-rails/actions/workflows/build.yml)
4
- [![Coverage Status](https://coveralls.io/repos/github/simukappu/amazon-chime-sdk-rails/badge.svg?branch=master)](https://coveralls.io/github/simukappu/amazon-chime-sdk-rails?branch=master)
5
3
  [![Gem Version](https://badge.fury.io/rb/amazon-chime-sdk-rails.svg)](https://rubygems.org/gems/amazon-chime-sdk-rails)
6
4
  [![Gem Downloads](https://img.shields.io/gem/dt/amazon-chime-sdk-rails.svg)](https://rubygems.org/gems/amazon-chime-sdk-rails)
7
- [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
5
+ [![Build Status](https://github.com/simukappu/amazon-chime-sdk-rails/actions/workflows/build.yml/badge.svg)](https://github.com/simukappu/amazon-chime-sdk-rails/actions/workflows/build.yml)
6
+ [![Coverage Status](https://coveralls.io/repos/github/simukappu/amazon-chime-sdk-rails/badge.svg?branch=master)](https://coveralls.io/github/simukappu/amazon-chime-sdk-rails?branch=master)
7
+ [![License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
8
8
 
9
9
  *amazon-chime-sdk-rails* brings server-side implementation of [Amazon Chime SDK](https://aws.amazon.com/chime/chime-sdk) to your [Ruby on Rails](https://rubyonrails.org) application. [Amazon Chime SDK](https://aws.amazon.com/chime/chime-sdk) provides client-side implementation to build real-time communications for your application, and *amazon-chime-sdk-rails* enables you to easily add server-side implementation to your Rails application.
10
10
 
@@ -185,7 +185,7 @@ class Api::MeetingsController < ApplicationController
185
185
  "PrivateRoom-#{@room.id}"
186
186
  end
187
187
 
188
- # Uncomment and update
188
+ # Update (replace raise NotImplementedError)
189
189
  def attendee_request_id
190
190
  "User-#{current_api_user.id}"
191
191
  end
@@ -194,16 +194,16 @@ class Api::MeetingsController < ApplicationController
194
194
  def application_meeting_metadata(meeting)
195
195
  {
196
196
  "MeetingType": "PrivateRoom",
197
- "PrivateRoom": @room
197
+ "PrivateRoom": @room.slice(:id, :name)
198
198
  }
199
199
  end
200
200
 
201
201
  # Uncomment
202
202
  def application_attendee_metadata(attendee)
203
- user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
203
+ user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
204
204
  {
205
205
  "AttendeeType": "User",
206
- "User": User.find_by_id(user_id)
206
+ "User": User.find_by_id(user_id)&.slice(:id, :name)
207
207
  }
208
208
  end
209
209
  end
@@ -234,17 +234,22 @@ class Api::MeetingAttendeesController < ApplicationController
234
234
  end
235
235
  end
236
236
 
237
- # Uncomment and update
237
+ # Uncomment (use the same value as your meetings controller)
238
+ def meeting_request_id
239
+ "PrivateRoom-#{@room.id}"
240
+ end
241
+
242
+ # Update (replace raise NotImplementedError)
238
243
  def attendee_request_id
239
244
  "User-#{current_api_user.id}"
240
245
  end
241
246
 
242
247
  # Uncomment
243
248
  def application_attendee_metadata(attendee)
244
- user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
249
+ user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
245
250
  {
246
251
  "AttendeeType": "User",
247
- "User": User.find_by_id(user_id)
252
+ "User": User.find_by_id(user_id)&.slice(:id, :name)
248
253
  }
249
254
  end
250
255
  end
@@ -355,37 +360,9 @@ $ curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:applicati
355
360
  "MediaRegion": "us-east-1",
356
361
  "ApplicationMetadata": {
357
362
  "MeetingType": "PrivateRoom",
358
- "Room": {
363
+ "PrivateRoom": {
359
364
  "id": 1,
360
- "name": "PrivateRoom-1",
361
- "created_at": "2020-10-16T11:15:56.223Z",
362
- "updated_at": "2020-10-16T11:15:56.223Z",
363
- "members": [
364
- {
365
- "id": 1,
366
- "provider": "email",
367
- "uid": "ichiro@example.com",
368
- "allow_password_change": false,
369
- "name": "ichiro",
370
- "nickname": null,
371
- "image": null,
372
- "email": "ichiro@example.com",
373
- "created_at": "2020-10-16T11:14:48.731Z",
374
- "updated_at": "2020-10-16T11:16:56.927Z"
375
- },
376
- {
377
- "id": 2,
378
- "provider": "email",
379
- "uid": "stephen@example.com",
380
- "allow_password_change": false,
381
- "name": "stephen",
382
- "nickname": null,
383
- "image": null,
384
- "email": "stephen@example.com",
385
- "created_at": "2020-10-16T11:15:33.226Z",
386
- "updated_at": "2020-10-16T11:15:33.314Z"
387
- }
388
- ]
365
+ "name": "PrivateRoom-1"
389
366
  }
390
367
  }
391
368
  },
@@ -397,15 +374,7 @@ $ curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:applicati
397
374
  "AttendeeType": "User",
398
375
  "User": {
399
376
  "id": 1,
400
- "provider": "email",
401
- "uid": "ichiro@example.com",
402
- "allow_password_change": false,
403
- "name": "ichiro",
404
- "nickname": null,
405
- "image": null,
406
- "email": "ichiro@example.com",
407
- "created_at": "2020-10-16T11:14:48.731Z",
408
- "updated_at": "2020-10-16T11:16:56.927Z"
377
+ "name": "ichiro"
409
378
  }
410
379
  }
411
380
  }
@@ -423,20 +392,11 @@ $ curl localhost:3000/api/v1/rooms/1/meetings/${MEETING_ID}/attendees -H "conten
423
392
  "Attendee": {
424
393
  "ExternalUserId": "ChimeSdkRailsApp-development-User-1",
425
394
  "AttendeeId": "b581c46d-661f-92bb-d80e-XXXXXXXXXXXX",
426
- "JoinToken": "YjU4MWM0NmQtNjYxZi05MmJiLWQ4MGUtZjRiMTU3ZDk1ZmU5OjgyZmM2NTMxLTIwMjctNGMxMS04OTE0LTQwZjXXXXXXXXXXXX",
427
395
  "ApplicationMetadata": {
428
396
  "AttendeeType": "User",
429
397
  "User": {
430
398
  "id": 1,
431
- "provider": "email",
432
- "uid": "ichiro@example.com",
433
- "allow_password_change": false,
434
- "name": "ichiro",
435
- "nickname": null,
436
- "image": null,
437
- "email": "ichiro@example.com",
438
- "created_at": "2020-10-16T11:14:48.731Z",
439
- "updated_at": "2020-10-16T11:16:56.927Z"
399
+ "name": "ichiro"
440
400
  }
441
401
  }
442
402
  }
@@ -452,20 +412,11 @@ $ curl localhost:3000/api/v1/rooms/1/meetings/${MEETING_ID}/attendees/${ATTENDEE
452
412
  "Attendee": {
453
413
  "ExternalUserId": "ChimeSdkRailsApp-development-User-1",
454
414
  "AttendeeId": "b581c46d-661f-92bb-d80e-XXXXXXXXXXXX",
455
- "JoinToken": "YjU4MWM0NmQtNjYxZi05MmJiLWQ4MGUtZjRiMTU3ZDk1ZmU5OjgyZmM2NTMxLTIwMjctNGMxMS04OTE0LTQwZjXXXXXXXXXXXX",
456
415
  "ApplicationMetadata": {
457
416
  "AttendeeType": "User",
458
417
  "User": {
459
418
  "id": 1,
460
- "provider": "email",
461
- "uid": "ichiro@example.com",
462
- "allow_password_change": false,
463
- "name": "ichiro",
464
- "nickname": null,
465
- "image": null,
466
- "email": "ichiro@example.com",
467
- "created_at": "2020-10-16T11:14:48.731Z",
468
- "updated_at": "2020-10-16T11:16:56.927Z"
419
+ "name": "ichiro"
469
420
  }
470
421
  }
471
422
  }
@@ -501,37 +452,9 @@ $ curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:applicati
501
452
  "MediaRegion": "us-east-1",
502
453
  "ApplicationMetadata": {
503
454
  "MeetingType": "PrivateRoom",
504
- "Room": {
455
+ "PrivateRoom": {
505
456
  "id": 1,
506
- "name": "PrivateRoom-1",
507
- "created_at": "2020-10-16T11:15:56.223Z",
508
- "updated_at": "2020-10-16T11:15:56.223Z",
509
- "members": [
510
- {
511
- "id": 1,
512
- "provider": "email",
513
- "uid": "ichiro@example.com",
514
- "allow_password_change": false,
515
- "name": "ichiro",
516
- "nickname": null,
517
- "image": null,
518
- "email": "ichiro@example.com",
519
- "created_at": "2020-10-16T11:14:48.731Z",
520
- "updated_at": "2020-10-16T11:16:56.927Z"
521
- },
522
- {
523
- "id": 2,
524
- "provider": "email",
525
- "uid": "stephen@example.com",
526
- "allow_password_change": false,
527
- "name": "stephen",
528
- "nickname": null,
529
- "image": null,
530
- "email": "stephen@example.com",
531
- "created_at": "2020-10-16T11:15:33.226Z",
532
- "updated_at": "2020-10-16T11:21:46.011Z"
533
- }
534
- ]
457
+ "name": "PrivateRoom-1"
535
458
  }
536
459
  }
537
460
  },
@@ -543,15 +466,7 @@ $ curl localhost:3000/api/v1/rooms/1/meetings -X POST -H "content-type:applicati
543
466
  "AttendeeType": "User",
544
467
  "User": {
545
468
  "id": 2,
546
- "provider": "email",
547
- "uid": "stephen@example.com",
548
- "allow_password_change": false,
549
- "name": "stephen",
550
- "nickname": null,
551
- "image": null,
552
- "email": "stephen@example.com",
553
- "created_at": "2020-10-16T11:15:33.226Z",
554
- "updated_at": "2020-10-16T11:21:46.011Z"
469
+ "name": "stephen"
555
470
  }
556
471
  }
557
472
  }
@@ -251,7 +251,7 @@ class MeetingsController < ApplicationController
251
251
  "PrivateRoom-#{@room.id}"
252
252
  end
253
253
 
254
- # Uncomment
254
+ # Update (replace raise NotImplementedError)
255
255
  def attendee_request_id
256
256
  "User-#{current_user.id}"
257
257
  end
@@ -260,16 +260,16 @@ class MeetingsController < ApplicationController
260
260
  def application_meeting_metadata(meeting)
261
261
  {
262
262
  "MeetingType": "PrivateRoom",
263
- "PrivateRoom": @room
263
+ "PrivateRoom": @room.slice(:id, :name)
264
264
  }
265
265
  end
266
266
 
267
267
  # Uncomment
268
268
  def application_attendee_metadata(attendee)
269
- user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
269
+ user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
270
270
  {
271
271
  "AttendeeType": "User",
272
- "User": User.find_by_id(user_id)
272
+ "User": User.find_by_id(user_id)&.slice(:id, :name)
273
273
  }
274
274
  end
275
275
  end
@@ -299,17 +299,22 @@ class MeetingAttendeesController < ApplicationController
299
299
  end
300
300
  end
301
301
 
302
- # Uncomment
302
+ # Uncomment (use the same value as your meetings controller)
303
+ def meeting_request_id
304
+ "PrivateRoom-#{@room.id}"
305
+ end
306
+
307
+ # Update (replace raise NotImplementedError)
303
308
  def attendee_request_id
304
309
  "User-#{current_user.id}"
305
310
  end
306
311
 
307
312
  # Uncomment
308
313
  def application_attendee_metadata(attendee)
309
- user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
314
+ user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
310
315
  {
311
316
  "AttendeeType": "User",
312
- "User": User.find_by_id(user_id)
317
+ "User": User.find_by_id(user_id)&.slice(:id, :name)
313
318
  }
314
319
  end
315
320
  end
@@ -64,6 +64,15 @@ module ChimeSdk
64
64
  # @return [Boolean] Whether the application creates meeting in meetings#index action by HTTP GET request
65
65
  attr_accessor :create_meeting_by_get_request
66
66
 
67
+ # @overload verify_meeting_request_id
68
+ # Return whether the controllers verify that requested meetings were created with ChimeSdk.config.prefix + meeting_request_id.
69
+ # @return [Boolean] Whether the controllers verify that requested meetings were created with ChimeSdk.config.prefix + meeting_request_id
70
+ # @overload verify_meeting_request_id=(value)
71
+ # Set whether the controllers verify that requested meetings were created with ChimeSdk.config.prefix + meeting_request_id.
72
+ # @param [Boolean] verify_meeting_request_id The new verify_meeting_request_id
73
+ # @return [Boolean] Whether the controllers verify that requested meetings were created with ChimeSdk.config.prefix + meeting_request_id
74
+ attr_accessor :verify_meeting_request_id
75
+
67
76
  # Initialize configuration for ChimeSdk.
68
77
  # These configuration can be overridden in initializer.
69
78
  # @return [Config] A new instance of Config
@@ -75,6 +84,7 @@ module ChimeSdk
75
84
  @create_meeting_with_attendee = true
76
85
  @create_attendee_from_meeting = true
77
86
  @create_meeting_by_get_request = false
87
+ @verify_meeting_request_id = true
78
88
  end
79
89
  end
80
90
  end
@@ -12,15 +12,17 @@ module ChimeSdk
12
12
  end
13
13
 
14
14
  # GET /meetings/:meeting_id/attendees
15
+ # Join tokens are omitted since they are credentials of each attendee.
15
16
  def index
16
17
  list_attendees
17
- render json: { attendees: @attendees }
18
+ render json: { attendees: @attendees.map { |attendee| without_join_token(attendee) } }
18
19
  end
19
20
 
20
21
  # GET /meetings/:meeting_id/attendees/:attendee_id
22
+ # The join token is omitted since it is a credential of the attendee.
21
23
  def show
22
24
  get_attendee
23
- render json: @attendee
25
+ render json: without_join_token(@attendee)
24
26
  end
25
27
 
26
28
  # POST /meetings/:meeting_id/attendees
@@ -55,13 +57,25 @@ module ChimeSdk
55
57
  params[:id]
56
58
  end
57
59
 
60
+ # Unique meeting request id to identify meeting by Amazon Chime.
61
+ # Configure it depending on your application resources to identify meeting.
62
+ # For example, set "PrivateRoom-#{@room.id}" by Room model.
63
+ # Requested meetings are verified with this id when ChimeSdk.config.verify_meeting_request_id is enabled.
64
+ # @api protected
65
+ # @return [String] Unique meeting request id to identify meeting by Amazon Chime
66
+ def meeting_request_id
67
+ "default"
68
+ end
69
+
58
70
  # Unique attendee request id to identify attendee by Amazon Chime.
59
71
  # Configure it depending on your application resources to identify attendee.
60
72
  # For example, set "User-#{current_user.id}" by User model.
73
+ # Each user must have a different value, since users with the same value join meetings as the same attendee.
61
74
  # @api protected
62
75
  # @return [String] Unique attendee request id to identify attendee by Amazon Chime
76
+ # @raise [NotImplementedError] Unless overridden in your controllers
63
77
  def attendee_request_id
64
- "default"
78
+ raise NotImplementedError, "Define attendee_request_id in #{self.class.name} to identify each attendee, e.g. \"User-\#{current_user.id}\""
65
79
  end
66
80
 
67
81
  # Path for meetings#index action such as meetings_path.
@@ -1,6 +1,9 @@
1
1
  module ChimeSdk
2
2
  # Controllers implementation by amazon-chime-sdk-rails.
3
3
  module Controller
4
+ # Error raised when the requested meeting is not the meeting identified by meeting_request_id.
5
+ class MeetingNotFoundError < StandardError; end
6
+
4
7
  # Common implementation for controllers.
5
8
  module Common
6
9
  extend ActiveSupport::Concern
@@ -9,6 +12,7 @@ module ChimeSdk
9
12
  rescue_from Aws::ChimeSDKMeetings::Errors::ForbiddenException, with: :render_forbidden
10
13
  rescue_from Aws::ChimeSDKMeetings::Errors::NotFoundException, with: :render_resource_not_found
11
14
  rescue_from Aws::ChimeSDKMeetings::Errors::ValidationException, with: :render_resource_not_found
15
+ rescue_from ChimeSdk::Controller::MeetingNotFoundError, with: :render_resource_not_found
12
16
 
13
17
  class ::String
14
18
  # Convets to boolean.
@@ -51,7 +55,7 @@ module ChimeSdk
51
55
  # @api protected
52
56
  # @return [Hash] Meeting
53
57
  def get_meeting
54
- @meeting = ChimeSdk::MeetingCoordinator.get_meeting(meeting_id)
58
+ @meeting = verify_meeting(ChimeSdk::MeetingCoordinator.get_meeting(meeting_id))
55
59
  @meeting = merge_application_meeting_metadata(@meeting)
56
60
  end
57
61
 
@@ -59,13 +63,43 @@ module ChimeSdk
59
63
  # @api protected
60
64
  # @return [void]
61
65
  def delete_meeting
66
+ verify_meeting_id
62
67
  ChimeSdk::MeetingCoordinator.delete_meeting(meeting_id)
63
68
  end
64
69
 
70
+ # Whether to verify that requested meetings were created with ChimeSdk.config.prefix + meeting_request_id.
71
+ # Override it to change the behavior for each controller.
72
+ # @api protected
73
+ # @return [Boolean] Whether to verify requested meetings
74
+ def verify_meeting_request_id?
75
+ ChimeSdk.config.verify_meeting_request_id
76
+ end
77
+
78
+ # Verify that the meeting was created with ChimeSdk.config.prefix + meeting_request_id.
79
+ # @api protected
80
+ # @param [Hash] meeting Meeting JSON object as hash
81
+ # @return [Hash] Verified meeting
82
+ # @raise [ChimeSdk::Controller::MeetingNotFoundError] When the meeting is not the meeting identified by meeting_request_id
83
+ def verify_meeting(meeting)
84
+ if verify_meeting_request_id? && meeting.dig(:Meeting, :ExternalMeetingId) != ChimeSdk.config.prefix + meeting_request_id
85
+ raise ChimeSdk::Controller::MeetingNotFoundError, "Meeting not found"
86
+ end
87
+ meeting
88
+ end
89
+
90
+ # Verify the requested meeting by meeting id unless it has already been verified.
91
+ # @api protected
92
+ # @return [void]
93
+ # @raise [ChimeSdk::Controller::MeetingNotFoundError] When the meeting is not the meeting identified by meeting_request_id
94
+ def verify_meeting_id
95
+ verify_meeting(ChimeSdk::MeetingCoordinator.get_meeting(meeting_id)) if @meeting.nil? && verify_meeting_request_id?
96
+ end
97
+
65
98
  # List attendees by MeetingCoordinator.
66
99
  # @api protected
67
100
  # @return [Array<Hash>] Attendee list
68
101
  def list_attendees
102
+ verify_meeting_id
69
103
  @attendees = ChimeSdk::MeetingCoordinator.list_attendees(meeting_id)
70
104
  @attendees = @attendees.map { |attendee| merge_application_attendee_metadata(attendee) }
71
105
  end
@@ -74,6 +108,7 @@ module ChimeSdk
74
108
  # @api protected
75
109
  # @return [Hash] Created attendee
76
110
  def create_attendee
111
+ verify_meeting_id
77
112
  @attendee = ChimeSdk::MeetingCoordinator.create_attendee(meeting_id, attendee_request_id)
78
113
  @attendee = merge_application_attendee_metadata(@attendee)
79
114
  end
@@ -82,6 +117,7 @@ module ChimeSdk
82
117
  # @api protected
83
118
  # @return [Hash] Attendee
84
119
  def get_attendee
120
+ verify_meeting_id
85
121
  @attendee = ChimeSdk::MeetingCoordinator.get_attendee(meeting_id, attendee_id)
86
122
  @attendee = merge_application_attendee_metadata(@attendee)
87
123
  end
@@ -90,6 +126,7 @@ module ChimeSdk
90
126
  # @api protected
91
127
  # @return [void]
92
128
  def delete_attendee
129
+ verify_meeting_id
93
130
  ChimeSdk::MeetingCoordinator.delete_attendee(meeting_id, attendee_id)
94
131
  end
95
132
 
@@ -154,6 +191,15 @@ module ChimeSdk
154
191
  attendee
155
192
  end
156
193
 
194
+ # Return attendee without its join token.
195
+ # Join token is a credential to join the meeting as the attendee and must not be returned to other users.
196
+ # @api protected
197
+ # @param [Hash] attendee Attendee JSON object as hash
198
+ # @return [Hash] Attendee without join token
199
+ def without_join_token(attendee)
200
+ attendee.merge(Attendee: attendee[:Attendee].except(:JoinToken))
201
+ end
202
+
157
203
  # Returns error response as Hash
158
204
  # @api protected
159
205
  # @return [Hash] Error message
@@ -16,9 +16,9 @@ module ChimeSdk
16
16
  # GET /meetings.json
17
17
  # @overload index(params)
18
18
  # @param [Hash] params Request parameter options
19
- # @option params [String] :create_meeting (ChimeSdk.config.create_meeting_by_get_request) Whether the application creates meeting in this meetings#index action by HTTP GET request
19
+ # @option params [String] :create_meeting (ChimeSdk.config.create_meeting_by_get_request) Whether the application creates meeting in this meetings#index action by HTTP GET request. This parameter can only disable it since GET requests are not protected from CSRF.
20
20
  def index
21
- if params[:create_meeting].to_s.to_boolean(false) || ChimeSdk.config.create_meeting_by_get_request && params[:create_meeting].to_s.to_boolean(true)
21
+ if ChimeSdk.config.create_meeting_by_get_request && params[:create_meeting].to_s.to_boolean(true)
22
22
  create
23
23
  else
24
24
  @meeting_request_id = ChimeSdk.config.prefix + meeting_request_id
@@ -34,10 +34,10 @@ module ChimeSdk
34
34
  # GET /meetings/:meeting_id.json
35
35
  # @overload show(params)
36
36
  # @param [Hash] params Request parameter options
37
- # @option params [String] :create_attendee_from_meeting (ChimeSdk.config.create_attendee_from_meeting) Whether the application creates attendee from meeting in meetings#show action
37
+ # @option params [String] :create_attendee_from_meeting (ChimeSdk.config.create_attendee_from_meeting) Whether the application creates attendee from meeting in meetings#show action. This parameter can only disable it since GET requests are not protected from CSRF.
38
38
  def show
39
39
  get_meeting
40
- if params[:create_attendee_from_meeting].to_s.to_boolean(false) || ChimeSdk.config.create_attendee_from_meeting && params[:create_attendee_from_meeting].to_s.to_boolean(true)
40
+ if ChimeSdk.config.create_attendee_from_meeting && params[:create_attendee_from_meeting].to_s.to_boolean(true)
41
41
  create_attendee_from_meeting
42
42
  end
43
43
  respond_to do |format|
@@ -97,10 +97,12 @@ module ChimeSdk
97
97
  # Unique attendee request id to identify attendee by Amazon Chime.
98
98
  # Configure it depending on your application resources to identify attendee.
99
99
  # For example, set "User-#{current_user.id}" by User model.
100
+ # Each user must have a different value, since users with the same value join meetings as the same attendee.
100
101
  # @api protected
101
102
  # @return [String] Unique attendee request id to identify attendee by Amazon Chime
103
+ # @raise [NotImplementedError] Unless overridden in your controllers
102
104
  def attendee_request_id
103
- "default"
105
+ raise NotImplementedError, "Define attendee_request_id in #{self.class.name} to identify each attendee, e.g. \"User-\#{current_user.id}\""
104
106
  end
105
107
 
106
108
  # Path for meetings#index action such as meetings_path.
@@ -1,4 +1,4 @@
1
1
  module ChimeSdk
2
2
  # Gem version of amazon-chime-sdk-rails
3
- VERSION = "2.1.0"
3
+ VERSION = "2.2.1"
4
4
  end
@@ -4,13 +4,17 @@ module ChimeSdk
4
4
  module Generators
5
5
  # Amazon Chime SDK single .js file generator.
6
6
  # Bundle Amazon Chime SDK into single amazon-chime-sdk.min.js file and copy it to app/assets/javascripts directory.
7
- # @example Run Amazon Chime SDK single .js file generator with the latest version from master branch
7
+ # @example Run Amazon Chime SDK single .js file generator with the version in the pinned singlejs package.json
8
8
  # rails generate chime_sdk:js
9
9
  # @example Run Amazon Chime SDK single .js file generator with specified version
10
10
  # rails generate chime_sdk:js 2.24.0
11
11
  # @see https://github.com/aws-samples/amazon-chime-sdk/tree/main/utils/singlejs
12
12
  # @see https://www.npmjs.com/package/amazon-chime-sdk-js
13
13
  class JsGenerator < Rails::Generators::Base
14
+ # Commit of aws-samples/amazon-chime-sdk repository to build the single .js file from.
15
+ # Pinned since the generator runs npm scripts in the cloned repository.
16
+ SINGLEJS_REPOSITORY_COMMIT = "6abe76a732c1fce4cce9d2dbbf28946482ac203e"
17
+
14
18
  desc <<-DESC.strip_heredoc
15
19
  Bundle Amazon Chime SDK into single amazon-chime-sdk.min.js file and copy it to app/assets/javascripts directory.
16
20
 
@@ -76,6 +80,7 @@ module ChimeSdk
76
80
  system "mkdir -p tmp"
77
81
  puts "Cloning into 'amazon-chime-sdk-js' git repository in tmp directory ..."
78
82
  system "cd tmp; git clone https://github.com/aws-samples/amazon-chime-sdk.git > /dev/null 2>&1"
83
+ system "cd tmp/amazon-chime-sdk; git -c advice.detachedHead=false checkout #{SINGLEJS_REPOSITORY_COMMIT} > /dev/null 2>&1"
79
84
  repository_path = "tmp/amazon-chime-sdk"
80
85
  singlejs_path = "#{repository_path}/utils/singlejs"
81
86
  package_json_path = "#{singlejs_path}/package.json"
@@ -22,4 +22,9 @@ ChimeSdk.configure do |config|
22
22
 
23
23
  # Configure whether the application creates meeting in meetings#index action by HTTP GET request.
24
24
  config.create_meeting_by_get_request = false
25
+
26
+ # Configure whether the controllers verify that requested meetings were created with config.prefix + meeting_request_id.
27
+ # When enabled, meetings and attendees controllers respond with 404 for meetings of other resources, such as other private rooms.
28
+ # Default value is true. Disabling it allows any meeting in your AWS account to be accessed by its meeting id.
29
+ config.verify_meeting_request_id = true
25
30
  end
@@ -23,14 +23,26 @@ class <%= @namespace %><%= @class_name_prefix %>MeetingAttendeesController < App
23
23
  # params[:id]
24
24
  # end
25
25
 
26
+ # Unique meeting request id to identify meeting by Amazon Chime.
27
+ # Configure it depending on your application resources to identify meeting.
28
+ # For example, set "PrivateRoom-#{@room.id}" by Room model.
29
+ # Use the same value as your meetings controller since requested meetings are verified with this id.
30
+ # @api protected
31
+ # @return [String] Unique meeting request id to identify meeting by Amazon Chime
32
+ def meeting_request_id
33
+ # "PrivateRoom-#{@room.id}"
34
+ "<%= @default_meeting_request_id %>"
35
+ end
36
+
26
37
  # Unique attendee request id to identify attendee by Amazon Chime.
27
38
  # Configure it depending on your application resources to identify attendee.
28
39
  # For example, set "User-#{current_user.id}" by User model.
40
+ # Each user must have a different value, since users with the same value join meetings as the same attendee.
29
41
  # @api protected
30
42
  # @return [String] Unique attendee request id to identify attendee by Amazon Chime
31
43
  def attendee_request_id
32
44
  # "User-#{current_user.id}"
33
- "default"
45
+ raise NotImplementedError, "Define attendee_request_id in #{self.class.name} to identify each attendee, e.g. \"User-\#{current_user.id}\""
34
46
  end
35
47
 
36
48
  # Path for meetings#index action such as meetings_path.
@@ -97,10 +109,10 @@ class <%= @namespace %><%= @class_name_prefix %>MeetingAttendeesController < App
97
109
  # @param [Hash] meeting Attendee JSON object as hash
98
110
  # @return [Hash] Appication metadata for attendees
99
111
  # def application_attendee_metadata(attendee)
100
- # user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
112
+ # user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
101
113
  # {
102
114
  # "AttendeeType": "User",
103
- # "User": User.find_by_id(user_id)
115
+ # "User": User.find_by_id(user_id)&.slice(:id, :name)
104
116
  # }
105
117
  # end
106
118
  end
@@ -28,11 +28,12 @@ class <%= @namespace %><%= @class_name_prefix %>MeetingsController < Application
28
28
  # Unique attendee request id to identify attendee by Amazon Chime.
29
29
  # Configure it depending on your application resources to identify attendee.
30
30
  # For example, set "User-#{current_user.id}" by User model.
31
+ # Each user must have a different value, since users with the same value join meetings as the same attendee.
31
32
  # @api protected
32
33
  # @return [String] Unique attendee request id to identify attendee by Amazon Chime
33
34
  def attendee_request_id
34
35
  # "User-#{current_user.id}"
35
- "default"
36
+ raise NotImplementedError, "Define attendee_request_id in #{self.class.name} to identify each attendee, e.g. \"User-\#{current_user.id}\""
36
37
  end
37
38
 
38
39
  # Path for meetings#index action such as meetings_path.
@@ -117,7 +118,7 @@ class <%= @namespace %><%= @class_name_prefix %>MeetingsController < Application
117
118
  # def application_meeting_metadata(meeting)
118
119
  # {
119
120
  # "MeetingType": "PrivateRoom",
120
- # "PrivateRoom": @room
121
+ # "PrivateRoom": @room.slice(:id, :name)
121
122
  # }
122
123
  # end
123
124
 
@@ -127,10 +128,10 @@ class <%= @namespace %><%= @class_name_prefix %>MeetingsController < Application
127
128
  # @param [Hash] meeting Attendee JSON object as hash
128
129
  # @return [Hash] Appication metadata for attendees
129
130
  # def application_attendee_metadata(attendee)
130
- # user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
131
+ # user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
131
132
  # {
132
133
  # "AttendeeType": "User",
133
- # "User": User.find_by_id(user_id)
134
+ # "User": User.find_by_id(user_id)&.slice(:id, :name)
134
135
  # }
135
136
  # end
136
137
 
@@ -38,7 +38,6 @@
38
38
  <% end %>
39
39
 
40
40
  <%= javascript_include_tag "amazon-chime-sdk.min.js" %>
41
- <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
42
41
 
43
42
  <script defer>
44
43
  // This is a very simple script as a template view using Amazon Chime SDK.
@@ -63,9 +62,16 @@
63
62
  const configuration = new ChimeSDK.MeetingSessionConfiguration(meeting, attendee);
64
63
  const meetingSession = new ChimeSDK.DefaultMeetingSession(configuration, logger, deviceController);
65
64
 
66
- const attendeeApi = axios.create({
67
- baseURL: `${attendeeResourcesPath}/`
68
- });
65
+ async function getAttendee(attendeeId) {
66
+ const response = await fetch(`${attendeeResourcesPath}/${encodeURIComponent(attendeeId)}`, {
67
+ headers: { 'Accept': 'application/json' },
68
+ credentials: 'same-origin'
69
+ });
70
+ if (!response.ok) {
71
+ throw new Error(`Failed to get attendee: ${response.status}`);
72
+ }
73
+ return response.json();
74
+ }
69
75
 
70
76
  async function join() {
71
77
  try {
@@ -109,9 +115,9 @@
109
115
  const presentAttendeeMap = {};
110
116
  const callback = (presentAttendeeId, present) => {
111
117
  if (present) {
112
- attendeeApi.get(presentAttendeeId)
113
- .then(response => {
114
- presentAttendeeMap[presentAttendeeId] = response.data;
118
+ getAttendee(presentAttendeeId)
119
+ .then(attendee => {
120
+ presentAttendeeMap[presentAttendeeId] = attendee;
115
121
  updateAttendeeStatus(presentAttendeeMap);
116
122
  })
117
123
  .catch(error => {
@@ -126,11 +132,15 @@
126
132
  }
127
133
 
128
134
  function updateAttendeeStatus(presentAttendeeMap) {
129
- presentAttendees = "<strong>Present Attendees</strong><ul>";
135
+ // Use textContent since attendee names are user-provided values
136
+ const title = document.createElement('strong');
137
+ title.textContent = 'Present Attendees';
138
+ const list = document.createElement('ul');
130
139
  Object.values(presentAttendeeMap).forEach(attendee => {
131
- presentAttendees = presentAttendees + `<li>${showApplicationUserName(attendee)}</li>`
140
+ const item = document.createElement('li');
141
+ item.textContent = showApplicationUserName(attendee);
142
+ list.appendChild(item);
132
143
  });
133
- presentAttendees = presentAttendees + '</ul>'
134
- document.getElementById("attendee-status").innerHTML = presentAttendees;
144
+ document.getElementById("attendee-status").replaceChildren(title, list);
135
145
  }
136
146
  </script>
@@ -29,6 +29,8 @@ describe ChimeSdk::Generators::ControllersGenerator, type: :generator do
29
29
  it { is_expected.to exist }
30
30
  it { is_expected.to contain(/class MeetingAttendeesController < ApplicationController/) }
31
31
  it { is_expected.to contain(" meetings_path(params)\n") }
32
+ it { is_expected.to contain(" def meeting_request_id\n # \"PrivateRoom-\#{@room.id}\"\n \"default\"\n") }
33
+ it { is_expected.to contain(" def attendee_request_id\n # \"User-\#{current_user.id}\"\n raise NotImplementedError") }
32
34
  end
33
35
  end
34
36
 
@@ -2,10 +2,16 @@ class Api::Public::MeetingAttendeesController < ApplicationController
2
2
  include ChimeSdk::Controller::Attendees::Mixin
3
3
 
4
4
  def application_attendee_metadata(attendee)
5
- user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
5
+ user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
6
6
  {
7
7
  "AttendeeType": "User",
8
- "User": User.find_by_id(user_id)
8
+ "User": User.find_by_id(user_id)&.slice(:id, :name)
9
9
  }
10
10
  end
11
+
12
+ # This public API for the React Meeting Demo accepts any meeting in the AWS account by its meeting id.
13
+ # Do not disable this verification in your applications unless you authorize access to meetings by yourself.
14
+ def verify_meeting_request_id?
15
+ false
16
+ end
11
17
  end
@@ -6,10 +6,16 @@ class Api::Public::MeetingsController < ApplicationController
6
6
  end
7
7
 
8
8
  def application_meeting_metadata(meeting)
9
- room_id = meeting[:Meeting][:ExternalMeetingId].split('-')[3]
9
+ room_id = meeting[:Meeting][:ExternalMeetingId].delete_prefix(ChimeSdk.config.prefix)[/\APrivateRoom-(\d+)\z/, 1]
10
10
  {
11
11
  "MeetingType": "PrivateRoom",
12
- "PrivateRoom": Room.find_by_id(room_id)
12
+ "PrivateRoom": Room.find_by_id(room_id)&.slice(:id, :name)
13
13
  }
14
14
  end
15
+
16
+ # This public API for the React Meeting Demo accepts any meeting in the AWS account by its meeting id.
17
+ # Do not disable this verification in your applications unless you authorize access to meetings by yourself.
18
+ def verify_meeting_request_id?
19
+ false
20
+ end
15
21
  end
@@ -39,6 +39,16 @@ class MeetingAttendeesController < ApplicationController
39
39
  # params[:id]
40
40
  # end
41
41
 
42
+ # Unique meeting request id to identify meeting by Amazon Chime.
43
+ # Configure it depending on your application resources to identify meeting.
44
+ # For example, set "PrivateRoom-#{@room.id}" by Room model.
45
+ # Use the same value as your meetings controller since requested meetings are verified with this id.
46
+ # @api protected
47
+ # @return [String] Unique meeting request id to identify meeting by Amazon Chime
48
+ def meeting_request_id
49
+ "PrivateRoom-#{@room.id}"
50
+ end
51
+
42
52
  # Unique attendee request id to identify attendee by Amazon Chime.
43
53
  # Configure it depending on your application resources to identify attendee.
44
54
  # For example, set "User-#{current_user.id}" by User model.
@@ -112,10 +122,10 @@ class MeetingAttendeesController < ApplicationController
112
122
  # @param [Hash] meeting Attendee JSON object as hash
113
123
  # @return [Hash] Appication metadata for attendees
114
124
  def application_attendee_metadata(attendee)
115
- user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
125
+ user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
116
126
  {
117
127
  "AttendeeType": "User",
118
- "User": User.find_by_id(user_id)
128
+ "User": User.find_by_id(user_id)&.slice(:id, :name)
119
129
  }
120
130
  end
121
131
  end
@@ -134,7 +134,7 @@ class MeetingsController < ApplicationController
134
134
  def application_meeting_metadata(meeting)
135
135
  {
136
136
  "MeetingType": "PrivateRoom",
137
- "PrivateRoom": @room
137
+ "PrivateRoom": @room.slice(:id, :name)
138
138
  }
139
139
  end
140
140
 
@@ -144,10 +144,10 @@ class MeetingsController < ApplicationController
144
144
  # @param [Hash] meeting Attendee JSON object as hash
145
145
  # @return [Hash] Appication metadata for attendees
146
146
  def application_attendee_metadata(attendee)
147
- user_id = attendee[:Attendee][:ExternalUserId].split('-')[3]
147
+ user_id = attendee[:Attendee][:ExternalUserId].delete_prefix(ChimeSdk.config.prefix)[/\AUser-(\d+)\z/, 1]
148
148
  {
149
149
  "AttendeeType": "User",
150
- "User": User.find_by_id(user_id)
150
+ "User": User.find_by_id(user_id)&.slice(:id, :name)
151
151
  }
152
152
  end
153
153
 
@@ -43,7 +43,6 @@
43
43
  <% end %>
44
44
 
45
45
  <%= javascript_include_tag "amazon-chime-sdk.min.js" %>
46
- <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
47
46
 
48
47
  <script defer>
49
48
  // This is a very simple script as a template view using Amazon Chime SDK.
@@ -75,9 +74,16 @@
75
74
  const configuration = new ChimeSDK.MeetingSessionConfiguration(meeting, attendee);
76
75
  const meetingSession = new ChimeSDK.DefaultMeetingSession(configuration, logger, deviceController);
77
76
 
78
- const attendeeApi = axios.create({
79
- baseURL: `${attendeeResourcesPath}/`
80
- });
77
+ async function getAttendee(attendeeId) {
78
+ const response = await fetch(`${attendeeResourcesPath}/${encodeURIComponent(attendeeId)}`, {
79
+ headers: { 'Accept': 'application/json' },
80
+ credentials: 'same-origin'
81
+ });
82
+ if (!response.ok) {
83
+ throw new Error(`Failed to get attendee: ${response.status}`);
84
+ }
85
+ return response.json();
86
+ }
81
87
 
82
88
  async function join() {
83
89
  try {
@@ -121,9 +127,9 @@
121
127
  const presentAttendeeMap = {};
122
128
  const callback = (presentAttendeeId, present) => {
123
129
  if (present) {
124
- attendeeApi.get(presentAttendeeId)
125
- .then(response => {
126
- presentAttendeeMap[presentAttendeeId] = response.data;
130
+ getAttendee(presentAttendeeId)
131
+ .then(attendee => {
132
+ presentAttendeeMap[presentAttendeeId] = attendee;
127
133
  updateAttendeeStatus(presentAttendeeMap);
128
134
  })
129
135
  .catch(error => {
@@ -138,11 +144,15 @@
138
144
  }
139
145
 
140
146
  function updateAttendeeStatus(presentAttendeeMap) {
141
- presentAttendees = "<strong>Present Attendees</strong><ul>";
147
+ // Use textContent since attendee names are user-provided values
148
+ const title = document.createElement('strong');
149
+ title.textContent = 'Present Attendees';
150
+ const list = document.createElement('ul');
142
151
  Object.values(presentAttendeeMap).forEach(attendee => {
143
- presentAttendees = presentAttendees + `<li>${showApplicationUserName(attendee)}</li>`
152
+ const item = document.createElement('li');
153
+ item.textContent = showApplicationUserName(attendee);
154
+ list.appendChild(item);
144
155
  });
145
- presentAttendees = presentAttendees + '</ul>'
146
- document.getElementById("attendee-status").innerHTML = presentAttendees;
156
+ document.getElementById("attendee-status").replaceChildren(title, list);
147
157
  }
148
158
  </script>
@@ -8,11 +8,7 @@
8
8
  <p>
9
9
  <strong>Private Meeting:</strong>
10
10
  <p><%= link_to 'Show Meetings Index', room_meetings_path(@room) %></p>
11
- <p>
12
- Create Meeting
13
- <%= link_to 'GET', room_meetings_path(@room, create_meeting: true) %>
14
- <%= link_to 'POST', room_meetings_path(@room), method: :post %>
15
- </p>
11
+ <p><%= link_to 'Create Meeting', room_meetings_path(@room), method: :post %></p>
16
12
  </p>
17
13
 
18
14
  <p>
@@ -4,8 +4,11 @@ describe ChimeSdk::Controller::Attendees, type: :request do
4
4
  @attendee_id = "76543210-ba98-fedc-jihg-vutsrqponmlk"
5
5
  @dummy_meeting_id = "91234567-89ab-cdef-ghij-klmnopqrstuv"
6
6
  @dummy_attendee_id = "96543210-ba98-fedc-jihg-vutsrqponmlk"
7
+ @other_meeting_id = "81234567-89ab-cdef-ghij-klmnopqrstuv"
8
+ @other_app_attendee_id = "86543210-ba98-fedc-jihg-vutsrqponmlk"
7
9
 
8
10
  @room = create(:room)
11
+ @other_room = create(:room)
9
12
  @attendee_user = create(:user)
10
13
  @room.add_member(@attendee_user)
11
14
  test_attendee = {
@@ -29,14 +32,39 @@ describe ChimeSdk::Controller::Attendees, type: :request do
29
32
  }
30
33
  }
31
34
 
35
+ meeting_of = -> (meeting_id, room) {
36
+ {
37
+ meeting_id: meeting_id,
38
+ external_meeting_id: "ChimeSdkRailsApp-test-PrivateRoom-#{room.id}",
39
+ media_region: "us-east-1",
40
+ media_placement: { audio_host_url: "dummy" }
41
+ }
42
+ }
43
+
44
+ other_app_attendee = test_attendee.merge(
45
+ external_user_id: "OtherApp-test-User-#{@attendee_user.id}",
46
+ attendee_id: @other_app_attendee_id
47
+ )
48
+
32
49
  # Use stubs for the AWS SDK for Ruby
33
50
  client = Aws::ChimeSDKMeetings::Client.new(stub_responses: true)
51
+ client.stub_responses(:get_meeting, -> (context) {
52
+ case context.params[:meeting_id]
53
+ when @meeting_id then { meeting: meeting_of.(@meeting_id, @room) }
54
+ when @other_meeting_id then { meeting: meeting_of.(@other_meeting_id, @other_room) }
55
+ else 'ForbiddenException'
56
+ end
57
+ })
34
58
  client.stub_responses(:list_attendees, {
35
59
  attendees: [ test_attendee, dummy_attendee ]
36
60
  })
37
61
  client.stub_responses(:get_attendee, -> (context) {
38
62
  context.params[:meeting_id] == @meeting_id ?
39
- (context.params[:attendee_id] == @attendee_id ? { attendee: test_attendee } : 'NotFoundException') :
63
+ (case context.params[:attendee_id]
64
+ when @attendee_id then { attendee: test_attendee }
65
+ when @other_app_attendee_id then { attendee: other_app_attendee }
66
+ else 'NotFoundException'
67
+ end) :
40
68
  'ForbiddenException'
41
69
  })
42
70
  client.stub_responses(:create_attendee, -> (context) {
@@ -109,6 +137,12 @@ describe ChimeSdk::Controller::Attendees, type: :request do
109
137
  it "returns valid attendee in the list" do
110
138
  expect(JSON.parse(response.body)["attendees"][0]["Attendee"]["AttendeeId"]).to eq(@attendee_id)
111
139
  end
140
+
141
+ it "does not return join tokens of attendees in the list" do
142
+ JSON.parse(response.body)["attendees"].each do |attendee|
143
+ expect(attendee["Attendee"]).not_to have_key("JoinToken")
144
+ end
145
+ end
112
146
  end
113
147
 
114
148
  describe "GET /meetings/:meeting_id/attendees/:attendee_id" do
@@ -125,9 +159,28 @@ describe ChimeSdk::Controller::Attendees, type: :request do
125
159
  expect(JSON.parse(response.body)["Attendee"]["AttendeeId"]).to eq(@attendee_id)
126
160
  end
127
161
 
162
+ it "does not return join token of the attendee" do
163
+ expect(JSON.parse(response.body)["Attendee"]).not_to have_key("JoinToken")
164
+ end
165
+
128
166
  it "returns application metadata in the attendee" do
129
167
  expect(JSON.parse(response.body)["Attendee"]["ApplicationMetadata"]["User"]["id"]).to eq(@attendee_user.id)
130
168
  end
169
+
170
+ it "returns only id and name of the user in application metadata" do
171
+ expect(JSON.parse(response.body)["Attendee"]["ApplicationMetadata"]["User"].keys).to match_array(["id", "name"])
172
+ end
173
+ end
174
+
175
+ context "when the attendee is created by another application" do
176
+ before do
177
+ get "#{api_meetings_path}/attendees/#{@other_app_attendee_id}", headers: @auth_headers
178
+ end
179
+
180
+ it "does not resolve the user from external user id" do
181
+ expect(response).to have_http_status(200)
182
+ expect(JSON.parse(response.body)["Attendee"]["ApplicationMetadata"]["User"]).to be_nil
183
+ end
131
184
  end
132
185
 
133
186
  context "when the attendee is not found" do
@@ -165,6 +218,34 @@ describe ChimeSdk::Controller::Attendees, type: :request do
165
218
  end
166
219
  end
167
220
 
221
+ context "when the meeting belongs to another private room" do
222
+ let(:other_meeting_path) { "#{api_path}/meetings/#{@other_meeting_id}" }
223
+
224
+ it "returns response with 404 status to GET /meetings/:meeting_id/attendees" do
225
+ expect(ChimeSdk::MeetingCoordinator).not_to receive(:list_attendees)
226
+ get "#{other_meeting_path}/attendees", headers: @auth_headers
227
+ expect(response).to have_http_status(404)
228
+ end
229
+
230
+ it "returns response with 404 status to GET /meetings/:meeting_id/attendees/:attendee_id" do
231
+ expect(ChimeSdk::MeetingCoordinator).not_to receive(:get_attendee)
232
+ get "#{other_meeting_path}/attendees/#{@attendee_id}", headers: @auth_headers
233
+ expect(response).to have_http_status(404)
234
+ end
235
+
236
+ it "returns response with 404 status to POST /meetings/:meeting_id/attendees" do
237
+ expect(ChimeSdk::MeetingCoordinator).not_to receive(:create_attendee)
238
+ post "#{other_meeting_path}/attendees", headers: @auth_headers
239
+ expect(response).to have_http_status(404)
240
+ end
241
+
242
+ it "returns response with 404 status to DELETE /meetings/:meeting_id/attendees/:attendee_id" do
243
+ expect(ChimeSdk::MeetingCoordinator).not_to receive(:delete_attendee)
244
+ delete "#{other_meeting_path}/attendees/#{@attendee_id}", headers: @auth_headers
245
+ expect(response).to have_http_status(404)
246
+ end
247
+ end
248
+
168
249
  describe "DELETE /meetings/:meeting_id/attendees/:attendee_id" do
169
250
  context "when the attendee is found" do
170
251
  before do
@@ -3,8 +3,10 @@ describe ChimeSdk::Controller::Meetings, type: :request do
3
3
  @meeting_id = "01234567-89ab-cdef-ghij-klmnopqrstuv"
4
4
  @attendee_id = "76543210-ba98-fedc-jihg-vutsrqponmlk"
5
5
  @dummy_meeting_id = "91234567-89ab-cdef-ghij-klmnopqrstuv"
6
+ @other_meeting_id = "81234567-89ab-cdef-ghij-klmnopqrstuv"
6
7
 
7
8
  @room = create(:room)
9
+ @other_room = create(:room)
8
10
  dummy_media_placement = {
9
11
  audio_host_url: "dummy",
10
12
  audio_fallback_url: "dummy",
@@ -51,10 +53,19 @@ describe ChimeSdk::Controller::Meetings, type: :request do
51
53
  meeting_arn: "arn:aws:chime:us-east-1:000000000000:meeting/#{@meeting_id}"
52
54
  }
53
55
 
56
+ other_room_meeting = test_meeting.merge(
57
+ meeting_id: @other_meeting_id,
58
+ external_meeting_id: "ChimeSdkRailsApp-test-PrivateRoom-#{@other_room.id}"
59
+ )
60
+
54
61
  # Use stubs for the AWS SDK for Ruby
55
62
  client = Aws::ChimeSDKMeetings::Client.new(stub_responses: true)
56
63
  client.stub_responses(:get_meeting, -> (context) {
57
- context.params[:meeting_id] == @meeting_id ? { meeting: test_meeting } : 'NotFoundException'
64
+ case context.params[:meeting_id]
65
+ when @meeting_id then { meeting: test_meeting }
66
+ when @other_meeting_id then { meeting: other_room_meeting }
67
+ else 'NotFoundException'
68
+ end
58
69
  })
59
70
  client.stub_responses(:create_meeting, {
60
71
  meeting: test_meeting
@@ -131,18 +142,37 @@ describe ChimeSdk::Controller::Meetings, type: :request do
131
142
  end
132
143
  end
133
144
 
134
- context "with true as create_meeting parameter" do
145
+ context "with true as create_meeting parameter and ChimeSdk.config.create_meeting_by_get_request is disabled" do
135
146
  before do
136
147
  get "#{api_path}/meetings", headers: @auth_headers, params: { create_meeting: true }
137
148
  end
138
149
 
139
- it "returns response with 201 status" do
140
- expect(response).to have_http_status(201)
150
+ it "does not create meeting" do
151
+ expect(response).to have_http_status(200)
152
+ expect(JSON.parse(response.body)["Meeting"]).to be_nil
141
153
  end
154
+ end
142
155
 
143
- it "returns created meeting" do
156
+ context "when ChimeSdk.config.create_meeting_by_get_request is enabled" do
157
+ before do
158
+ ChimeSdk.config.create_meeting_by_get_request = true
159
+ end
160
+
161
+ after do
162
+ ChimeSdk.config.create_meeting_by_get_request = false
163
+ end
164
+
165
+ it "returns created meeting with 201 status" do
166
+ get "#{api_path}/meetings", headers: @auth_headers
167
+ expect(response).to have_http_status(201)
144
168
  expect(JSON.parse(response.body)["Meeting"]["MeetingId"]).to eq(@meeting_id)
145
169
  end
170
+
171
+ it "does not create meeting with false as create_meeting parameter" do
172
+ get "#{api_path}/meetings", headers: @auth_headers, params: { create_meeting: false }
173
+ expect(response).to have_http_status(200)
174
+ expect(JSON.parse(response.body)["Meeting"]).to be_nil
175
+ end
146
176
  end
147
177
  end
148
178
 
@@ -164,6 +194,10 @@ describe ChimeSdk::Controller::Meetings, type: :request do
164
194
  expect(JSON.parse(response.body)["Meeting"]["ApplicationMetadata"]["PrivateRoom"]["id"]).to eq(@room.id)
165
195
  end
166
196
 
197
+ it "returns only id and name of the room in application metadata" do
198
+ expect(JSON.parse(response.body)["Meeting"]["ApplicationMetadata"]["PrivateRoom"].keys).to match_array(["id", "name"])
199
+ end
200
+
167
201
  it "returns created attendee since ChimeSdk.config.create_attendee_from_meeting is enabled" do
168
202
  expect(JSON.parse(response.body)["Attendee"]["AttendeeId"]).to eq(@attendee_id)
169
203
  end
@@ -178,6 +212,52 @@ describe ChimeSdk::Controller::Meetings, type: :request do
178
212
  expect(response).to have_http_status(404)
179
213
  end
180
214
  end
215
+
216
+ context "with true as create_attendee_from_meeting parameter and ChimeSdk.config.create_attendee_from_meeting is disabled" do
217
+ before do
218
+ ChimeSdk.config.create_attendee_from_meeting = false
219
+ get "#{api_path}/meetings/#{@meeting_id}", params: { create_attendee_from_meeting: true }, headers: @auth_headers
220
+ end
221
+
222
+ after do
223
+ ChimeSdk.config.create_attendee_from_meeting = true
224
+ end
225
+
226
+ it "does not create attendee" do
227
+ expect(response).to have_http_status(200)
228
+ expect(JSON.parse(response.body)["Attendee"]).to be_nil
229
+ end
230
+ end
231
+
232
+ context "when the meeting belongs to another private room" do
233
+ before do
234
+ get "#{api_path}/meetings/#{@other_meeting_id}", headers: @auth_headers
235
+ end
236
+
237
+ it "returns response with 404 status" do
238
+ expect(response).to have_http_status(404)
239
+ end
240
+
241
+ it "does not return the meeting" do
242
+ expect(JSON.parse(response.body)["Meeting"]).to be_nil
243
+ end
244
+ end
245
+
246
+ context "when the meeting belongs to another private room and ChimeSdk.config.verify_meeting_request_id is disabled" do
247
+ before do
248
+ ChimeSdk.config.verify_meeting_request_id = false
249
+ get "#{api_path}/meetings/#{@other_meeting_id}", params: { create_attendee_from_meeting: false }, headers: @auth_headers
250
+ end
251
+
252
+ after do
253
+ ChimeSdk.config.verify_meeting_request_id = true
254
+ end
255
+
256
+ it "returns the meeting" do
257
+ expect(response).to have_http_status(200)
258
+ expect(JSON.parse(response.body)["Meeting"]["MeetingId"]).to eq(@other_meeting_id)
259
+ end
260
+ end
181
261
  end
182
262
 
183
263
  describe "POST /meetings" do
@@ -243,6 +323,14 @@ describe ChimeSdk::Controller::Meetings, type: :request do
243
323
  expect(response).to have_http_status(404)
244
324
  end
245
325
  end
326
+
327
+ context "when the meeting belongs to another private room" do
328
+ it "returns response with 404 status without deleting the meeting" do
329
+ expect(ChimeSdk::MeetingCoordinator).not_to receive(:delete_meeting)
330
+ delete "#{api_path}/meetings/#{@other_meeting_id}", headers: @auth_headers
331
+ expect(response).to have_http_status(404)
332
+ end
333
+ end
246
334
  end
247
335
  end
248
336
  end
@@ -310,16 +398,29 @@ describe ChimeSdk::Controller::Meetings, type: :request do
310
398
  end
311
399
  end
312
400
 
313
- context "with true as create_meeting parameter" do
401
+ context "with true as create_meeting parameter and ChimeSdk.config.create_meeting_by_get_request is disabled" do
314
402
  before do
315
403
  get "#{api_path}/meetings", params: { create_meeting: true }
316
404
  end
317
405
 
318
- it "returns response with 302 status" do
319
- expect(response).to have_http_status(302)
406
+ it "does not create meeting" do
407
+ expect(response).to have_http_status(200)
408
+ expect(response.body).to include("Meetings by Amazon Chime SDK")
409
+ end
410
+ end
411
+
412
+ context "when ChimeSdk.config.create_meeting_by_get_request is enabled" do
413
+ before do
414
+ ChimeSdk.config.create_meeting_by_get_request = true
415
+ get "#{api_path}/meetings"
416
+ end
417
+
418
+ after do
419
+ ChimeSdk.config.create_meeting_by_get_request = false
320
420
  end
321
421
 
322
422
  it "redirects to created meeting path" do
423
+ expect(response).to have_http_status(302)
323
424
  expect(response.body).to redirect_to("#{api_path}/meetings/#{@meeting_id}")
324
425
  end
325
426
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amazon-chime-sdk-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shota Yamazaki
@@ -116,6 +116,7 @@ executables: []
116
116
  extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
+ - ".github/dependabot.yml"
119
120
  - ".github/workflows/build.yml"
120
121
  - ".gitignore"
121
122
  - ".rspec"