Implement phone number verification by SMS

This commit is contained in:
Kijin Sung 2019-09-13 14:57:36 +09:00
parent 5d058942af
commit 2a13e41953
8 changed files with 217 additions and 7 deletions

View file

@ -31,6 +31,8 @@
<action name="procMemberAuthAccount" type="controller" method="GET|POST" />
<action name="procMemberAuthEmailAddress" type="controller" method="GET|POST" />
<action name="procMemberResendAuthMail" type="controller" ruleset="resendAuthMail" />
<action name="procMemberSendVerificationSMS" type="controller" />
<action name="procMemberConfirmVerificationSMS" type="controller" />
<action name="procMemberModifyInfoBefore" type="controller" permission="member" ruleset="recheckedPassword" />
<action name="procMemberModifyInfo" type="controller" permission="member" ruleset="@insertMember" />
<action name="procMemberModifyPassword" type="controller" permission="member" ruleset="modifyPassword" />

View file

@ -34,6 +34,7 @@ $lang->group_order_change = 'Change Group Priority';
$lang->phone_number_default_country = 'Default Country Code';
$lang->phone_number_hide_country = 'Hide Country Code Selection';
$lang->phone_number_allow_duplicate = 'Allow Multiple Members';
$lang->phone_number_verify_by_sms = 'Require SMS Verification';
$lang->msg_need_default_country = 'You must select a default country code if you want to hide the country code selection box.';
$lang->signature = 'Signature';
$lang->profile_image = 'Profile Image';
@ -71,6 +72,14 @@ $lang->allow_message_type['Y'] = 'Allow All';
$lang->allow_message_type['F'] = 'Allow for Friends only';
$lang->allow_message_type['N'] = 'Reject All';
$lang->about_allow_message = 'You may allow or reject messages.';
$lang->verify_by_sms = 'Verify';
$lang->verify_by_sms_confirm = 'Confirm';
$lang->verify_by_sms_message = 'Your verification code is %s.';
$lang->verify_by_sms_code_sent = 'A verification code has been sent to the number you entered.';
$lang->verify_by_sms_code_incorrect = 'The code you entered is incorrect.';
$lang->verify_by_sms_code_confirmed = 'Your phone number has been confirmed.';
$lang->verify_by_sms_incomplete = 'Your phone number has not been verified. Please go through the verification process first.';
$lang->verify_by_sms_error = 'This website cannot send SMS.';
$lang->logged_users = 'Logged on Users';
$lang->webmaster_name = 'Webmaster Name';
$lang->webmaster_email = 'Webmaster Email';

View file

@ -34,6 +34,7 @@ $lang->group_order_change = '그룹 우선순위 변경';
$lang->phone_number_default_country = '기본 선택 국가';
$lang->phone_number_hide_country = '국가 목록 숨김';
$lang->phone_number_allow_duplicate = '중복 가입 허용';
$lang->phone_number_verify_by_sms = '문자 인증 사용';
$lang->msg_need_default_country = '국가 목록을 숨기려면 기본 선택 국가가 있어야 합니다.';
$lang->signature = '서명';
$lang->profile_image = '프로필 사진';
@ -71,6 +72,14 @@ $lang->allow_message_type['Y'] = '모두 허용';
$lang->allow_message_type['F'] = '등록된 친구들만 허용';
$lang->allow_message_type['N'] = '모두 금지';
$lang->about_allow_message = '쪽지 허용 방법 및 대상을 지정할 수 있습니다.';
$lang->verify_by_sms = '인증';
$lang->verify_by_sms_confirm = '인증번호 확인';
$lang->verify_by_sms_message = '인증번호는 %s입니다.';
$lang->verify_by_sms_code_sent = '인증번호가 SMS로 발송되었습니다.';
$lang->verify_by_sms_code_incorrect = '인증번호가 올바르지 않습니다.';
$lang->verify_by_sms_code_confirmed = '인증이 완료되었습니다.';
$lang->verify_by_sms_incomplete = '전화번호가 인증되지 않았습니다. 인증 과정을 거쳐 주십시오.';
$lang->verify_by_sms_error = 'SMS를 발송할 수 없습니다.';
$lang->logged_users = '현재 접속자';
$lang->msg_mail_authorization = '메일 인증을 사용하려면 웸마스터의 이름과 메일주소가 유효해야 합니다.';
$lang->webmaster_name = '웹마스터 이름';

View file

