Skip to content

Commit

Permalink
New user journey dev (#1278)
Browse files Browse the repository at this point in the history
* Add reason for manual cancel in build cancellation

* Add login of user who cancelled the build

* Use user instead of current user

* Use RSS token for builds atom feed

re #BSFY-206

* Change reason for cancellation

* Revert "Add reason for manual cancel in build cancellation"

* New payment details implementation

* Antifraud for v1 plans

* Add spec

* Fix spec

* Take recaptcha secret from env

* storage,collaborator cleanup , specs

* redirection for app updates

* using vcs_redirect only if there's no state param

* force redirection to github-apps endpoint on install

* corrected storage key name

* storage fix

* storage param name fix

* auth with installation updates

* dbg

* Dt fingerprint fix (#1289)

* fingerprint fix

* spec
  • Loading branch information
GbArc authored Jul 24, 2023
1 parent e3ffe60 commit b810180
Show file tree
Hide file tree
Showing 28 changed files with 802 additions and 26 deletions.
13 changes: 12 additions & 1 deletion lib/travis/api/app/endpoint/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,15 @@ class Authorization < Endpoint
get '/handshake/?:provider?' do
method = org? ? :handshake : :vcs_handshake
params[:provider] ||= 'github'
params[:signup] ||= false
send(method) do |user, token, redirect_uri|
if target_ok? redirect_uri
user[:installation] = params[:installation_id]
content_type :html
if params[:setup_action] && params[:setup_action] == 'install' && params[:provider] == 'github'
redirect_uri = redirect_uri + "?installation_id=#{params[:installation_id]}"
redirect_uri = "#{Travis.config.vcs_redirects.web_url}#{Travis.config.vcs_redirects[params[:provider]]}?installation_id=#{params[:installation_id]}"
end
data = { user: user, token: token, uri: redirect_uri }
erb(:post_payload, locals: data)
else
Expand Down Expand Up @@ -214,6 +220,10 @@ def remote_vcs_user

def vcs_handshake
if params[:code]
if params[:setup_action] && (params[:setup_action] == 'update' || params[:setup_action] == 'install') && params[:provider] && !params[:state]
redirect to("#{Travis.config.vcs_redirects.web_url}#{Travis.config.vcs_redirects[params[:provider]]}?installation_id=#{params[:installation_id]}")
end

unless state_ok?(params[:state], params[:provider])
handle_invalid_response
return
Expand All @@ -239,7 +249,8 @@ def vcs_handshake
vcs_data = remote_vcs_user.auth_request(
provider: params[:provider],
state: state,
redirect_uri: oauth_endpoint
redirect_uri: oauth_endpoint,
signup: params[:signup]
)

response.set_cookie(cookie_name(params[:provider]), value: state, httponly: true)
Expand Down
16 changes: 14 additions & 2 deletions lib/travis/api/v3/billing_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def allowance(owner_type, owner_id)
response = connection(timeout: ALLOWANCE_TIMEOUT).get("/usage/#{owner_type.downcase}s/#{owner_id}/allowance")
return BillingClient.default_allowance_response unless response.status == 200

Travis::API::V3::Models::Allowance.new(2, owner_id, response.body)
Travis::API::V3::Models::Allowance.new(response.body.fetch('subscription_type', 2), owner_id, response.body)
end

def authorize_build(repo, sender_id, jobs)
Expand All @@ -27,7 +27,11 @@ def self.default_allowance_response(id = 0)
"private_repos" => false,
"concurrency_limit" => 1,
"user_usage" => false,
"pending_user_licenses" => false
"pending_user_licenses" => false,
"payment_changes_block_captcha" => false,
"payment_changes_block_credit" => false,
"credit_card_block_duration" => 0,
"captcha_block_duration" => 0
}.freeze)
end

Expand Down Expand Up @@ -239,6 +243,14 @@ def cancel_v2_subscription(id, reason_data)
handle_subscription_response(response)
end

def usage_stats(owners)
data = connection.post("/usage/stats", owners: owners, query: 'paid_plan_count')
data = data&.body
data.fetch('paid_plans').to_i > 0 if data && data['paid_plans']
rescue
false
end

private

def handle_subscription_response(response)
Expand Down
7 changes: 6 additions & 1 deletion lib/travis/api/v3/models/allowance.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Travis::API::V3
class Models::Allowance
attr_reader :subscription_type, :public_repos, :private_repos, :concurrency_limit, :user_usage, :pending_user_licenses, :id
attr_reader :subscription_type, :public_repos, :private_repos, :concurrency_limit, :user_usage, :pending_user_licenses, :id,
:payment_changes_block_credit, :payment_changes_block_captcha, :credit_card_block_duration, :captcha_block_duration

