require_dependency 'account_creator' require_relative '../../lib/user_account_creator' class SignupController < ApplicationController include LoginHelper include AccountCreator layout 'frontend' ssl_required :signup, :create, :create_http_authentication, :create_http_authentication_in_progress skip_before_filter :http_header_authentication, only: [:create_http_authentication, :create_http_authentication_in_progress] before_filter :load_organization, only: [:create_http_authentication, :create_http_authentication_in_progress] before_filter :check_organization_quotas, only: [:create_http_authentication] before_filter :load_mandatory_organization, only: [:signup, :create] before_filter :disable_if_ldap_configured before_filter :initialize_oauth_config def signup email = params[:email].present? ? params[:email] : nil @user = ::User.new(email: email) end def create account_creator = CartoDB::UserAccountCreator.new(Carto::UserCreation::CREATED_VIA_ORG_SIGNUP). with_organization(@organization, viewer: invitation.try(:viewer)). with_invitation_token(params[:invitation_token]) raise "Organization doesn't allow user + password authentication" if user_password_signup? && !@organization.auth_username_password_enabled if params[:user] account_creator.with_username(params[:user][:username]) if params[:user][:username].present? account_creator.with_email(params[:user][:email]) if params[:user][:email].present? account_creator.with_password(params[:user][:password]) if params[:user][:password].present? end oauth_config = oauth_provider if oauth_config api = oauth_config.class.api_class.new(oauth_config, params[:oauth_access_token]) @oauth_fields = api.hidden_fields account_creator.with_oauth_api(api) end if account_creator.valid? trigger_account_creation(account_creator) render 'shared/signup_confirmation' else @user = account_creator.user errors = account_creator.validation_errors CartoDB.notify_debug('User not valid at signup', { errors: errors } ) if errors['organization'] && !errors[:organization].empty? @signup_source = 'Organization' @signup_errors = errors render 'shared/signup_issue' else if google_signup? && existing_user(@user) flash.now[:error] = "User already registered, go to login" elsif @user.errors.empty? # No need for additional errors if there're field errors flash.now[:error] = 'User not valid' end render action: 'signup', status: @user.errors.empty? ? 200 : 422 end end rescue StandardError => e @user ||= ::User.new CartoDB.notify_exception(e, { new_user: account_creator.user.inspect }) flash.now[:error] = e.message render action: 'signup', status: 400 end def create_http_authentication authenticator = Carto::HttpHeaderAuthentication.new render_404 and return false unless authenticator.autocreation_enabled? render_500 and return false unless authenticator.autocreation_valid?(request) render_403 and return false unless authenticator.valid?(request) account_creator = CartoDB::UserAccountCreator. new(Carto::UserCreation::CREATED_VIA_HTTP_AUTENTICATION). with_email_only(authenticator.email(request)) account_creator = account_creator.with_organization(@organization) if @organization if account_creator.valid? trigger_account_creation(account_creator) render 'shared/signup_confirmation' else render_500 end rescue StandardError => e CartoDB.report_exception(e, "Creating user with HTTP authentication", new_user: account_creator.user.inspect) flash.now[:error] = e.message render_500 end def create_http_authentication_in_progress authenticator = Carto::HttpHeaderAuthentication.new if !authenticator.creation_in_progress?(request) redirect_to CartoDB.url(self, 'login') else render 'shared/signup_confirmation' end end private def oauth_provider case params[:oauth_provider] when 'google' @google_config when 'github' @github_config end end def existing_user(user) !Carto::User.find_by_username_and_email(user.username, user.email).nil? end def google_access_token_from_params [params.fetch(:google_access_token, nil), params.fetch(:google_signup_access_token, nil)].uniq.compact.first end def google_signup? google_access_token_from_params.present? end def user_password_signup? params && params['user'] && params['user']['username'].present? && params['user']['email'].present? && params['user']['password'].present? end def initialize_oauth_config @oauth_configs = [google_config, github_config].compact end def google_config unless @organization && !@organization.auth_google_enabled @google_config = Carto::Oauth::Google::Config.instance(form_authenticity_token, google_oauth_url, invitation_token: params[:invitation_token], organization_name: @organization.try(:name)) end end def github_config unless @organization && !@organization.auth_github_enabled @github_config = Carto::Oauth::Github::Config.instance(form_authenticity_token, github_url, invitation_token: params[:invitation_token], organization_name: @organization.try(:name)) end end def load_organization if CartoDB.subdomainless_urls? subdomain = request.host.to_s.gsub(".#{CartoDB.session_domain}", '') if subdomain == CartoDB.session_domain subdomain = params[:user_domain] end else subdomain = CartoDB.subdomain_from_request(request) end if subdomain && subdomain != CartoDB.session_domain @organization = ::Organization.where(name: subdomain).first end end def check_organization_quotas if @organization check_signup_errors = Sequel::Model::Errors.new user = ::User.new_with_organization(@organization, viewer: @invitation.try(:viewer)) @organization.validate_for_signup(check_signup_errors, user) @signup_source = 'Organization' @signup_errors = check_signup_errors render 'shared/signup_issue' and return false if check_signup_errors.length > 0 end end def load_mandatory_organization load_organization return render_404 unless @organization && (@organization.signup_page_enabled || invitation) check_organization_quotas end def disable_if_ldap_configured render_404 and return false if Carto::Ldap::Manager.new.configuration_present? end def invitation return @invitation if @invitation email = (params[:user] && params[:user][:email]) || params[:email] token = params[:invitation_token] return unless email && token invitations = Carto::Invitation.query_with_valid_email(email).where(organization_id: @organization.id).all @invitation = invitations.find { |i| i.token(email) == token } end end