@ -317,7 +317,7 @@ class memberAdminController extends member
'limit_day_description',
'emailhost_check',
'redirect_url',
'phone_number_default_country', 'phone_number_hide_country', 'phone_number_allow_duplicate',
'phone_number_default_country', 'phone_number_hide_country', 'phone_number_allow_duplicate', 'phone_number_verify_by_sms',
'profile_image', 'profile_image_max_width', 'profile_image_max_height', 'profile_image_max_filesize',
'image_name', 'image_name_max_width', 'image_name_max_height', 'image_name_max_filesize',
'image_mark', 'image_mark_max_width', 'image_mark_max_height', 'image_mark_max_filesize',
@ -348,6 +348,7 @@ class memberAdminController extends member
$args->phone_number_default_country = preg_replace('/[^0-9-]/', '', $args->phone_number_default_country);
$args->phone_number_hide_country = $args->phone_number_hide_country == 'Y' ? 'Y' : 'N';
$args->phone_number_allow_duplicate = $args->phone_number_allow_duplicate == 'Y' ? 'Y' : 'N';
$args->phone_number_verify_by_sms = $args->phone_number_verify_by_sms == 'Y' ? 'Y' : 'N';
if ($args->phone_number_hide_country === 'Y' && !$args->phone_number_default_country)
{
return new BaseObject('-1', 'msg_need_default_country');

View file

@ -590,6 +590,14 @@ class memberAdminView extends member
$phone_number = '';
}
$inputTag .= '<input type="tel" name="phone_number" id="phone_number" class="phone_number" value="'.$phone_number .'" />';
if($member_config->phone_number_verify_by_sms === 'Y')
{
$inputTag .= "\n" . '<button type="button" class="btn verifySMS" style="display:none">' . lang('member.verify_by_sms') . '</button>';
$inputTag .= "\n" . '<div class="verifySMS_input_area" style="display:none">';
$inputTag .= '<input type="number" class="verifySMS_input_number" />';
$inputTag .= '<button type="button" class="btn verifySMS_input_button">' . lang('member.verify_by_sms_confirm') . '</button>';
$inputTag .= '</div>';
}
}
else if($formInfo->name == 'homepage')
{

View file

@ -610,6 +610,29 @@ class memberController extends member
$accept_agreement_rearranged[$i] = $accept_agreement[$i] === 'Y' ? 'Y' : 'N';
$accept_agreement_rearranged[$i] .= ':' . date('YmdHis', RX_TIME);
}
// Check phone number
if ($config->phone_number_verify_by_sms === 'Y')
{
if (!isset($_SESSION['verify_by_sms']) || !$_SESSION['verify_by_sms']['status'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
$phone_country = Context::get('phone_country');
if ($config->phone_number_default_country && (!$phone_country || $config->phone_number_hide_country === 'Y'))
{
$phone_country = $config->phone_number_default_country;
}
if ($phone_country !== $_SESSION['verify_by_sms']['country'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
$phone_number = Context::get('phone_number');
if ($phone_number !== $_SESSION['verify_by_sms']['number'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
}
// Extract the necessary information in advance
$getVars = array();
@ -855,10 +878,48 @@ class memberController extends member
throw new Rhymix\Framework\Exceptions\InvalidRequest;
}
unset($_SESSION['rechecked_password_step']);
// Get current module config and user info
$oMemberModel = getModel('member');
$config = $oMemberModel->getMemberConfig();
$logged_info = Context::get('logged_info');
// Check phone number
if ($config->phone_number_verify_by_sms === 'Y')
{
$phone_verify_needed = false;
$phone_country = Context::get('phone_country');
$phone_number = Context::get('phone_number');
if ($config->phone_number_default_country && (!$phone_country || $config->phone_number_hide_country === 'Y'))
{
$phone_country = $config->phone_number_default_country;
}
if (preg_replace('/[^0-9]/', '', $phone_country) !== $logged_info->phone_country)
{
$phone_verify_needed = true;
}
if (preg_replace('/[^0-9]/', '', $phone_number) !== $logged_info->phone_number)
{
$phone_verify_needed = true;
}
if ($phone_verify_needed)
{
if (!isset($_SESSION['verify_by_sms']) || !$_SESSION['verify_by_sms']['status'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($phone_country !== $_SESSION['verify_by_sms']['country'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($phone_number !== $_SESSION['verify_by_sms']['number'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
}
}
// Extract the necessary information in advance
$oMemberModel = &getModel ('member');
$config = $oMemberModel->getMemberConfig();
$getVars = array('allow_mailing','allow_message');
if($config->signupForm)
{
@ -884,10 +945,6 @@ class memberController extends member
$args->phone_country = preg_replace('/[^0-9-]/', '', Context::get('phone_country'));
}
}
// Login Information
$logged_info = Context::get('logged_info');
$args->member_srl = $logged_info->member_srl;
// mobile input date format can be different
if($args->birthday)
@ -906,6 +963,8 @@ class memberController extends member
{
$args->birthday = intval(strtr($args->birthday_ui, array('-'=>'', '/'=>'', '.'=>'', ' '=>'')));
}
$args->member_srl = $logged_info->member_srl;
// Remove some unnecessary variables from all the vars
$all_args = Context::getRequestVars();
@ -3234,6 +3293,77 @@ class memberController extends member
$this->setRedirectUrl(getNotEncodedUrl('', 'act', 'dispMemberInfo'));
}
function procMemberSendVerificationSMS()
{
$config = getModel('member')->getMemberConfig();
if ($config->phone_number_verify_by_sms !== 'Y')
{
throw new Rhymix\Framework\Exceptions\FeatureDisabled;
}
$phone_country = Context::get('phone_country');
$phone_number = Context::get('phone_number');
if ($config->phone_number_default_country && (!$phone_country || $config->phone_number_hide_country === 'Y'))
{
$phone_country = $config->phone_number_default_country;
}
if (!preg_match('/[0-9]{1,}/', $phone_country) || !preg_match('/[0-9]{2,}/', $phone_number))
{
return new BaseObject(-1, 'msg_invalid_phone_number');
}
if ($phone_country == '82' && !Rhymix\Framework\Korea::isValidPhoneNumber($phone_number))
{
return new BaseObject(-1, 'msg_invalid_phone_number');
}
$code = intval(mt_rand(100000, 999999));
$_SESSION['verify_by_sms'] = array(
'country' => $phone_country,
'number' => $phone_number,
'code' => $code,
'status' => false,
);
$sms = new Rhymix\Framework\SMS;
$sms->addTo($phone_number, $phone_country);
$content = '[' . Context::get('site_module_info')->settings->title . '] ' . sprintf(lang('member.verify_by_sms_message'), $code);
$sms->setContent($content);
$result = $sms->send();
if ($result && config('sms.type') !== 'dummy')
{
return new BaseObject(0, 'verify_by_sms_code_sent');
}
else
{
return new BaseObject(0, 'verify_by_sms_error');
}
}
function procMemberConfirmVerificationSMS()
{
$config = getModel('member')->getMemberConfig();
if ($config->phone_number_verify_by_sms !== 'Y')
{
throw new Rhymix\Framework\Exceptions\FeatureDisabled;
}
$code = Context::get('code');
if(!preg_match('/^[0-9]{6}$/', $code))
{
throw new Rhymix\Framework\Exception('verify_by_sms_code_incorrect');
}
$code = intval($code);
if(!isset($_SESSION['verify_by_sms']) || $_SESSION['verify_by_sms']['code'] !== $code)
{
throw new Rhymix\Framework\Exception('verify_by_sms_code_incorrect');
}
$_SESSION['verify_by_sms']['status'] = true;
return new BaseObject(0, 'verify_by_sms_code_confirmed');
}
/**
* trigger for document.getDocumentMenu. Append to popup menu a button for procMemberSpammerManage()
*

View file

@ -50,3 +50,49 @@ function completeMemberCheckValue(ret_obj, response_tags, field) {
function removeMemberCheckValueOutput(dummy, obj) {
dummy.style.display = "none";
}
// 문자 인증 처리
(function($) {
$(function() {
$('input.phone_number').on('keydown change', function() {
$(this).siblings('button.verifySMS').show();
});
$('button.verifySMS').on('click', function(event) {
event.preventDefault();
$(this).attr('disabled', 'disabled');
var phone_country = $(this).siblings('.phone_country').val();
var phone_number = $(this).siblings('.phone_number').val();
var that = $(this);
exec_json('member.procMemberSendVerificationSMS', { phone_country: phone_country, phone_number: phone_number }, function(data) {
alert(data.message);
if (!data.error) {
var input_area = that.siblings('.verifySMS_input_area');
input_area.show().find('input').focus();
that.hide();
} else {
that.removeAttr('disabled');
}
}, function() {
that.removeAttr('disabled');
});
});
$('button.verifySMS_input_button').on('click', function(event) {
event.preventDefault();
$(this).attr('disabled', 'disabled');
var code = $(this).siblings('.verifySMS_input_number').val();
var that = $(this);
exec_json('member.procMemberConfirmVerificationSMS', { code: code }, function(data) {
alert(data.message);
if (!data.error) {
var input_area = that.parents('.verifySMS_input_area');
input_area.hide();
input_area.siblings('.phone_number,.phone_country').attr('readonly', 'readonly');
} else {
that.removeAttr('disabled');
}
}, function() {
that.removeAttr('disabled');
});
});
});
})(jQuery);

View file

@ -158,6 +158,11 @@
<label class="x_inline"><input type="radio" name="phone_number_allow_duplicate" value="Y" checked="checked"|cond="$config->phone_number_allow_duplicate === 'Y'" /> {$lang->cmd_yes}</label>
<label class="x_inline"><input type="radio" name="phone_number_allow_duplicate" value="N" checked="checked"|cond="$config->phone_number_allow_duplicate !== 'Y'" /> {$lang->cmd_no}</label>
</p>
<p class="x_help-block">
<label class="x_inline">{$lang->phone_number_verify_by_sms}</label>
<label class="x_inline"><input type="radio" name="phone_number_verify_by_sms" value="Y" checked="checked"|cond="$config->phone_number_verify_by_sms === 'Y'" /> {$lang->cmd_yes}</label>
<label class="x_inline"><input type="radio" name="phone_number_verify_by_sms" value="N" checked="checked"|cond="$config->phone_number_verify_by_sms !== 'Y'" /> {$lang->cmd_no}</label>
</p>
</div>
<div cond="$item->name == 'signature'" class="_subItem" style="display:none;padding-top:5px"|cond="!$item->isUse">