def initialize(subscription_type, owner_id, attributes = {})
@subscription_type = subscription_type
Expand All @@ -11,6 +12,10 @@ def initialize(subscription_type, owner_id, attributes = {})
@concurrency_limit = attributes.fetch('concurrency_limit', nil)
@user_usage = attributes.fetch('user_usage', nil)
@pending_user_licenses = attributes.fetch('pending_user_licenses', nil)
@payment_changes_block_captcha = attributes.fetch('payment_changes_block_captcha', nil)
@payment_changes_block_credit = attributes.fetch('payment_changes_block_credit', nil)
@credit_card_block_duration = attributes.fetch('credit_card_block_duration', nil)
@captcha_block_duration = attributes.fetch('captcha_block_duration', nil)
end
end
end
38 changes: 38 additions & 0 deletions lib/travis/api/v3/models/storage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'redis'

module Travis::API::V3
class Models::Storage

attr_reader :id, :value

def initialize(attrs)
puts "ATTRS: #{attrs.inspect}"
@id = attrs.fetch(:id)
@value = attrs[:value]
end

def public?
true
end

def get
puts "ID: #{id.inspect}, VAL: #{value.inspect}"
@value = Travis.redis.get(id) || 0


puts "ID: #{id.inspect}, VAL: #{value.inspect}"
self
end

def create
Travis.redis.set(id, value)
self
end

def delete
Travis.redis.del(id)
@value = 0
self
end
end
end
45 changes: 45 additions & 0 deletions lib/travis/api/v3/queries/storage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Travis::API::V3
class Queries::Storage < Query
params :user, :id,:value, prefix: :storage

PERMITTED_OPTIONS = [:billing_wizard_state]

def find
Models::Storage.new(id: option_id).get if valid?
end

def update
Models::Storage.new(id: option_id, value: value).create if valid?
end

def delete
Models::Storage.new(id: option_id).delete if valid?
end

private
def option_id
"#{user}::storage::#{id}"
end

def valid?
PERMITTED_OPTIONS.include? id.to_sym
end


def user
params['user.id']
end

def id
params['id']
end

def value
params['value']
end

def redis
Travis.redis
end
end
end
40 changes: 40 additions & 0 deletions lib/travis/api/v3/queries/subscription.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
module Travis::API::V3
class Queries::Subscription < Query
def update_payment_details(user_id)
recaptcha_redis_key = "recaptcha_attempts_v1_#{params['subscription.id']}"
count = Travis.redis.get(recaptcha_redis_key)&.to_i
count = count.nil? ? 0 : count
if count > captcha_max_failed_attempts
raise ClientError, 'Error verifying reCAPTCHA, you have exausted your attempts, please wait.'
end

result = recaptcha_client.verify(params['captcha_token'])
unless result
if count == 0
Travis.redis.setex(recaptcha_redis_key, captcha_block_duration, count + 1)
else
ttl = Travis.redis.ttl(recaptcha_redis_key)
Travis.redis.setex(recaptcha_redis_key, ttl, count + 1)
end
raise ClientError, 'Error verifying reCAPTCHA, please try again.'
end

address_data = params.dup.tap { |h| h.delete('subscription.id') }
address_data = address_data.tap { |h| h.delete('token') }
client = BillingClient.new(user_id)
client.update_address(params['subscription.id'], address_data) unless address_data.empty?
client.update_creditcard(params['subscription.id'], params['token']) if params.key?('token')
end

def update_address(user_id)
address_data = params.dup.tap { |h| h.delete('subscription.id') }
client = BillingClient.new(user_id)
Expand Down Expand Up @@ -42,5 +68,19 @@ def pay(user_id)
client = BillingClient.new(user_id)
client.pay(params['subscription.id'])
end

private

def recaptcha_client
@recaptcha_client ||= RecaptchaClient.new
end

def captcha_block_duration
Travis.config.antifraud.captcha_block_duration
end

def captcha_max_failed_attempts
Travis.config.antifraud.captcha_max_failed_attempts
end
end
end
21 changes: 21 additions & 0 deletions lib/travis/api/v3/queries/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@ def find_by_email(email)
end
end

def collaborator?(id)
user = Models::User.find_by_id(id) if id
return false unless user

owners=[]
user.organizations.each do |org|
owners << {
:id => org.id,
:type => 'Organization'
}
end
Models::Repository.where(id: user.shared_repositories_ids).uniq.pluck(:owner_id, :owner_type).each do |owner|
owners << {
:id => owner[0],
:type =>owner[1]
}
end
client = BillingClient.new(id)
client.usage_stats(owners)
end

def sync(user)
raise AlreadySyncing if user.is_syncing?
if Travis::Features.user_active?(:use_vcs, user) || !user.github?
Expand Down
40 changes: 40 additions & 0 deletions lib/travis/api/v3/queries/v2_subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@ module Travis::API::V3
class Queries::V2Subscription < Query
params :enabled, :threshold, :amount

