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

: ensure the system is valid and exists #334

Open
github-actions bot opened this issue Feb 9, 2023 · 0 comments
Open

: ensure the system is valid and exists #334

github-actions bot opened this issue Feb 9, 2023 · 0 comments
Assignees
Labels

Comments

@github-actions
Copy link

github-actions bot commented Feb 9, 2023

the system id that defines the name and rules for a collection of chats

note, this is not a chat, it represents a collection of chats

  • system public flag for grabbing metadata

this route provides a guest access to an anonymous chat room

# TODO:: ensure the system is valid and exists

require "base64"
require "./application"
require "./webrtc/*"

module PlaceOS::Api
  class WebRTC < Application
    base "/api/engine/v2/webrtc/"

    struct CaptchaResponse
      include JSON::Serializable

      property success : Bool
    end

    class PlaceOS::Api::Error
      class RecaptchaFailed < Error
      end

      class GuestAccessDisabled < Error
      end
    end

    # 401 if recaptcha fails
    @[AC::Route::Exception(Error::RecaptchaFailed, status_code: HTTP::Status::UNAUTHORIZED)]
    def recaptcha_failed(error) : CommonError
      Log.debug { error.message }
      CommonError.new(error, false)
    end

    JWT_SECRET = ENV["JWT_SECRET"]?.try { |k| Base64.decode_string(k) }

    # this route provides a guest access to an anonymous chat room
    @[AC::Route::POST("/guest_entry/:system_id", body: guest)]
    def guest_entry(
      guest : GuestParticipant,
      @[AC::Param::Info(description: "either a system id or a unique permalink", example: "sys-12345")]
      system_id : String
    ) : Nil
      jwt_secret = JWT_SECRET
      raise Error::GuestAccessDisabled.new("guest access not enabled") unless jwt_secret

      # TODO:: ensure the system is valid and exists
      # the system id that defines the name and rules for a collection of chats
      # note, this is not a chat, it represents a collection of chats

      # captcha, name, phone, type, chat_to_user_id, room_id, guest_chat_id (user_id), session_id
      authority = current_authority.not_nil!
      if recaptcha_secret = authority.internals["recaptcha_secret"]?.try(&.as_s)
        HTTP::Client.new("www.google.com", tls: true) do |client|
          client.connect_timeout = 2

          begin
            captresp = client.post("/recaptcha/api/siteverify?secret=#{recaptcha_secret}&response=#{guest.captcha}")
            if captresp.success?
              result = CaptchaResponse.from_json(captresp.body)
              raise Error::RecaptchaFailed.new("recaptcha rejected") unless result.success
            else
              raise Error::RecaptchaFailed.new("error verifying recaptcha response")
            end
          rescue error
            # We don't want chat to be out of action if google is down, so we'll continue
            Log.error(exception: error) { "recaptcha failed" }
          end
        end
      else
        raise Error::RecaptchaFailed.new("recaptcha not configured")
      end

      payload = {
        iss:   "POS",
        iat:   1.minute.ago.to_unix,
        exp:   12.hours.from_now.to_unix,
        jti:   UUID.random.to_s,
        aud:   authority.domain,
        scope: ["guest"],
        sub:   guest.user_id,
        u:     {
          n: guest.name,
          e: guest.email || "#{guest.phone}@phone" || "#{guest.name}@unknown",
          p: 0,
          r: [guest.user_id, system_id],
        },
      }

      jwt = JWT.encode(payload, jwt_secret, JWT::Algorithm::RS256)
      cookies["api-key"] = jwt
      ::PlaceOS::Driver::RedisStorage.with_redis &.publish("placeos/#{authority.domain}/guest/entry", {
        system_id => guest,
      }.to_json)
    end

    @[AC::Route::POST("/transfer/:user_id/?:session_id", body: body, status: {
      Nil  => HTTP::Status::OK,
      Bool => HTTP::Status::PRECONDITION_REQUIRED,
    })]
    def transfer_guest(
      user_id : String,
      session_id : String? = nil,
      body : JSON::Any? = nil
    ) : Nil | Bool
      result = MANAGER.transfer(user_id, session_id, body.try &.to_json)
      case result
      in .signal_sent?
        nil
      in .no_session?
        true
      in .no_connection?
        false
      end
    end

    struct RoomDetails
      include JSON::Serializable

      getter system : Model::ControlSystem
      getter metadata : Hash(String, PlaceOS::Model::Metadata::Interface)

      def initialize(@system, @metadata)
      end
    end

    # * create a permalink entry for systems
    # * system public flag for grabbing metadata
    # *
    # this route provides a guest access to an anonymous chat room
    @[AC::Route::GET("/system/:system_id")]
    def public_room(
      @[AC::Param::Info(description: "either a system id or a unique permalink", example: "sys-12345")]
      system_id : String
    ) : RoomDetails
      # TODO:: check the system is public
      system = Model::ControlSystem.find!(system_id, runopts: {"read_mode" => "majority"})
      meta = Model::Metadata.build_metadata(system_id, nil)
      RoomDetails.new(system, meta)
    end

    ICE_CONFIG = {} of String => String
    MANAGER    = ChatManager.new(ICE_CONFIG)

    # WebRTC signaller endpoint, managing call participants
    @[AC::Route::WebSocket("/signaller")]
    def signaller(websocket) : Nil
      Log.trace { {request_id: request_id, frame: "OPEN"} }

      authority = current_authority.not_nil!
      auth_id = authority.id.as(String)

      # https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer
      ICE_CONFIG[auth_id] = authority.internals["webrtc_ice"]?.try(&.to_json) || WEBRTC_DEFAULT_ICE_CONFIG

      MANAGER.handle_session(websocket, request_id, user_token.id, auth_id)
    end
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant