Warning (2) : include_once(analyticstracking.php): failed to open stream: No such file or directory [APP/View/Layouts/login.ctp , line 25 ]Code Context < html xmlns = "http://www.w3.org/1999/xhtml" >
< head >
<?php include_once( "analyticstracking.php" ) ?> $viewFile = '/home/softlife2/softlife2.com/public_html/test/app/View/Layouts/login.ctp'
$dataForView = array(
'title_for_layout' => 'ログイン - 派遣管理システム',
'page_name' => '',
'controller_id' => (int) 0,
'controller' => 'users',
'display_inquiry' => '',
'list_auth' => null,
'list_menu' => array(
(int) 1 => 'ホーム',
(int) 2 => 'メール',
(int) 3 => 'スタッフ管理',
(int) 4 => '案件管理',
(int) 5 => 'シフト管理',
(int) 6 => '勤怠管理',
(int) 7 => '売上給与',
(int) 8 => '現場',
(int) 9 => 'クライアント本社',
(int) 21 => '管理者ページ(履歴)',
(int) 30 => '全体',
(int) 31 => 'その他'
),
'notice1' => null,
'list_class' => array(
(int) 11 => '大阪-人材派遣',
(int) 12 => '大阪-住宅営業',
(int) 21 => '東京-人材派遣',
(int) 22 => '東京-住宅営業',
(int) 23 => '東京-IT営業',
(int) 31 => '名古屋-人材派遣',
(int) 32 => '名古屋-住宅営業',
(int) 41 => '福岡-人材派遣',
(int) 42 => '福岡-住宅営業',
(int) 51 => '札幌-人材派遣',
(int) 52 => '札幌-住宅営業',
(int) 61 => '仙台-人材派遣',
(int) 62 => '仙台-住宅営業',
(int) 71 => '〇〇-人材派遣',
(int) 72 => '〇〇-住宅営業',
(int) 81 => '〇〇-人材派遣',
(int) 82 => '〇〇-住宅営業',
(int) 91 => '〇〇-人材派遣',
(int) 92 => '〇〇-住宅営業',
(int) 991 => '管理者-人材派遣',
(int) 992 => '管理者-住宅営業'
),
'role' => null,
'result' => array(),
'username' => null,
'corp_name' => '株式会社ソフトライフ',
'selected_class' => null,
'auth' => object(AuthComponent) {
components => array(
[maximum depth reached]
)
authenticate => array(
[maximum depth reached]
)
authorize => false
ajaxLogin => null
flash => array(
[maximum depth reached]
)
loginAction => array(
[maximum depth reached]
)
loginRedirect => array(
[maximum depth reached]
)
logoutRedirect => array(
[maximum depth reached]
)
authError => 'あなたのIDとパスワードを入力して下さい。'
unauthorizedRedirect => true
allowedActions => array(
[maximum depth reached]
)
request => object(CakeRequest) {}
response => object(CakeResponse) {}
settings => array(
[maximum depth reached]
)
[protected] _authenticateObjects => array(
[maximum depth reached]
)
[protected] _authorizeObjects => array([maximum depth reached])
[protected] _user => array([maximum depth reached])
[protected] _methods => array(
[maximum depth reached]
)
[protected] _Collection => object(ComponentCollection) {}
[protected] _componentMap => array(
[maximum depth reached]
)
},
'content_for_layout' => '<link rel="stylesheet" type="text/css" href="/css/main.css"/><script>
$(function() {
$('.toggle-pass').on('click touchend', function() {
var input = $('#UserPassword');
if (input.attr('type') == 'text') {
input.attr('type','password');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/><';
} else {
input.attr('type','text');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"/>';
}
$(this).html(html);
});
sessionStorage.setItem('disp_popup', '');
});
</script>
<style>
.input-wrap{
position: relative;
}
.toggle-pass{
position:absolute;
top:50%;
right: 0px;
transform: translateY(-50%);
cursor:pointer;
}
#content {
padding-bottom: 10px;
}
input {
padding:3px 3px;
}
.btn {
transition-property: all;
transition-duration: 0.1s;
transition-timing-function: ease-in-out;
position: relative;
top: 0px;
background-color: #ffcc33;
color:black;
border: 1px solid gray;
font-size: 115%;
border-radius:10px;
padding:3px 10px;
margin-left:10px;
cursor:pointer;
-webkit-filter: drop-shadow(1px 1px 1px #ccc);
filter: drop-shadow(1px 1px 1 #ccc);
}
.btn:hover {
/*background-color: #ffcc33;*/
background-color: #ffffcc;
}
.btn:active {
top: 5px;
}
.btn:active {
-webkit-filter: drop-shadow(0 0 0 #ccc);
filter: drop-shadow(0 0 0 #ccc);
}
</style>
<!-- content start -->
<br />
<div style="margin-top: 50px;margin-bottom: 50px;">
<center>
<form action="/" id="UserLoginForm" method="post" accept-charset="utf-8"><div style="display:none;"><input type="hidden" name="_method" value="POST"/></div> <table cellspacing="0" cellpadding="5" border="1" align="center" id="staff_master">
<tr>
<td>
<table cellspacing="2" cellpadding="5" border="0" align="center" style="background-color: #ccffcc;">
<tr><td colspan="3"> </td></tr>
<tr>
<td style="width:5em;"> </td>
<td style="text-align: center;width:15em;"><b>ソフトライフ</b></td>
<td style="width:5em;"> </td>
</tr>
<tr>
<td> </td>
<td>
アカウントID<br>
<!-- 名前コンボの値セット START -->
<div class="input test required"><input name="data[User][account]" style="width: 100%;font-size:100%;" type="test" id="UserAccount" required="required"/></div> <input type="hidden" name="data[User][username]" style="width: 100%;font-size:100%;" id="UserUsername"/> <!-- 名前コンボの値セット END -->
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td>
パスワード<br>
<div class="input-wrap">
<input name="data[User][password]" style="width: 100%;" type="password" id="UserPassword" required="required"/> <!-- <i class="toggle-pass fas fa-eye-slash"></i>-->
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 640 512" class="toggle-pass">
<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/>
</svg>
</div>
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td style="text-align: center;padding:10px 15px;">
<div style="float:left;padding-top:3px;">
<input name="login" class="load" type="submit" value="ログイン"/></form> </div>
<div style="float:right;padding-top:3px;">
<button type="button" id="login3" class="btn" title="パスキーでログイン">
<table>
<tr>
<td>
<img src="/img/fido2+.png" style="width:25px;" />
</td>
<td>
パスキー
</td>
</tr>
</table>
</button>
<!-- <img src="/img/fido2.png" id="login3" class="btn" title="多要素認証でログイン" style="border-radius:10px;cursor:pointer;" />-->
</div>
<div style="clear:both;"></div>
<input type="hidden" name="recaptcha_response" id="recaptchaResponse" />
<td> </td>
</tr>
<tr>
<td> </td>
<td align="left" style="padding-bottom:20px;">
<span style="font-size: 90%;">
※推奨:<a href="https://www.google.co.jp/chrome/browser/desktop/index.html" title="最も推奨" id="browser" target="_blank"><b>Chrome</b></a>, Edge, Firefox<br>
<font title="一部で不具合を確認しています。">Internet Explorerでは使用不可</font>
</span>
</td>
<td> </td>
</tr>
<tr><td nowrap> </td></tr>
</table>
</td>
</tr>
</table>
<div style="overflow-wrap: break-word;text-align:center;margin-top:30px;width:500px;">
●アカウントIDまたはパスワードをお忘れの方は<br><a href="/users/forget_pwd">[パスワードのリセット]</a>
</div>
</form> </center>
<!-- content end -->
</div>
<script>
document.getElementById('login3').onclick = async () => {
const account = $('#UserAccount').val();
const password = $('#UserPassword').val();
// const res = await fetch('/users/loginBegin');
const res = await fetch('/users/loginBegin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
account: account,
password: password
})
});
const json = await res.json();
if (json.status == 'error') {
alert(json.message);
return; // ダイアログを出さず終了
} else if (json.status == 'error2') {
$('#UserUsername').val(json.userId);
dialog_passkey();
return; // ダイアログを出さず終了
}
const options = json.options;
// options.challenge = new Uint8Array(options.challenge);
options.challenge = base64urlToBuffer(options.challenge);
// ★ allowCredentials.id を ArrayBuffer に変換する
if (options.allowCredentials) {
options.allowCredentials = options.allowCredentials.map(cred => {
cred.id = base64urlToBuffer(cred.id);
return cred;
});
}
//console.log("LOGIN user.id (before encode):", options);
//options.user.id = new TextEncoder().encode(options.user.id);
//console.log("LOGIN user.id (after encode):", options.user.id);
const assertion = await navigator.credentials.get({ publicKey: options });
const result = await fetch('/users/loginFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: assertion.id,
rawId: arrayBufferToBase64(assertion.rawId),
type: assertion.type,
response: {
clientDataJSON: arrayBufferToBase64(assertion.response.clientDataJSON),
authenticatorData: arrayBufferToBase64(assertion.response.authenticatorData),
signature: arrayBufferToBase64(assertion.response.signature),
// userHandle: arrayBufferToBase64(assertion.response.userHandle)
userHandle: arrayBufferToBase64(assertion.response.userHandle)
}
})
});
const json2 = await result.json();
// alert(json2.status === 'ok' ? 'ログイン成功!' : 'ログイン失敗 ('+json2.message+')');
if (json2.status === 'ok') {
// alert('ログイン成功!');
displayLogin();
$('#UserLoginForm').submit();
} else {
alert('ログイン失敗 ('+json2.message+')');
}
};
function dialog_passkey() {
// ダイアログ
const body = document.querySelector('body');
const modal = document.querySelector(".modal");
modal.showModal();
//モーダルダイアログを閉じる
const closeModal = document.querySelector(".close-modal");
closeModal.addEventListener("click", () => {
closePopup();
modal.close();
});
$(".open-passkey").on("click", function(){
closePopup();
openWindow6();
modal.close();
});
$(".open-passkey-manual").on("click", function(){
openWindow7();
// closePopup();
// modal.close();
});
$(".close-modal2").on("click", function(){
closePopup();
modal.close();
});
function closePopup() {
body.classList.remove('open_popup');
}
}
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
</script>
<!-- モーダルダイアログ -->
<style>
/* ダイアログを開くボタン */
.open-button {
margin: 10px;
width: 200px;
height: 50px;
font-size: 20px;
font-weight: bold;
border: none;
border-radius: 10px;
background-color: #fcc891;
cursor: pointer;
}
.open-button:hover {
color: #ffffff;
background-color: #ed7e07;
box-shadow: 0 5px 5px #cccccc;
}
/* ダイアログ */
.modeless,
.modal {
width: 70%;
max-width: 550px;
padding: 0;
border: none;
box-shadow: #595959 2px 2px 5px 2px;
}
/* モーダルダイアログの背後 */
.modal::backdrop {
background-color: #12121290;
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); /* Safari */
}
/* ダイアログヘッダー */
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 10px 0 10px;
margin-bottom: 0px;
color: #ffffff;
background-color: #085BB5;
}
.dialog-header p {
font-size: 16px;
vertical-align:middle;
padding: 10px 0px;
margin-bottom: 0px;
}
/* ダイアログを閉じるボタン */
.close-button {
width: 50px;
height: 30px;
font-size: 13px;
font-weight: bold;
margin-left: auto;
background-color: #e6e6e6;
cursor: pointer;
border: none;
border-radius: 10px;
}
.close-button:hover {
color: #ffffff;
font-weight: bold;
background-color: #ed7e07;
}
.title {
font-size: 30pxe;
margin: 10px 20px;
color: navy;
}
.message2 {
font-size: 16px;
margin: 10px 20px 10px;
/*background-color: #ffffee;*/
color: black;
}
.bottom-button {
padding: 10px 20px 30px;
text-align: left;
}
.open-passkey-manual {
margin-left:30px;background-color:blue;color:white;border-radius:5px;
}
</style>
<dialog class="modal">
<!-- ダイアログヘッダー -->
<div class="dialog-header">
<p>お知らせ</p>
<button type="button" class="close-modal close-button">✕</button>
</div>
<!-- ダイアログコンテンツ -->
<div style="float:left;padding:5px 5px;">
<img src="/img/passkie.png" />
<!-- <div style="text-align:center;color:brown;">ぱすきぃ</div>-->
</div>
<div style="float:left;">
<h2 class="title">パスキー登録のお願い</h2>
<div class="message2">セキュリティー向上のために<br>パスキーの登録をお願いいたします。</div>
<div class="bottom-button">
<button type="button" class="register_passkey" style="background-color:orange;color:black;padding:5px 10px;border-radius:5px;font-size:105%;"><i class="fas fa-key"></i> パスキー登録</button>
<button type="button" class="close-modal2" style="margin-left:20px;">スキップ</button>
<button type="button" class="open-passkey-manual" title="パスキー設定マニュアルを表示します">利用手順</button>
</div>
</div>
<div style="clear:both;"></div>
</dialog>
<script>
$(function(){
$('.register_passkey').on('click', async function() {
try {
const userId = $('#UserUsername').val();
if ((userId == "") || (userId == 0)) {
alert("登録に失敗しました。");
return false;
}
console.log(userId);
const res = await fetch('/webauthn/registerBegin/'+userId);
const options = await res.json();
// options.challenge = Uint8Array.from(atob(options.challenge.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
// options.user.id = Uint8Array.from(options.user.id, c => c.charCodeAt(0));
options.challenge = base64urlToBuffer(options.challenge);
options.user.id = base64urlToBuffer(options.user.id); // ★ 修正ポイント
// --- excludeCredentials の id も ArrayBuffer に変換 ---
if (options.excludeCredentials) {
options.excludeCredentials = options.excludeCredentials.map(c => ({
...c,
id: base64urlToBuffer(c.id) // ← これをしないと同じデバイス再登録が防げない
}));
}
console.log("PublicKeyCredentialCreationOptions:", options);
const credential = await navigator.credentials.create({ publicKey: options });
console.log("credential:");
console.log(credential);
const result = await fetch('/webauthn/registerFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential)
});
const json = await result.json();
// alert(json.status === 'ok' ? '登録完了!' : '登録失敗 ('+json.message+')');
if (json.status == 0) {
alert('登録失敗 ('+json.message+')');
} else {
// 入力ダイアログを表示 + 入力内容を user に代入
var ua = window.navigator.userAgent.toLowerCase();
var device = getDevice(ua);
let remarks = window.prompt("【登録名】認証端末などを入力してください(会社スマホ、PCなど)", device);
const id = json.status;
if (remarks !== "") {
new Promise((resolve, reject) => {
$.ajax({
url: '/users/ajax_save_passkey_remarks',
type:'POST',
dataType: 'html',
data : {save : id, value : remarks },
async: false,
timeout:3000,
}).done(function(data) {
if (data === "error") {
alert('登録に失敗しました (1)');
}
// }).fail(function(XMLHttpRequest, textStatus, errorThrown) {
// alert('登録に失敗しました (2)');
})
resolve();
}).then(() => {
alert('登録完了!');
location.reload();
});
} else {
alert('登録完了!');
location.reload();
}
}
} catch (err) {
console.log("WebAuthn registration error:");
// わかりやすいメッセージに変換
let message = "登録に失敗しました。";
if (err instanceof DOMException && err.name === "InvalidStateError") {
message = "このデバイスはすでに登録されています。";
} else if (err instanceof DOMException && err.name === "NotAllowedError") {
message = "登録操作がキャンセルされました。";
}
alert(message);
}
});
});
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
function base64urlToUint8Array(base64url) {
const padding = '='.repeat((4 - base64url.length % 4) % 4);
const base64 = (base64url + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function getDevice(ua) {
let ret = '';
if(ua.indexOf("windows") !== -1) {
ret = "Windows";
} else if(ua.indexOf("android") !== -1) {
ret = "Android";
} else if(ua.indexOf("iphone") !== -1) {
ret = "iPhone";
} else if(ua.indexOf("ipad") !== -1) {
ret = "iPad";
} else if(ua.indexOf("mac os x") !== -1) {
ret = "Appleデバイス";
} else {
ret = "その他";
}
return ret;
}
</script>
<style>
.modeless,
.modal {
top: -50%;
}
</style>
',
'scripts_for_layout' => ''
)
$title_for_layout = 'ログイン - 派遣管理システム'
$page_name = ''
$controller_id = (int) 0
$controller = 'users'
$display_inquiry = ''
$list_auth = null
$list_menu = array(
(int) 1 => 'ホーム',
(int) 2 => 'メール',
(int) 3 => 'スタッフ管理',
(int) 4 => '案件管理',
(int) 5 => 'シフト管理',
(int) 6 => '勤怠管理',
(int) 7 => '売上給与',
(int) 8 => '現場',
(int) 9 => 'クライアント本社',
(int) 21 => '管理者ページ(履歴)',
(int) 30 => '全体',
(int) 31 => 'その他'
)
$notice1 = null
$list_class = array(
(int) 11 => '大阪-人材派遣',
(int) 12 => '大阪-住宅営業',
(int) 21 => '東京-人材派遣',
(int) 22 => '東京-住宅営業',
(int) 23 => '東京-IT営業',
(int) 31 => '名古屋-人材派遣',
(int) 32 => '名古屋-住宅営業',
(int) 41 => '福岡-人材派遣',
(int) 42 => '福岡-住宅営業',
(int) 51 => '札幌-人材派遣',
(int) 52 => '札幌-住宅営業',
(int) 61 => '仙台-人材派遣',
(int) 62 => '仙台-住宅営業',
(int) 71 => '〇〇-人材派遣',
(int) 72 => '〇〇-住宅営業',
(int) 81 => '〇〇-人材派遣',
(int) 82 => '〇〇-住宅営業',
(int) 91 => '〇〇-人材派遣',
(int) 92 => '〇〇-住宅営業',
(int) 991 => '管理者-人材派遣',
(int) 992 => '管理者-住宅営業'
)
$role = null
$result = array()
$username = null
$corp_name = '株式会社ソフトライフ'
$selected_class = null
$auth = object(AuthComponent) {
components => array(
(int) 0 => 'Session',
(int) 1 => 'Flash',
(int) 2 => 'RequestHandler'
)
authenticate => array(
'Form' => array(
[maximum depth reached]
)
)
authorize => false
ajaxLogin => null
flash => array(
'element' => 'default',
'key' => 'auth',
'params' => array([maximum depth reached])
)
loginAction => array(
'controller' => 'users',
'action' => 'login'
)
loginRedirect => array(
'controller' => 'users',
'action' => 'index'
)
logoutRedirect => array(
'controller' => 'users',
'action' => 'login'
)
authError => 'あなたのIDとパスワードを入力して下さい。'
unauthorizedRedirect => true
allowedActions => array(
(int) 0 => 'login',
(int) 1 => 'login2',
(int) 2 => 'loginBegin',
(int) 3 => 'loginFinish',
(int) 4 => 'logout',
(int) 5 => 'restriction',
(int) 6 => 'line',
(int) 7 => 'station',
(int) 8 => 'station2',
(int) 9 => 'login_test1',
(int) 10 => 'forget_pwd',
(int) 11 => 'reset_pwd',
(int) 12 => 'access_err'
)
request => object(CakeRequest) {}
response => object(CakeResponse) {}
settings => array(
'loginRedirect' => array(
[maximum depth reached]
),
'logoutRedirect' => array(
[maximum depth reached]
),
'authenticate' => array(
[maximum depth reached]
),
'loginAction' => array(
[maximum depth reached]
),
'authError' => 'あなたのIDとパスワードを入力して下さい。',
'allowedActions' => array(
[maximum depth reached]
)
)
[protected] _authenticateObjects => array(
(int) 0 => object(FormAuthenticate) {}
)
[protected] _authorizeObjects => array()
[protected] _user => array()
[protected] _methods => array(
(int) 0 => 'batch',
(int) 1 => 'index',
(int) 2 => 'edit_notice',
(int) 3 => 'manual_vpn',
(int) 4 => 'manual_passkey',
(int) 5 => 'window',
(int) 6 => 'window2',
(int) 7 => 'dialog1',
(int) 8 => 'issue_list',
(int) 9 => 'check_log',
(int) 10 => 'getArea',
(int) 11 => 'inquiry',
(int) 12 => 'inquiry_edit',
(int) 13 => 'ajax_upload_file',
(int) 14 => 'ajax_check_class',
(int) 15 => 'ajax_write_log',
(int) 16 => 'ajax_save_passkey_remarks',
(int) 17 => 'inquiry_edit2',
(int) 18 => 'list_file',
(int) 19 => 'detail',
(int) 20 => 'infra_input',
(int) 21 => 'forget_pwd',
(int) 22 => 'reset_pwd',
(int) 23 => 'restriction',
(int) 24 => 'add',
(int) 25 => 'add2',
(int) 26 => 'edit',
(int) 27 => 'edit2',
(int) 28 => 'view',
(int) 29 => 'passwd',
(int) 30 => 'passwd2',
(int) 31 => 'list_page',
(int) 32 => 'page_register',
(int) 33 => 'login',
(int) 34 => 'set_dir',
(int) 35 => 'send_mail',
(int) 36 => 'login2',
(int) 37 => 'getRpId',
(int) 38 => 'login3',
(int) 39 => 'loginBegin',
(int) 40 => 'loginFinish',
(int) 41 => 'passkey',
(int) 42 => 'access_err',
(int) 43 => 'auth_err',
(int) 44 => 'logout',
(int) 45 => 'logout2',
(int) 46 => 'clearCache',
(int) 47 => 'login_test1',
(int) 48 => 'getPasswordHash',
(int) 49 => 'timesheet',
(int) 50 => 'line',
(int) 51 => 'station',
(int) 52 => 'station2',
(int) 53 => 'list_line',
(int) 54 => 'list_station',
(int) 55 => 'setAuth',
(int) 56 => 'getValue',
(int) 57 => 'getOS',
(int) 58 => 'getBrowser',
(int) 59 => 'chkDirectory',
(int) 60 => 'ajax_get_session',
(int) 61 => 'ajax_get_cookie',
(int) 62 => 'send_err_info',
(int) 64 => 'isAuthorized',
(int) 65 => 'list_account',
(int) 66 => 'list_class',
(int) 67 => 'file_info',
(int) 68 => 'getAreaName',
(int) 69 => 'getCaseName',
(int) 70 => 'getListCaseName',
(int) 71 => 'getClientName',
(int) 72 => 'getFlagSO',
(int) 73 => 'writeSession',
(int) 74 => 'readSession',
(int) 75 => 'deleteSession',
(int) 76 => 'checkSession',
(int) 77 => 'writeOutputLog',
(int) 78 => 'getLastOutputLog',
(int) 79 => 'getLastOutputLog2',
(int) 80 => 'getLastOutputLog3',
(int) 81 => 'corp_name',
(int) 82 => 'setDataChangeLog',
(int) 83 => 'japan_holiday',
(int) 84 => 'send_notice'
)
[protected] _Collection => object(ComponentCollection) {}
[protected] _componentMap => array(
'Session' => array(
[maximum depth reached]
),
'Flash' => array(
[maximum depth reached]
),
'RequestHandler' => array(
[maximum depth reached]
)
)
}
$content_for_layout = '<link rel="stylesheet" type="text/css" href="/css/main.css"/><script>
$(function() {
$('.toggle-pass').on('click touchend', function() {
var input = $('#UserPassword');
if (input.attr('type') == 'text') {
input.attr('type','password');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/><';
} else {
input.attr('type','text');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"/>';
}
$(this).html(html);
});
sessionStorage.setItem('disp_popup', '');
});
</script>
<style>
.input-wrap{
position: relative;
}
.toggle-pass{
position:absolute;
top:50%;
right: 0px;
transform: translateY(-50%);
cursor:pointer;
}
#content {
padding-bottom: 10px;
}
input {
padding:3px 3px;
}
.btn {
transition-property: all;
transition-duration: 0.1s;
transition-timing-function: ease-in-out;
position: relative;
top: 0px;
background-color: #ffcc33;
color:black;
border: 1px solid gray;
font-size: 115%;
border-radius:10px;
padding:3px 10px;
margin-left:10px;
cursor:pointer;
-webkit-filter: drop-shadow(1px 1px 1px #ccc);
filter: drop-shadow(1px 1px 1 #ccc);
}
.btn:hover {
/*background-color: #ffcc33;*/
background-color: #ffffcc;
}
.btn:active {
top: 5px;
}
.btn:active {
-webkit-filter: drop-shadow(0 0 0 #ccc);
filter: drop-shadow(0 0 0 #ccc);
}
</style>
<!-- content start -->
<br />
<div style="margin-top: 50px;margin-bottom: 50px;">
<center>
<form action="/" id="UserLoginForm" method="post" accept-charset="utf-8"><div style="display:none;"><input type="hidden" name="_method" value="POST"/></div> <table cellspacing="0" cellpadding="5" border="1" align="center" id="staff_master">
<tr>
<td>
<table cellspacing="2" cellpadding="5" border="0" align="center" style="background-color: #ccffcc;">
<tr><td colspan="3"> </td></tr>
<tr>
<td style="width:5em;"> </td>
<td style="text-align: center;width:15em;"><b>ソフトライフ</b></td>
<td style="width:5em;"> </td>
</tr>
<tr>
<td> </td>
<td>
アカウントID<br>
<!-- 名前コンボの値セット START -->
<div class="input test required"><input name="data[User][account]" style="width: 100%;font-size:100%;" type="test" id="UserAccount" required="required"/></div> <input type="hidden" name="data[User][username]" style="width: 100%;font-size:100%;" id="UserUsername"/> <!-- 名前コンボの値セット END -->
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td>
パスワード<br>
<div class="input-wrap">
<input name="data[User][password]" style="width: 100%;" type="password" id="UserPassword" required="required"/> <!-- <i class="toggle-pass fas fa-eye-slash"></i>-->
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 640 512" class="toggle-pass">
<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/>
</svg>
</div>
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td style="text-align: center;padding:10px 15px;">
<div style="float:left;padding-top:3px;">
<input name="login" class="load" type="submit" value="ログイン"/></form> </div>
<div style="float:right;padding-top:3px;">
<button type="button" id="login3" class="btn" title="パスキーでログイン">
<table>
<tr>
<td>
<img src="/img/fido2+.png" style="width:25px;" />
</td>
<td>
パスキー
</td>
</tr>
</table>
</button>
<!-- <img src="/img/fido2.png" id="login3" class="btn" title="多要素認証でログイン" style="border-radius:10px;cursor:pointer;" />-->
</div>
<div style="clear:both;"></div>
<input type="hidden" name="recaptcha_response" id="recaptchaResponse" />
<td> </td>
</tr>
<tr>
<td> </td>
<td align="left" style="padding-bottom:20px;">
<span style="font-size: 90%;">
※推奨:<a href="https://www.google.co.jp/chrome/browser/desktop/index.html" title="最も推奨" id="browser" target="_blank"><b>Chrome</b></a>, Edge, Firefox<br>
<font title="一部で不具合を確認しています。">Internet Explorerでは使用不可</font>
</span>
</td>
<td> </td>
</tr>
<tr><td nowrap> </td></tr>
</table>
</td>
</tr>
</table>
<div style="overflow-wrap: break-word;text-align:center;margin-top:30px;width:500px;">
●アカウントIDまたはパスワードをお忘れの方は<br><a href="/users/forget_pwd">[パスワードのリセット]</a>
</div>
</form> </center>
<!-- content end -->
</div>
<script>
document.getElementById('login3').onclick = async () => {
const account = $('#UserAccount').val();
const password = $('#UserPassword').val();
// const res = await fetch('/users/loginBegin');
const res = await fetch('/users/loginBegin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
account: account,
password: password
})
});
const json = await res.json();
if (json.status == 'error') {
alert(json.message);
return; // ダイアログを出さず終了
} else if (json.status == 'error2') {
$('#UserUsername').val(json.userId);
dialog_passkey();
return; // ダイアログを出さず終了
}
const options = json.options;
// options.challenge = new Uint8Array(options.challenge);
options.challenge = base64urlToBuffer(options.challenge);
// ★ allowCredentials.id を ArrayBuffer に変換する
if (options.allowCredentials) {
options.allowCredentials = options.allowCredentials.map(cred => {
cred.id = base64urlToBuffer(cred.id);
return cred;
});
}
//console.log("LOGIN user.id (before encode):", options);
//options.user.id = new TextEncoder().encode(options.user.id);
//console.log("LOGIN user.id (after encode):", options.user.id);
const assertion = await navigator.credentials.get({ publicKey: options });
const result = await fetch('/users/loginFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: assertion.id,
rawId: arrayBufferToBase64(assertion.rawId),
type: assertion.type,
response: {
clientDataJSON: arrayBufferToBase64(assertion.response.clientDataJSON),
authenticatorData: arrayBufferToBase64(assertion.response.authenticatorData),
signature: arrayBufferToBase64(assertion.response.signature),
// userHandle: arrayBufferToBase64(assertion.response.userHandle)
userHandle: arrayBufferToBase64(assertion.response.userHandle)
}
})
});
const json2 = await result.json();
// alert(json2.status === 'ok' ? 'ログイン成功!' : 'ログイン失敗 ('+json2.message+')');
if (json2.status === 'ok') {
// alert('ログイン成功!');
displayLogin();
$('#UserLoginForm').submit();
} else {
alert('ログイン失敗 ('+json2.message+')');
}
};
function dialog_passkey() {
// ダイアログ
const body = document.querySelector('body');
const modal = document.querySelector(".modal");
modal.showModal();
//モーダルダイアログを閉じる
const closeModal = document.querySelector(".close-modal");
closeModal.addEventListener("click", () => {
closePopup();
modal.close();
});
$(".open-passkey").on("click", function(){
closePopup();
openWindow6();
modal.close();
});
$(".open-passkey-manual").on("click", function(){
openWindow7();
// closePopup();
// modal.close();
});
$(".close-modal2").on("click", function(){
closePopup();
modal.close();
});
function closePopup() {
body.classList.remove('open_popup');
}
}
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
</script>
<!-- モーダルダイアログ -->
<style>
/* ダイアログを開くボタン */
.open-button {
margin: 10px;
width: 200px;
height: 50px;
font-size: 20px;
font-weight: bold;
border: none;
border-radius: 10px;
background-color: #fcc891;
cursor: pointer;
}
.open-button:hover {
color: #ffffff;
background-color: #ed7e07;
box-shadow: 0 5px 5px #cccccc;
}
/* ダイアログ */
.modeless,
.modal {
width: 70%;
max-width: 550px;
padding: 0;
border: none;
box-shadow: #595959 2px 2px 5px 2px;
}
/* モーダルダイアログの背後 */
.modal::backdrop {
background-color: #12121290;
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); /* Safari */
}
/* ダイアログヘッダー */
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 10px 0 10px;
margin-bottom: 0px;
color: #ffffff;
background-color: #085BB5;
}
.dialog-header p {
font-size: 16px;
vertical-align:middle;
padding: 10px 0px;
margin-bottom: 0px;
}
/* ダイアログを閉じるボタン */
.close-button {
width: 50px;
height: 30px;
font-size: 13px;
font-weight: bold;
margin-left: auto;
background-color: #e6e6e6;
cursor: pointer;
border: none;
border-radius: 10px;
}
.close-button:hover {
color: #ffffff;
font-weight: bold;
background-color: #ed7e07;
}
.title {
font-size: 30pxe;
margin: 10px 20px;
color: navy;
}
.message2 {
font-size: 16px;
margin: 10px 20px 10px;
/*background-color: #ffffee;*/
color: black;
}
.bottom-button {
padding: 10px 20px 30px;
text-align: left;
}
.open-passkey-manual {
margin-left:30px;background-color:blue;color:white;border-radius:5px;
}
</style>
<dialog class="modal">
<!-- ダイアログヘッダー -->
<div class="dialog-header">
<p>お知らせ</p>
<button type="button" class="close-modal close-button">✕</button>
</div>
<!-- ダイアログコンテンツ -->
<div style="float:left;padding:5px 5px;">
<img src="/img/passkie.png" />
<!-- <div style="text-align:center;color:brown;">ぱすきぃ</div>-->
</div>
<div style="float:left;">
<h2 class="title">パスキー登録のお願い</h2>
<div class="message2">セキュリティー向上のために<br>パスキーの登録をお願いいたします。</div>
<div class="bottom-button">
<button type="button" class="register_passkey" style="background-color:orange;color:black;padding:5px 10px;border-radius:5px;font-size:105%;"><i class="fas fa-key"></i> パスキー登録</button>
<button type="button" class="close-modal2" style="margin-left:20px;">スキップ</button>
<button type="button" class="open-passkey-manual" title="パスキー設定マニュアルを表示します">利用手順</button>
</div>
</div>
<div style="clear:both;"></div>
</dialog>
<script>
$(function(){
$('.register_passkey').on('click', async function() {
try {
const userId = $('#UserUsername').val();
if ((userId == "") || (userId == 0)) {
alert("登録に失敗しました。");
return false;
}
console.log(userId);
const res = await fetch('/webauthn/registerBegin/'+userId);
const options = await res.json();
// options.challenge = Uint8Array.from(atob(options.challenge.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
// options.user.id = Uint8Array.from(options.user.id, c => c.charCodeAt(0));
options.challenge = base64urlToBuffer(options.challenge);
options.user.id = base64urlToBuffer(options.user.id); // ★ 修正ポイント
// --- excludeCredentials の id も ArrayBuffer に変換 ---
if (options.excludeCredentials) {
options.excludeCredentials = options.excludeCredentials.map(c => ({
...c,
id: base64urlToBuffer(c.id) // ← これをしないと同じデバイス再登録が防げない
}));
}
console.log("PublicKeyCredentialCreationOptions:", options);
const credential = await navigator.credentials.create({ publicKey: options });
console.log("credential:");
console.log(credential);
const result = await fetch('/webauthn/registerFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential)
});
const json = await result.json();
// alert(json.status === 'ok' ? '登録完了!' : '登録失敗 ('+json.message+')');
if (json.status == 0) {
alert('登録失敗 ('+json.message+')');
} else {
// 入力ダイアログを表示 + 入力内容を user に代入
var ua = window.navigator.userAgent.toLowerCase();
var device = getDevice(ua);
let remarks = window.prompt("【登録名】認証端末などを入力してください(会社スマホ、PCなど)", device);
const id = json.status;
if (remarks !== "") {
new Promise((resolve, reject) => {
$.ajax({
url: '/users/ajax_save_passkey_remarks',
type:'POST',
dataType: 'html',
data : {save : id, value : remarks },
async: false,
timeout:3000,
}).done(function(data) {
if (data === "error") {
alert('登録に失敗しました (1)');
}
// }).fail(function(XMLHttpRequest, textStatus, errorThrown) {
// alert('登録に失敗しました (2)');
})
resolve();
}).then(() => {
alert('登録完了!');
location.reload();
});
} else {
alert('登録完了!');
location.reload();
}
}
} catch (err) {
console.log("WebAuthn registration error:");
// わかりやすいメッセージに変換
let message = "登録に失敗しました。";
if (err instanceof DOMException && err.name === "InvalidStateError") {
message = "このデバイスはすでに登録されています。";
} else if (err instanceof DOMException && err.name === "NotAllowedError") {
message = "登録操作がキャンセルされました。";
}
alert(message);
}
});
});
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
function base64urlToUint8Array(base64url) {
const padding = '='.repeat((4 - base64url.length % 4) % 4);
const base64 = (base64url + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function getDevice(ua) {
let ret = '';
if(ua.indexOf("windows") !== -1) {
ret = "Windows";
} else if(ua.indexOf("android") !== -1) {
ret = "Android";
} else if(ua.indexOf("iphone") !== -1) {
ret = "iPhone";
} else if(ua.indexOf("ipad") !== -1) {
ret = "iPad";
} else if(ua.indexOf("mac os x") !== -1) {
ret = "Appleデバイス";
} else {
ret = "その他";
}
return ret;
}
</script>
<style>
.modeless,
.modal {
top: -50%;
}
</style>
'
$scripts_for_layout = ''
$display = (int) 1
$style_body = ''
$style_content = '' include_once - APP/View/Layouts/login.ctp, line 25
include - APP/View/Layouts/login.ctp, line 25
View::_evaluate() - CORE/Cake/View/View.php, line 971
View::_render() - CORE/Cake/View/View.php, line 933
View::renderLayout() - CORE/Cake/View/View.php, line 546
View::render() - CORE/Cake/View/View.php, line 481
Controller::render() - CORE/Cake/Controller/Controller.php, line 968
Dispatcher::_invoke() - CORE/Cake/Routing/Dispatcher.php, line 200
Dispatcher::dispatch() - CORE/Cake/Routing/Dispatcher.php, line 167
require - APP/webroot/index.php, line 117
[main] - ROOT/index.php, line 41 Warning (2) : include_once() [<a href='http://php.net/function.include'>function.include</a>]: Failed opening 'analyticstracking.php' for inclusion (include_path='.:/opt/php-7.3.33-3/data/pear') [APP/View/Layouts/login.ctp , line 25 ]Code Context < html xmlns = "http://www.w3.org/1999/xhtml" >
< head >
<?php include_once( "analyticstracking.php" ) ?> $viewFile = '/home/softlife2/softlife2.com/public_html/test/app/View/Layouts/login.ctp'
$dataForView = array(
'title_for_layout' => 'ログイン - 派遣管理システム',
'page_name' => '',
'controller_id' => (int) 0,
'controller' => 'users',
'display_inquiry' => '',
'list_auth' => null,
'list_menu' => array(
(int) 1 => 'ホーム',
(int) 2 => 'メール',
(int) 3 => 'スタッフ管理',
(int) 4 => '案件管理',
(int) 5 => 'シフト管理',
(int) 6 => '勤怠管理',
(int) 7 => '売上給与',
(int) 8 => '現場',
(int) 9 => 'クライアント本社',
(int) 21 => '管理者ページ(履歴)',
(int) 30 => '全体',
(int) 31 => 'その他'
),
'notice1' => null,
'list_class' => array(
(int) 11 => '大阪-人材派遣',
(int) 12 => '大阪-住宅営業',
(int) 21 => '東京-人材派遣',
(int) 22 => '東京-住宅営業',
(int) 23 => '東京-IT営業',
(int) 31 => '名古屋-人材派遣',
(int) 32 => '名古屋-住宅営業',
(int) 41 => '福岡-人材派遣',
(int) 42 => '福岡-住宅営業',
(int) 51 => '札幌-人材派遣',
(int) 52 => '札幌-住宅営業',
(int) 61 => '仙台-人材派遣',
(int) 62 => '仙台-住宅営業',
(int) 71 => '〇〇-人材派遣',
(int) 72 => '〇〇-住宅営業',
(int) 81 => '〇〇-人材派遣',
(int) 82 => '〇〇-住宅営業',
(int) 91 => '〇〇-人材派遣',
(int) 92 => '〇〇-住宅営業',
(int) 991 => '管理者-人材派遣',
(int) 992 => '管理者-住宅営業'
),
'role' => null,
'result' => array(),
'username' => null,
'corp_name' => '株式会社ソフトライフ',
'selected_class' => null,
'auth' => object(AuthComponent) {
components => array(
[maximum depth reached]
)
authenticate => array(
[maximum depth reached]
)
authorize => false
ajaxLogin => null
flash => array(
[maximum depth reached]
)
loginAction => array(
[maximum depth reached]
)
loginRedirect => array(
[maximum depth reached]
)
logoutRedirect => array(
[maximum depth reached]
)
authError => 'あなたのIDとパスワードを入力して下さい。'
unauthorizedRedirect => true
allowedActions => array(
[maximum depth reached]
)
request => object(CakeRequest) {}
response => object(CakeResponse) {}
settings => array(
[maximum depth reached]
)
[protected] _authenticateObjects => array(
[maximum depth reached]
)
[protected] _authorizeObjects => array([maximum depth reached])
[protected] _user => array([maximum depth reached])
[protected] _methods => array(
[maximum depth reached]
)
[protected] _Collection => object(ComponentCollection) {}
[protected] _componentMap => array(
[maximum depth reached]
)
},
'content_for_layout' => '<link rel="stylesheet" type="text/css" href="/css/main.css"/><script>
$(function() {
$('.toggle-pass').on('click touchend', function() {
var input = $('#UserPassword');
if (input.attr('type') == 'text') {
input.attr('type','password');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/><';
} else {
input.attr('type','text');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"/>';
}
$(this).html(html);
});
sessionStorage.setItem('disp_popup', '');
});
</script>
<style>
.input-wrap{
position: relative;
}
.toggle-pass{
position:absolute;
top:50%;
right: 0px;
transform: translateY(-50%);
cursor:pointer;
}
#content {
padding-bottom: 10px;
}
input {
padding:3px 3px;
}
.btn {
transition-property: all;
transition-duration: 0.1s;
transition-timing-function: ease-in-out;
position: relative;
top: 0px;
background-color: #ffcc33;
color:black;
border: 1px solid gray;
font-size: 115%;
border-radius:10px;
padding:3px 10px;
margin-left:10px;
cursor:pointer;
-webkit-filter: drop-shadow(1px 1px 1px #ccc);
filter: drop-shadow(1px 1px 1 #ccc);
}
.btn:hover {
/*background-color: #ffcc33;*/
background-color: #ffffcc;
}
.btn:active {
top: 5px;
}
.btn:active {
-webkit-filter: drop-shadow(0 0 0 #ccc);
filter: drop-shadow(0 0 0 #ccc);
}
</style>
<!-- content start -->
<br />
<div style="margin-top: 50px;margin-bottom: 50px;">
<center>
<form action="/" id="UserLoginForm" method="post" accept-charset="utf-8"><div style="display:none;"><input type="hidden" name="_method" value="POST"/></div> <table cellspacing="0" cellpadding="5" border="1" align="center" id="staff_master">
<tr>
<td>
<table cellspacing="2" cellpadding="5" border="0" align="center" style="background-color: #ccffcc;">
<tr><td colspan="3"> </td></tr>
<tr>
<td style="width:5em;"> </td>
<td style="text-align: center;width:15em;"><b>ソフトライフ</b></td>
<td style="width:5em;"> </td>
</tr>
<tr>
<td> </td>
<td>
アカウントID<br>
<!-- 名前コンボの値セット START -->
<div class="input test required"><input name="data[User][account]" style="width: 100%;font-size:100%;" type="test" id="UserAccount" required="required"/></div> <input type="hidden" name="data[User][username]" style="width: 100%;font-size:100%;" id="UserUsername"/> <!-- 名前コンボの値セット END -->
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td>
パスワード<br>
<div class="input-wrap">
<input name="data[User][password]" style="width: 100%;" type="password" id="UserPassword" required="required"/> <!-- <i class="toggle-pass fas fa-eye-slash"></i>-->
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 640 512" class="toggle-pass">
<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/>
</svg>
</div>
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td style="text-align: center;padding:10px 15px;">
<div style="float:left;padding-top:3px;">
<input name="login" class="load" type="submit" value="ログイン"/></form> </div>
<div style="float:right;padding-top:3px;">
<button type="button" id="login3" class="btn" title="パスキーでログイン">
<table>
<tr>
<td>
<img src="/img/fido2+.png" style="width:25px;" />
</td>
<td>
パスキー
</td>
</tr>
</table>
</button>
<!-- <img src="/img/fido2.png" id="login3" class="btn" title="多要素認証でログイン" style="border-radius:10px;cursor:pointer;" />-->
</div>
<div style="clear:both;"></div>
<input type="hidden" name="recaptcha_response" id="recaptchaResponse" />
<td> </td>
</tr>
<tr>
<td> </td>
<td align="left" style="padding-bottom:20px;">
<span style="font-size: 90%;">
※推奨:<a href="https://www.google.co.jp/chrome/browser/desktop/index.html" title="最も推奨" id="browser" target="_blank"><b>Chrome</b></a>, Edge, Firefox<br>
<font title="一部で不具合を確認しています。">Internet Explorerでは使用不可</font>
</span>
</td>
<td> </td>
</tr>
<tr><td nowrap> </td></tr>
</table>
</td>
</tr>
</table>
<div style="overflow-wrap: break-word;text-align:center;margin-top:30px;width:500px;">
●アカウントIDまたはパスワードをお忘れの方は<br><a href="/users/forget_pwd">[パスワードのリセット]</a>
</div>
</form> </center>
<!-- content end -->
</div>
<script>
document.getElementById('login3').onclick = async () => {
const account = $('#UserAccount').val();
const password = $('#UserPassword').val();
// const res = await fetch('/users/loginBegin');
const res = await fetch('/users/loginBegin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
account: account,
password: password
})
});
const json = await res.json();
if (json.status == 'error') {
alert(json.message);
return; // ダイアログを出さず終了
} else if (json.status == 'error2') {
$('#UserUsername').val(json.userId);
dialog_passkey();
return; // ダイアログを出さず終了
}
const options = json.options;
// options.challenge = new Uint8Array(options.challenge);
options.challenge = base64urlToBuffer(options.challenge);
// ★ allowCredentials.id を ArrayBuffer に変換する
if (options.allowCredentials) {
options.allowCredentials = options.allowCredentials.map(cred => {
cred.id = base64urlToBuffer(cred.id);
return cred;
});
}
//console.log("LOGIN user.id (before encode):", options);
//options.user.id = new TextEncoder().encode(options.user.id);
//console.log("LOGIN user.id (after encode):", options.user.id);
const assertion = await navigator.credentials.get({ publicKey: options });
const result = await fetch('/users/loginFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: assertion.id,
rawId: arrayBufferToBase64(assertion.rawId),
type: assertion.type,
response: {
clientDataJSON: arrayBufferToBase64(assertion.response.clientDataJSON),
authenticatorData: arrayBufferToBase64(assertion.response.authenticatorData),
signature: arrayBufferToBase64(assertion.response.signature),
// userHandle: arrayBufferToBase64(assertion.response.userHandle)
userHandle: arrayBufferToBase64(assertion.response.userHandle)
}
})
});
const json2 = await result.json();
// alert(json2.status === 'ok' ? 'ログイン成功!' : 'ログイン失敗 ('+json2.message+')');
if (json2.status === 'ok') {
// alert('ログイン成功!');
displayLogin();
$('#UserLoginForm').submit();
} else {
alert('ログイン失敗 ('+json2.message+')');
}
};
function dialog_passkey() {
// ダイアログ
const body = document.querySelector('body');
const modal = document.querySelector(".modal");
modal.showModal();
//モーダルダイアログを閉じる
const closeModal = document.querySelector(".close-modal");
closeModal.addEventListener("click", () => {
closePopup();
modal.close();
});
$(".open-passkey").on("click", function(){
closePopup();
openWindow6();
modal.close();
});
$(".open-passkey-manual").on("click", function(){
openWindow7();
// closePopup();
// modal.close();
});
$(".close-modal2").on("click", function(){
closePopup();
modal.close();
});
function closePopup() {
body.classList.remove('open_popup');
}
}
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
</script>
<!-- モーダルダイアログ -->
<style>
/* ダイアログを開くボタン */
.open-button {
margin: 10px;
width: 200px;
height: 50px;
font-size: 20px;
font-weight: bold;
border: none;
border-radius: 10px;
background-color: #fcc891;
cursor: pointer;
}
.open-button:hover {
color: #ffffff;
background-color: #ed7e07;
box-shadow: 0 5px 5px #cccccc;
}
/* ダイアログ */
.modeless,
.modal {
width: 70%;
max-width: 550px;
padding: 0;
border: none;
box-shadow: #595959 2px 2px 5px 2px;
}
/* モーダルダイアログの背後 */
.modal::backdrop {
background-color: #12121290;
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); /* Safari */
}
/* ダイアログヘッダー */
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 10px 0 10px;
margin-bottom: 0px;
color: #ffffff;
background-color: #085BB5;
}
.dialog-header p {
font-size: 16px;
vertical-align:middle;
padding: 10px 0px;
margin-bottom: 0px;
}
/* ダイアログを閉じるボタン */
.close-button {
width: 50px;
height: 30px;
font-size: 13px;
font-weight: bold;
margin-left: auto;
background-color: #e6e6e6;
cursor: pointer;
border: none;
border-radius: 10px;
}
.close-button:hover {
color: #ffffff;
font-weight: bold;
background-color: #ed7e07;
}
.title {
font-size: 30pxe;
margin: 10px 20px;
color: navy;
}
.message2 {
font-size: 16px;
margin: 10px 20px 10px;
/*background-color: #ffffee;*/
color: black;
}
.bottom-button {
padding: 10px 20px 30px;
text-align: left;
}
.open-passkey-manual {
margin-left:30px;background-color:blue;color:white;border-radius:5px;
}
</style>
<dialog class="modal">
<!-- ダイアログヘッダー -->
<div class="dialog-header">
<p>お知らせ</p>
<button type="button" class="close-modal close-button">✕</button>
</div>
<!-- ダイアログコンテンツ -->
<div style="float:left;padding:5px 5px;">
<img src="/img/passkie.png" />
<!-- <div style="text-align:center;color:brown;">ぱすきぃ</div>-->
</div>
<div style="float:left;">
<h2 class="title">パスキー登録のお願い</h2>
<div class="message2">セキュリティー向上のために<br>パスキーの登録をお願いいたします。</div>
<div class="bottom-button">
<button type="button" class="register_passkey" style="background-color:orange;color:black;padding:5px 10px;border-radius:5px;font-size:105%;"><i class="fas fa-key"></i> パスキー登録</button>
<button type="button" class="close-modal2" style="margin-left:20px;">スキップ</button>
<button type="button" class="open-passkey-manual" title="パスキー設定マニュアルを表示します">利用手順</button>
</div>
</div>
<div style="clear:both;"></div>
</dialog>
<script>
$(function(){
$('.register_passkey').on('click', async function() {
try {
const userId = $('#UserUsername').val();
if ((userId == "") || (userId == 0)) {
alert("登録に失敗しました。");
return false;
}
console.log(userId);
const res = await fetch('/webauthn/registerBegin/'+userId);
const options = await res.json();
// options.challenge = Uint8Array.from(atob(options.challenge.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
// options.user.id = Uint8Array.from(options.user.id, c => c.charCodeAt(0));
options.challenge = base64urlToBuffer(options.challenge);
options.user.id = base64urlToBuffer(options.user.id); // ★ 修正ポイント
// --- excludeCredentials の id も ArrayBuffer に変換 ---
if (options.excludeCredentials) {
options.excludeCredentials = options.excludeCredentials.map(c => ({
...c,
id: base64urlToBuffer(c.id) // ← これをしないと同じデバイス再登録が防げない
}));
}
console.log("PublicKeyCredentialCreationOptions:", options);
const credential = await navigator.credentials.create({ publicKey: options });
console.log("credential:");
console.log(credential);
const result = await fetch('/webauthn/registerFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential)
});
const json = await result.json();
// alert(json.status === 'ok' ? '登録完了!' : '登録失敗 ('+json.message+')');
if (json.status == 0) {
alert('登録失敗 ('+json.message+')');
} else {
// 入力ダイアログを表示 + 入力内容を user に代入
var ua = window.navigator.userAgent.toLowerCase();
var device = getDevice(ua);
let remarks = window.prompt("【登録名】認証端末などを入力してください(会社スマホ、PCなど)", device);
const id = json.status;
if (remarks !== "") {
new Promise((resolve, reject) => {
$.ajax({
url: '/users/ajax_save_passkey_remarks',
type:'POST',
dataType: 'html',
data : {save : id, value : remarks },
async: false,
timeout:3000,
}).done(function(data) {
if (data === "error") {
alert('登録に失敗しました (1)');
}
// }).fail(function(XMLHttpRequest, textStatus, errorThrown) {
// alert('登録に失敗しました (2)');
})
resolve();
}).then(() => {
alert('登録完了!');
location.reload();
});
} else {
alert('登録完了!');
location.reload();
}
}
} catch (err) {
console.log("WebAuthn registration error:");
// わかりやすいメッセージに変換
let message = "登録に失敗しました。";
if (err instanceof DOMException && err.name === "InvalidStateError") {
message = "このデバイスはすでに登録されています。";
} else if (err instanceof DOMException && err.name === "NotAllowedError") {
message = "登録操作がキャンセルされました。";
}
alert(message);
}
});
});
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
function base64urlToUint8Array(base64url) {
const padding = '='.repeat((4 - base64url.length % 4) % 4);
const base64 = (base64url + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function getDevice(ua) {
let ret = '';
if(ua.indexOf("windows") !== -1) {
ret = "Windows";
} else if(ua.indexOf("android") !== -1) {
ret = "Android";
} else if(ua.indexOf("iphone") !== -1) {
ret = "iPhone";
} else if(ua.indexOf("ipad") !== -1) {
ret = "iPad";
} else if(ua.indexOf("mac os x") !== -1) {
ret = "Appleデバイス";
} else {
ret = "その他";
}
return ret;
}
</script>
<style>
.modeless,
.modal {
top: -50%;
}
</style>
',
'scripts_for_layout' => ''
)
$title_for_layout = 'ログイン - 派遣管理システム'
$page_name = ''
$controller_id = (int) 0
$controller = 'users'
$display_inquiry = ''
$list_auth = null
$list_menu = array(
(int) 1 => 'ホーム',
(int) 2 => 'メール',
(int) 3 => 'スタッフ管理',
(int) 4 => '案件管理',
(int) 5 => 'シフト管理',
(int) 6 => '勤怠管理',
(int) 7 => '売上給与',
(int) 8 => '現場',
(int) 9 => 'クライアント本社',
(int) 21 => '管理者ページ(履歴)',
(int) 30 => '全体',
(int) 31 => 'その他'
)
$notice1 = null
$list_class = array(
(int) 11 => '大阪-人材派遣',
(int) 12 => '大阪-住宅営業',
(int) 21 => '東京-人材派遣',
(int) 22 => '東京-住宅営業',
(int) 23 => '東京-IT営業',
(int) 31 => '名古屋-人材派遣',
(int) 32 => '名古屋-住宅営業',
(int) 41 => '福岡-人材派遣',
(int) 42 => '福岡-住宅営業',
(int) 51 => '札幌-人材派遣',
(int) 52 => '札幌-住宅営業',
(int) 61 => '仙台-人材派遣',
(int) 62 => '仙台-住宅営業',
(int) 71 => '〇〇-人材派遣',
(int) 72 => '〇〇-住宅営業',
(int) 81 => '〇〇-人材派遣',
(int) 82 => '〇〇-住宅営業',
(int) 91 => '〇〇-人材派遣',
(int) 92 => '〇〇-住宅営業',
(int) 991 => '管理者-人材派遣',
(int) 992 => '管理者-住宅営業'
)
$role = null
$result = array()
$username = null
$corp_name = '株式会社ソフトライフ'
$selected_class = null
$auth = object(AuthComponent) {
components => array(
(int) 0 => 'Session',
(int) 1 => 'Flash',
(int) 2 => 'RequestHandler'
)
authenticate => array(
'Form' => array(
[maximum depth reached]
)
)
authorize => false
ajaxLogin => null
flash => array(
'element' => 'default',
'key' => 'auth',
'params' => array([maximum depth reached])
)
loginAction => array(
'controller' => 'users',
'action' => 'login'
)
loginRedirect => array(
'controller' => 'users',
'action' => 'index'
)
logoutRedirect => array(
'controller' => 'users',
'action' => 'login'
)
authError => 'あなたのIDとパスワードを入力して下さい。'
unauthorizedRedirect => true
allowedActions => array(
(int) 0 => 'login',
(int) 1 => 'login2',
(int) 2 => 'loginBegin',
(int) 3 => 'loginFinish',
(int) 4 => 'logout',
(int) 5 => 'restriction',
(int) 6 => 'line',
(int) 7 => 'station',
(int) 8 => 'station2',
(int) 9 => 'login_test1',
(int) 10 => 'forget_pwd',
(int) 11 => 'reset_pwd',
(int) 12 => 'access_err'
)
request => object(CakeRequest) {}
response => object(CakeResponse) {}
settings => array(
'loginRedirect' => array(
[maximum depth reached]
),
'logoutRedirect' => array(
[maximum depth reached]
),
'authenticate' => array(
[maximum depth reached]
),
'loginAction' => array(
[maximum depth reached]
),
'authError' => 'あなたのIDとパスワードを入力して下さい。',
'allowedActions' => array(
[maximum depth reached]
)
)
[protected] _authenticateObjects => array(
(int) 0 => object(FormAuthenticate) {}
)
[protected] _authorizeObjects => array()
[protected] _user => array()
[protected] _methods => array(
(int) 0 => 'batch',
(int) 1 => 'index',
(int) 2 => 'edit_notice',
(int) 3 => 'manual_vpn',
(int) 4 => 'manual_passkey',
(int) 5 => 'window',
(int) 6 => 'window2',
(int) 7 => 'dialog1',
(int) 8 => 'issue_list',
(int) 9 => 'check_log',
(int) 10 => 'getArea',
(int) 11 => 'inquiry',
(int) 12 => 'inquiry_edit',
(int) 13 => 'ajax_upload_file',
(int) 14 => 'ajax_check_class',
(int) 15 => 'ajax_write_log',
(int) 16 => 'ajax_save_passkey_remarks',
(int) 17 => 'inquiry_edit2',
(int) 18 => 'list_file',
(int) 19 => 'detail',
(int) 20 => 'infra_input',
(int) 21 => 'forget_pwd',
(int) 22 => 'reset_pwd',
(int) 23 => 'restriction',
(int) 24 => 'add',
(int) 25 => 'add2',
(int) 26 => 'edit',
(int) 27 => 'edit2',
(int) 28 => 'view',
(int) 29 => 'passwd',
(int) 30 => 'passwd2',
(int) 31 => 'list_page',
(int) 32 => 'page_register',
(int) 33 => 'login',
(int) 34 => 'set_dir',
(int) 35 => 'send_mail',
(int) 36 => 'login2',
(int) 37 => 'getRpId',
(int) 38 => 'login3',
(int) 39 => 'loginBegin',
(int) 40 => 'loginFinish',
(int) 41 => 'passkey',
(int) 42 => 'access_err',
(int) 43 => 'auth_err',
(int) 44 => 'logout',
(int) 45 => 'logout2',
(int) 46 => 'clearCache',
(int) 47 => 'login_test1',
(int) 48 => 'getPasswordHash',
(int) 49 => 'timesheet',
(int) 50 => 'line',
(int) 51 => 'station',
(int) 52 => 'station2',
(int) 53 => 'list_line',
(int) 54 => 'list_station',
(int) 55 => 'setAuth',
(int) 56 => 'getValue',
(int) 57 => 'getOS',
(int) 58 => 'getBrowser',
(int) 59 => 'chkDirectory',
(int) 60 => 'ajax_get_session',
(int) 61 => 'ajax_get_cookie',
(int) 62 => 'send_err_info',
(int) 64 => 'isAuthorized',
(int) 65 => 'list_account',
(int) 66 => 'list_class',
(int) 67 => 'file_info',
(int) 68 => 'getAreaName',
(int) 69 => 'getCaseName',
(int) 70 => 'getListCaseName',
(int) 71 => 'getClientName',
(int) 72 => 'getFlagSO',
(int) 73 => 'writeSession',
(int) 74 => 'readSession',
(int) 75 => 'deleteSession',
(int) 76 => 'checkSession',
(int) 77 => 'writeOutputLog',
(int) 78 => 'getLastOutputLog',
(int) 79 => 'getLastOutputLog2',
(int) 80 => 'getLastOutputLog3',
(int) 81 => 'corp_name',
(int) 82 => 'setDataChangeLog',
(int) 83 => 'japan_holiday',
(int) 84 => 'send_notice'
)
[protected] _Collection => object(ComponentCollection) {}
[protected] _componentMap => array(
'Session' => array(
[maximum depth reached]
),
'Flash' => array(
[maximum depth reached]
),
'RequestHandler' => array(
[maximum depth reached]
)
)
}
$content_for_layout = '<link rel="stylesheet" type="text/css" href="/css/main.css"/><script>
$(function() {
$('.toggle-pass').on('click touchend', function() {
var input = $('#UserPassword');
if (input.attr('type') == 'text') {
input.attr('type','password');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/><';
} else {
input.attr('type','text');
var html = '<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"/>';
}
$(this).html(html);
});
sessionStorage.setItem('disp_popup', '');
});
</script>
<style>
.input-wrap{
position: relative;
}
.toggle-pass{
position:absolute;
top:50%;
right: 0px;
transform: translateY(-50%);
cursor:pointer;
}
#content {
padding-bottom: 10px;
}
input {
padding:3px 3px;
}
.btn {
transition-property: all;
transition-duration: 0.1s;
transition-timing-function: ease-in-out;
position: relative;
top: 0px;
background-color: #ffcc33;
color:black;
border: 1px solid gray;
font-size: 115%;
border-radius:10px;
padding:3px 10px;
margin-left:10px;
cursor:pointer;
-webkit-filter: drop-shadow(1px 1px 1px #ccc);
filter: drop-shadow(1px 1px 1 #ccc);
}
.btn:hover {
/*background-color: #ffcc33;*/
background-color: #ffffcc;
}
.btn:active {
top: 5px;
}
.btn:active {
-webkit-filter: drop-shadow(0 0 0 #ccc);
filter: drop-shadow(0 0 0 #ccc);
}
</style>
<!-- content start -->
<br />
<div style="margin-top: 50px;margin-bottom: 50px;">
<center>
<form action="/" id="UserLoginForm" method="post" accept-charset="utf-8"><div style="display:none;"><input type="hidden" name="_method" value="POST"/></div> <table cellspacing="0" cellpadding="5" border="1" align="center" id="staff_master">
<tr>
<td>
<table cellspacing="2" cellpadding="5" border="0" align="center" style="background-color: #ccffcc;">
<tr><td colspan="3"> </td></tr>
<tr>
<td style="width:5em;"> </td>
<td style="text-align: center;width:15em;"><b>ソフトライフ</b></td>
<td style="width:5em;"> </td>
</tr>
<tr>
<td> </td>
<td>
アカウントID<br>
<!-- 名前コンボの値セット START -->
<div class="input test required"><input name="data[User][account]" style="width: 100%;font-size:100%;" type="test" id="UserAccount" required="required"/></div> <input type="hidden" name="data[User][username]" style="width: 100%;font-size:100%;" id="UserUsername"/> <!-- 名前コンボの値セット END -->
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td>
パスワード<br>
<div class="input-wrap">
<input name="data[User][password]" style="width: 100%;" type="password" id="UserPassword" required="required"/> <!-- <i class="toggle-pass fas fa-eye-slash"></i>-->
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 640 512" class="toggle-pass">
<!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"/>
</svg>
</div>
</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td style="text-align: center;padding:10px 15px;">
<div style="float:left;padding-top:3px;">
<input name="login" class="load" type="submit" value="ログイン"/></form> </div>
<div style="float:right;padding-top:3px;">
<button type="button" id="login3" class="btn" title="パスキーでログイン">
<table>
<tr>
<td>
<img src="/img/fido2+.png" style="width:25px;" />
</td>
<td>
パスキー
</td>
</tr>
</table>
</button>
<!-- <img src="/img/fido2.png" id="login3" class="btn" title="多要素認証でログイン" style="border-radius:10px;cursor:pointer;" />-->
</div>
<div style="clear:both;"></div>
<input type="hidden" name="recaptcha_response" id="recaptchaResponse" />
<td> </td>
</tr>
<tr>
<td> </td>
<td align="left" style="padding-bottom:20px;">
<span style="font-size: 90%;">
※推奨:<a href="https://www.google.co.jp/chrome/browser/desktop/index.html" title="最も推奨" id="browser" target="_blank"><b>Chrome</b></a>, Edge, Firefox<br>
<font title="一部で不具合を確認しています。">Internet Explorerでは使用不可</font>
</span>
</td>
<td> </td>
</tr>
<tr><td nowrap> </td></tr>
</table>
</td>
</tr>
</table>
<div style="overflow-wrap: break-word;text-align:center;margin-top:30px;width:500px;">
●アカウントIDまたはパスワードをお忘れの方は<br><a href="/users/forget_pwd">[パスワードのリセット]</a>
</div>
</form> </center>
<!-- content end -->
</div>
<script>
document.getElementById('login3').onclick = async () => {
const account = $('#UserAccount').val();
const password = $('#UserPassword').val();
// const res = await fetch('/users/loginBegin');
const res = await fetch('/users/loginBegin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
account: account,
password: password
})
});
const json = await res.json();
if (json.status == 'error') {
alert(json.message);
return; // ダイアログを出さず終了
} else if (json.status == 'error2') {
$('#UserUsername').val(json.userId);
dialog_passkey();
return; // ダイアログを出さず終了
}
const options = json.options;
// options.challenge = new Uint8Array(options.challenge);
options.challenge = base64urlToBuffer(options.challenge);
// ★ allowCredentials.id を ArrayBuffer に変換する
if (options.allowCredentials) {
options.allowCredentials = options.allowCredentials.map(cred => {
cred.id = base64urlToBuffer(cred.id);
return cred;
});
}
//console.log("LOGIN user.id (before encode):", options);
//options.user.id = new TextEncoder().encode(options.user.id);
//console.log("LOGIN user.id (after encode):", options.user.id);
const assertion = await navigator.credentials.get({ publicKey: options });
const result = await fetch('/users/loginFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: assertion.id,
rawId: arrayBufferToBase64(assertion.rawId),
type: assertion.type,
response: {
clientDataJSON: arrayBufferToBase64(assertion.response.clientDataJSON),
authenticatorData: arrayBufferToBase64(assertion.response.authenticatorData),
signature: arrayBufferToBase64(assertion.response.signature),
// userHandle: arrayBufferToBase64(assertion.response.userHandle)
userHandle: arrayBufferToBase64(assertion.response.userHandle)
}
})
});
const json2 = await result.json();
// alert(json2.status === 'ok' ? 'ログイン成功!' : 'ログイン失敗 ('+json2.message+')');
if (json2.status === 'ok') {
// alert('ログイン成功!');
displayLogin();
$('#UserLoginForm').submit();
} else {
alert('ログイン失敗 ('+json2.message+')');
}
};
function dialog_passkey() {
// ダイアログ
const body = document.querySelector('body');
const modal = document.querySelector(".modal");
modal.showModal();
//モーダルダイアログを閉じる
const closeModal = document.querySelector(".close-modal");
closeModal.addEventListener("click", () => {
closePopup();
modal.close();
});
$(".open-passkey").on("click", function(){
closePopup();
openWindow6();
modal.close();
});
$(".open-passkey-manual").on("click", function(){
openWindow7();
// closePopup();
// modal.close();
});
$(".close-modal2").on("click", function(){
closePopup();
modal.close();
});
function closePopup() {
body.classList.remove('open_popup');
}
}
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
</script>
<!-- モーダルダイアログ -->
<style>
/* ダイアログを開くボタン */
.open-button {
margin: 10px;
width: 200px;
height: 50px;
font-size: 20px;
font-weight: bold;
border: none;
border-radius: 10px;
background-color: #fcc891;
cursor: pointer;
}
.open-button:hover {
color: #ffffff;
background-color: #ed7e07;
box-shadow: 0 5px 5px #cccccc;
}
/* ダイアログ */
.modeless,
.modal {
width: 70%;
max-width: 550px;
padding: 0;
border: none;
box-shadow: #595959 2px 2px 5px 2px;
}
/* モーダルダイアログの背後 */
.modal::backdrop {
background-color: #12121290;
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); /* Safari */
}
/* ダイアログヘッダー */
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 10px 0 10px;
margin-bottom: 0px;
color: #ffffff;
background-color: #085BB5;
}
.dialog-header p {
font-size: 16px;
vertical-align:middle;
padding: 10px 0px;
margin-bottom: 0px;
}
/* ダイアログを閉じるボタン */
.close-button {
width: 50px;
height: 30px;
font-size: 13px;
font-weight: bold;
margin-left: auto;
background-color: #e6e6e6;
cursor: pointer;
border: none;
border-radius: 10px;
}
.close-button:hover {
color: #ffffff;
font-weight: bold;
background-color: #ed7e07;
}
.title {
font-size: 30pxe;
margin: 10px 20px;
color: navy;
}
.message2 {
font-size: 16px;
margin: 10px 20px 10px;
/*background-color: #ffffee;*/
color: black;
}
.bottom-button {
padding: 10px 20px 30px;
text-align: left;
}
.open-passkey-manual {
margin-left:30px;background-color:blue;color:white;border-radius:5px;
}
</style>
<dialog class="modal">
<!-- ダイアログヘッダー -->
<div class="dialog-header">
<p>お知らせ</p>
<button type="button" class="close-modal close-button">✕</button>
</div>
<!-- ダイアログコンテンツ -->
<div style="float:left;padding:5px 5px;">
<img src="/img/passkie.png" />
<!-- <div style="text-align:center;color:brown;">ぱすきぃ</div>-->
</div>
<div style="float:left;">
<h2 class="title">パスキー登録のお願い</h2>
<div class="message2">セキュリティー向上のために<br>パスキーの登録をお願いいたします。</div>
<div class="bottom-button">
<button type="button" class="register_passkey" style="background-color:orange;color:black;padding:5px 10px;border-radius:5px;font-size:105%;"><i class="fas fa-key"></i> パスキー登録</button>
<button type="button" class="close-modal2" style="margin-left:20px;">スキップ</button>
<button type="button" class="open-passkey-manual" title="パスキー設定マニュアルを表示します">利用手順</button>
</div>
</div>
<div style="clear:both;"></div>
</dialog>
<script>
$(function(){
$('.register_passkey').on('click', async function() {
try {
const userId = $('#UserUsername').val();
if ((userId == "") || (userId == 0)) {
alert("登録に失敗しました。");
return false;
}
console.log(userId);
const res = await fetch('/webauthn/registerBegin/'+userId);
const options = await res.json();
// options.challenge = Uint8Array.from(atob(options.challenge.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
// options.user.id = Uint8Array.from(options.user.id, c => c.charCodeAt(0));
options.challenge = base64urlToBuffer(options.challenge);
options.user.id = base64urlToBuffer(options.user.id); // ★ 修正ポイント
// --- excludeCredentials の id も ArrayBuffer に変換 ---
if (options.excludeCredentials) {
options.excludeCredentials = options.excludeCredentials.map(c => ({
...c,
id: base64urlToBuffer(c.id) // ← これをしないと同じデバイス再登録が防げない
}));
}
console.log("PublicKeyCredentialCreationOptions:", options);
const credential = await navigator.credentials.create({ publicKey: options });
console.log("credential:");
console.log(credential);
const result = await fetch('/webauthn/registerFinish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credential)
});
const json = await result.json();
// alert(json.status === 'ok' ? '登録完了!' : '登録失敗 ('+json.message+')');
if (json.status == 0) {
alert('登録失敗 ('+json.message+')');
} else {
// 入力ダイアログを表示 + 入力内容を user に代入
var ua = window.navigator.userAgent.toLowerCase();
var device = getDevice(ua);
let remarks = window.prompt("【登録名】認証端末などを入力してください(会社スマホ、PCなど)", device);
const id = json.status;
if (remarks !== "") {
new Promise((resolve, reject) => {
$.ajax({
url: '/users/ajax_save_passkey_remarks',
type:'POST',
dataType: 'html',
data : {save : id, value : remarks },
async: false,
timeout:3000,
}).done(function(data) {
if (data === "error") {
alert('登録に失敗しました (1)');
}
// }).fail(function(XMLHttpRequest, textStatus, errorThrown) {
// alert('登録に失敗しました (2)');
})
resolve();
}).then(() => {
alert('登録完了!');
location.reload();
});
} else {
alert('登録完了!');
location.reload();
}
}
} catch (err) {
console.log("WebAuthn registration error:");
// わかりやすいメッセージに変換
let message = "登録に失敗しました。";
if (err instanceof DOMException && err.name === "InvalidStateError") {
message = "このデバイスはすでに登録されています。";
} else if (err instanceof DOMException && err.name === "NotAllowedError") {
message = "登録操作がキャンセルされました。";
}
alert(message);
}
});
});
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return btoa(binary);
}
// Base64URL → ArrayBuffer
function base64urlToBuffer(base64url) {
let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
let binary = atob(base64);
let len = binary.length;
let buffer = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
function base64urlToUint8Array(base64url) {
const padding = '='.repeat((4 - base64url.length % 4) % 4);
const base64 = (base64url + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function getDevice(ua) {
let ret = '';
if(ua.indexOf("windows") !== -1) {
ret = "Windows";
} else if(ua.indexOf("android") !== -1) {
ret = "Android";
} else if(ua.indexOf("iphone") !== -1) {
ret = "iPhone";
} else if(ua.indexOf("ipad") !== -1) {
ret = "iPad";
} else if(ua.indexOf("mac os x") !== -1) {
ret = "Appleデバイス";
} else {
ret = "その他";
}
return ret;
}
</script>
<style>
.modeless,
.modal {
top: -50%;
}
</style>
'
$scripts_for_layout = ''
$display = (int) 1
$style_body = ''
$style_content = '' include_once - APP/View/Layouts/login.ctp, line 25
include - APP/View/Layouts/login.ctp, line 25
View::_evaluate() - CORE/Cake/View/View.php, line 971
View::_render() - CORE/Cake/View/View.php, line 933
View::renderLayout() - CORE/Cake/View/View.php, line 546
View::render() - CORE/Cake/View/View.php, line 481
Controller::render() - CORE/Cake/Controller/Controller.php, line 968
Dispatcher::_invoke() - CORE/Cake/Routing/Dispatcher.php, line 200
Dispatcher::dispatch() - CORE/Cake/Routing/Dispatcher.php, line 167
require - APP/webroot/index.php, line 117
[main] - ROOT/index.php, line 41
パスキー登録のお願い
セキュリティー向上のために パスキーの登録をお願いいたします。
パスキー登録
スキップ
利用手順