Class ::xo::Authorize (public)
::nx::Class ::xo::Authorize
Defined in packages/xooauth/tcl/authorize-procs.tcl
Base class to support OAuth authorization API
- Testcases:
- No testcase defined.
Source code: :property {pretty_name} :property {base_url} :property {responder_url} :property {after_successful_login_url /pvt/} :property {login_failure_url /} :property {create_not_registered_users:switch false} :property {create_with_dotlrn_role ""} :property {scope} :property {debug:switch false} :method qualified {partial_url} { return [util_current_location]$partial_url } :method encoded_state {{-return_url ""}} { set state [::xo::oauth::nonce] append state . [ns_base64urlencode $return_url] return $state } :method decoded_state {state} { lassign [split $state .] nonce encoded_url return [list nonce $nonce return_url [ns_base64urldecode $encoded_url]] } :public method login_url { {-return_url ""} {-login} } { # # Returns the URL for log-in # set base ${:base_url}/authorize set client_id ${:client_id} set scope ${:scope} set state [:encoded_state -return_url $return_url] set redirect_uri [:qualified ${:responder_url}] return [export_vars -no_empty -base $base { client_id redirect_uri state scope login }] } :method redeem_code {code} { set client_id ${:client_id} set client_secret ${:client_secret} set redirect_uri [:qualified ${:responder_url}] set url [export_vars -no_empty -base ${:base_url}/access_token { client_id client_secret code redirect_uri }] set data [ns_http run $url] if {[dict get $data status] ne 200} { dict set data error oacs-cant_redeem_code dict set data error_description $data } else { set form_data [ns_set array [ns_parsequery [dict get $data body]]] ns_log notice "[self] redeem_code formdata has keys: [lsort [dict keys $form_data]]" if {![dict exists $form_data access_token]} { if {[dict exists $form_data error]} { dict set data error [dict get $form_data error] dict set data error_description [dict get $form_data error_description] } else { dict set data error oacs-no_access_token dict set data error_description $form_data } } else { dict set data access_token [dict get $form_data access_token] } } ns_log notice "[self] redeem_code returns $data" return $data } :public method logout {} { # # Perform logout operation from oauth in the background # (i.e. without a redirect) when the logout_url is # nonempty. # set url [:logout_url] if {$url ne ""} { ns_http run $url } } :public method name {} { # # @return instance name # return [expr {[info exists :pretty_name] ? ${:pretty_name} : [namespace tail [self]]}] } :method lookup_user_id {-email} -returns integer { set user_id [party::get_by_email -email [string tolower $email]] if {$user_id eq ""} { # # Here one could do some more checks or alternative lookups # } return [expr {$user_id eq "" ? 0 : $user_id}] } :method required_fields {} { return [expr {${:create_not_registered_users} ? "email given_name family_name" : "email"}] } :method get_required_fields { {-claims:required} {-mapped_fields:required} } { # # Check, if required fields are provided in the claims and # perform the name mapping between what was provided from # the identity provided and what we need in OpenACS. # set result "" set fields {} foreach pair $mapped_fields { lassign $pair field target if {[dict exists $claims $field]} { dict set fields $target [dict get $claims $field] } } dict set result fields $fields foreach field [:required_fields] { if {![dict exists $fields $field] || [dict get $fields $field] in {"" "null"} } { set not_enough_data $field break } } if {[info exists not_enough_data]} { ns_log warning "[self] get_user_data: not enough data:" $not_enough_data "is missing" "($field -> $target, claims: $claims)" dict set result error oacs-not_enough_data } return $result } :method record_oauth_registration {user_id} { # # Record the fact that this user_id was created via an # OAuth identity provider. # set auth_obj [self] db_dml _ { INSERT INTO xooauth_authorized_users (user_id, auth_obj) VALUES (:user_id, :auth_obj) } } :method register_new_user { {-first_names} {-last_name} {-email} } -returns integer { # # Register the user and return the user_id. In case, the # registration of the new user fails, raise an exception. # # not tested # db_transaction { set user_info(first_names) $first_names set user_info(last_name) $last_name if {![util_email_unique_p $email]} { error "Email is not unique: $email" } set user_info(email) $email array set creation_info [auth::create_local_account -authority_id [auth::authority::local] -username $email -array user_info] if {$creation_info(creation_status) ne "ok"} { set errorMsg "" error [append errorMsg "Error when creating user: " $creation_info(creation_status) " " $creation_info(element_messages)] } set user_id $creation_info(user_id) :record_oauth_registration $user_id if {[apm_package_installed_p dotlrn] && ${:create_with_dotlrn_role} ne ""} { # # We have DotLRN installed, and we want to create # for this register object the new users in the # provided role. Note that one can define # different instances of this class behaving # differently. # dotlrn::user_add -type ${:create_with_dotlrn_role} -can_browse=1 -id $email -user_id $user_id ::permission::grant -party_id $user_id -object_id [dotlrn::get_package_id] -privilege read_private_data } } on_error { ns_log error "OAuth Login (Error during user creation): $errmsg ($email)" error "OAuth user creation failed: $errmsg" } return $user_id } :public method perform_login {-token {-state ""}} { # # Get the provided claims from the identity provider and # perform an OpenACS login, when the user exists. # # In case the user does not exist, create it optionally (when # "create_not_registered_users" is activated. # # When the user is created, and dotlrn is installed, the # new user might be added optionally as a dotlrn user with # the role as specified in "create_with_dotlrn_role". # set data [:get_user_data -token $token] if {[dict exists $data error]} { # # There was already an error in the steps leading to # this. # ns_log warning "[self] OAuth login failed:" [dict get $data error] "\n$data" } elseif {![dict exists $data email]} { # # No error and no email in result... actually, this # should not happen. # dict set data error oacs-no_email_in_result ns_log warning "OAuth login failed strangely: " [dict get $data error] "\n$data" } else { dict set data decoded_state [:decoded_state $state] set user_id [:lookup_user_id -email [dict get $data email]] if {!${:debug} && $user_id == 0 && ${:create_not_registered_users} } { try { :register_new_user -first_names [dict get $data given_name] -last_name [dict get $data family_name] -email [dict get $data email] } on ok {result} { set user_id $result } on error {errorMsg} { dict set data error oacs-register_failed dict set data error_description $errorMsg } } dict set data user_id $user_id if {$user_id != 0} { # # The lookup of the user_id was successful. We can # login as this user.... but only, when no "debug" # is activated. # if {!${:debug}} { ad_user_login -external_registry [self] $user_id } } else { # # For the time being, just report data back to the # calling script. # dict set data error "oacs-no_such_user" } } return $data }XQL Not present: Generic, PostgreSQL, Oracle