kelsin/byfirebepurged

View on GitHub
app/controllers/sessions_controller.rb

Summary

Maintainability
A
1 hr
Test Coverage
require 'bnet'

class SessionsController < ApplicationController
  skip_before_action :authenticate, :except => :destroy
  skip_authorization_check

  @@bnet = Bnet.new

  # Provides some static data to point users to the proper API requests and
  # websites.
  def index
  end

  # Attempts to login to the api. You must pass in a json body in the following form:
  #
  #     {
  #       "redirect": "https://localhost/?key="
  #     }
  #
  # The response will look like:
  #
  #     {
  #       "method": "Battle.net OAuth 2.0",
  #       "href": "https://localhost:3000/auth/bnet?key=960c109e-af1f-4012-9e8a-31783a3e9270"
  #     }
  #
  # To complete a login, forward your user to the provided href value, and then
  # we will redirect to your original redirect value with your proper API key
  # appended. In our example we would redirect to a url like:
  #
  #     https://localhost/?key=51032c55-5ff6-4ee7-887c-3d2e2f3587e1
  #
  # From now on just append the following header to all API requests:
  #
  #     Authorization: apikey 51032c55-5ff6-4ee7-887c-3d2e2f3587e1
  def new
    raise Exceptions::ByFireBePurgedError, 'Must provide a redirect value' unless params[:redirect]

    redirect = URI.parse(params[:redirect]) rescue nil

    unless redirect.kind_of?(URI::HTTP) or redirect.kind_of?(URI::HTTPS)
      raise Exceptions::ByFireBePurgedError, 'Redirect value must be a valid http or https url'
    end

    @login = Login.create(:key => SecureRandom.uuid,
                          :redirect => params[:redirect])
  end

  # This is the OAuth redirect route that we use when using Battle.net's OAuth
  # authentication.
  #
  # This route creates or saves the user with the proper account_id and
  # battletag and then loads their WoW characters and redirects to the original
  # redirect url provided by the user when they hit +new+
  def create
    # First find the login record created for this authentication attempt
    @login = Login.find_by_key(params_hash['key'])
    raise Exceptions::ByFireBePurgedError, "Can not find login request for #{params_hash['key']}" unless @login

    # Now find or create the account for this Battle.net account
    begin
      @account = Account.find_or_initialize_by(:account_id => auth_hash['info']['id'])
    rescue ActiveRecord::RecordNotUnique
      retry
    end

    raise Exceptions::ByFireBePurgedError, 'Error attempting to find account' unless @account

    @account.key ||= SecureRandom.uuid
    @account.battletag = auth_hash['info']['battletag']

    raise Exceptions::ByFireBePurgedError, 'Error attempting to update account' unless @account.save

    # Now create a session for this user
    @session = Session.create(:access_token => auth_hash['credentials']['token'],
                              :account_id => @account.id,
                              :key => SecureRandom.uuid)

    raise Exceptions::ByFireBePurgedError, 'Error creating session' unless @session

    # Update characters if it's been an hour since last login
    update_characters

    redirect_to "#{@login.redirect}#{@session.key}"
  end

  def destroy
    @session.try(:destroy)

    render :json => { :logged_out => true }
  end

  private

  # Helper to easily get the omniauth data from the request
  def auth_hash
    request.env['omniauth.auth']
  end

  # Helper to easily get the omniauth params from the request
  def params_hash
    request.env['omniauth.params']
  end

  # Update the accounts characters from the Battle.net api
  def update_characters
    @@bnet.characters(@session.access_token).each do |character|
      guild = nil
      if character['guild'].present? and character['guildRealm'].present?
        guild = update_guild(character['guild'], character['guildRealm'])
      end

      c = Character.find_or_initialize_by(:account_id => @session.account_id,
                                          :name => character['name'],
                                          :realm => character['realm'])
      c.update(:guild_id => guild.try(:id),
               :image_url => image_url(character),
               :level => character['level'],
               :race_id => character['race'],
               :class_id => character['class'],
               :gender_id => character['gender'])

      c.update(:item_level => @@bnet.ilvl(c.name, c.realm)) if c.level >= 100
    end
  end

  def update_guild(name, realm)
    guild = Guild.find_or_create_by(:name => name,
                                    :realm => realm)

    if guild
      response = @@bnet.guild(name, realm)
      emblem = response['emblem']
      guild.update(:icon => emblem['icon'],
                   :border => emblem['border'],
                   :icon_color => emblem['iconColor'],
                   :border_color => emblem['borderColor'],
                   :background_color => emblem['backgroundColor']) if emblem
    end

    return guild
  end

  # Full image url from api character data
  def image_url(character)
    "https://render-us.worldofwarcraft.com/character/#{character['thumbnail']}?alt=/wow/static/images/2d/avatar/#{character['race']}-#{character['gender']}.jpg"
  end
end