def update_payment_details(user_id)
recaptcha_redis_key = "recaptcha_attempts_v2_#{params['subscription.id']}"
count = Travis.redis.get(recaptcha_redis_key)&.to_i
count = count.nil? ? 0 : count
if count > captcha_max_failed_attempts
raise ClientError, 'Error verifying reCAPTCHA, you have exausted your attempts, please wait.'
end

result = recaptcha_client.verify(params['captcha_token'])
unless result
if count == 0
Travis.redis.setex(recaptcha_redis_key, captcha_block_duration, count + 1)
else
ttl = Travis.redis.ttl(recaptcha_redis_key)
Travis.redis.setex(recaptcha_redis_key, ttl, count + 1)
end
raise ClientError, 'Error verifying reCAPTCHA, please try again.'
end

address_data = params.dup.tap { |h| h.delete('subscription.id') }
address_data = address_data.tap { |h| h.delete('token') }
client = BillingClient.new(user_id)
client.update_v2_address(params['subscription.id'], address_data) unless address_data.empty?
client.update_v2_creditcard(params['subscription.id'], params['token'], params['fingerprint']) if params.key?('token')
end

def update_address(user_id)
address_data = params.dup.tap { |h| h.delete('subscription.id') }
client = BillingClient.new(user_id)
Expand Down Expand Up @@ -60,5 +86,19 @@ def update_auto_refill(user_id, addon_id)
client = BillingClient.new(user_id)
client.update_auto_refill(addon_id, threshold, amount)
end

private

def recaptcha_client
@recaptcha_client ||= RecaptchaClient.new
end

def captcha_block_duration
Travis.config.antifraud.captcha_block_duration
end

def captcha_max_failed_attempts
Travis.config.antifraud.captcha_max_failed_attempts
end
end
end
41 changes: 41 additions & 0 deletions lib/travis/api/v3/recaptcha_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module Travis::API::V3
class RecaptchaClient
class ConfigurationError < StandardError; end

def verify(token)
response = connection.post('/recaptcha/api/siteverify') do |req|
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
req.body = URI.encode_www_form({ secret: recaptcha_secret, response: token })
end
handle_errors_and_respond(response) { |r| r['success'] }
end

private

def handle_errors_and_respond(response)
case response.status
when 200, 201
yield(JSON.parse(response.body)) if block_given?
when 204
true
else
raise Travis::API::V3::ServerError, 'ReCaptcha system error'
end
end

def connection
@connection ||= Faraday.new(url: recaptcha_url, ssl: { ca_path: '/usr/lib/ssl/certs' }) do |conn|
conn.use OpenCensus::Trace::Integrations::FaradayMiddleware if Travis::Api::App::Middleware::OpenCensus.enabled?
conn.adapter Faraday.default_adapter
end
end

def recaptcha_url
Travis.config.recaptcha.endpoint || raise(ConfigurationError, 'No recaptcha url configured')
end

def recaptcha_secret
Travis.config.recaptcha.secret || raise(ConfigurationError, 'No recaptcha secret configured')
end
end
end
3 changes: 2 additions & 1 deletion lib/travis/api/v3/renderer/allowance.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Travis::API::V3
class Renderer::Allowance < ModelRenderer
representation(:minimal, :id)
representation(:standard, :subscription_type, :public_repos, :private_repos, :concurrency_limit, :user_usage, :pending_user_licenses, :id)
representation(:standard, :subscription_type, :public_repos, :private_repos, :concurrency_limit, :user_usage, :pending_user_licenses,
:payment_changes_block_credit, :payment_changes_block_captcha, :credit_card_block_duration, :captcha_block_duration, :id)
end
end
9 changes: 9 additions & 0 deletions lib/travis/api/v3/renderer/storage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Travis::API::V3
class Renderer::Storage < ModelRenderer
representation(:standard,:id, :value)

def id
model.id.split('::')&.last || id
end
end
end
6 changes: 5 additions & 1 deletion lib/travis/api/v3/renderer/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Travis::API::V3
class Renderer::User < Renderer::Owner
representation(:standard, :email, :is_syncing, :synced_at, :recently_signed_up, :secure_user_hash, :ro_mode, :confirmed_at, :custom_keys)
representation(:additional, :emails)
representation(:additional, :emails, :collaborator)

def email
@model.email if show_emails?
Expand All @@ -13,6 +13,10 @@ def emails
show_emails? ? @model.emails.map(&:email) : []
end

def collaborator
query(:user).collaborator? @model.id
end

def secure_user_hash
hmac_secret_key = Travis.config.intercom && Travis.config.intercom.hmac_secret_key.to_s
OpenSSL::HMAC.hexdigest('sha256', hmac_secret_key, @model.id.to_s) if @model.id && hmac_secret_key
Expand Down
Loading

0 comments on commit b810180

Please sign in to comment.