diff --git a/app/controllers/Detail.php b/app/controllers/Detail.php index 6f796914932a56833e5ac6c36d92552bde122913..6799ca97ed749df26564eacf81de0bc0f3756c9a 100644 --- a/app/controllers/Detail.php +++ b/app/controllers/Detail.php @@ -66,9 +66,9 @@ class Detail extends Controller $entityBody = json_decode(file_get_contents('php://input'), true); //TODO MASUKKIN KARTU NASABAH ID $user = $model_user->readUserById($user_id); - $orderid = $soap->buyBook($_COOKIE['bookid'], $entityBody['total'], $user['no_kartu']); + $orderid = $soap->buyBook($_COOKIE['bookid'], $entityBody['total'], $user['no_kartu'], $entityBody['token']); if ($orderid != -1) { - $orderid = $model->createOrder($_COOKIE['bookid'], $user_id, $orderid); + $model->createOrder($_COOKIE['bookid'], $user_id, $orderid); } echo $orderid; diff --git a/app/controllers/History.php b/app/controllers/History.php index e356fd44fc19fbeec20df3d84381fc9985e72ea8..352d8d54ed1926f299d9ad8b588a6f7ebc50ddbb 100644 --- a/app/controllers/History.php +++ b/app/controllers/History.php @@ -8,7 +8,8 @@ class History extends Controller } if (isset($_COOKIE['access_token'])) { - if ($this->model('Token')->validateToken($_COOKIE['access_token'])) { + $id_user = $this->model('Token')->validateToken($_COOKIE['access_token']); + if ($id_user) { $access_valid = true; } else { $access_valid = false; @@ -22,7 +23,7 @@ class History extends Controller exit(); } else { - $data['data'] = $this->model("Order")->readHistoryByUserId($_COOKIE['id']); + $data['data'] = $this->model("Order")->readHistoryByUserId($id_user); if (count($data) > 0){ $data["navigation"] = "History"; $this->view("history", $data); diff --git a/app/controllers/Home.php b/app/controllers/Home.php index f8a8b63d316009c9e6165ca252ec29168c6aa71a..b81fd73e584bac9d47743d34b044fcb5a7e19e8a 100644 --- a/app/controllers/Home.php +++ b/app/controllers/Home.php @@ -9,7 +9,8 @@ class Home extends Controller } if (isset($_COOKIE['access_token'])) { - if ($this->model('Token')->validateToken($_COOKIE['access_token'])) { + $id_user = $this->model('Token')->validateToken($_COOKIE['access_token']); + if ($id_user) { $access_valid = true; } else { $access_valid = false; @@ -23,7 +24,7 @@ class Home extends Controller exit(); } else { - $data = $this->model("User")->readUserById($_COOKIE['id']); + $data = $this->model("User")->readUserById($id_user); $data["navigation"] = "Browse"; $this->view('home' ,$data); } @@ -62,7 +63,6 @@ class Home extends Controller } session_destroy(); $temp = $this->model("Token")->deleteToken($_COOKIE['access_token']); - setcookie("id", "", time() - 3600,'/'); setcookie("access_token","",time()-3600,'/'); header("location: /login"); exit(); diff --git a/app/controllers/Login.php b/app/controllers/Login.php index a4c018ff9ecb302d2e0ae32eee4a5a1c3cd63669..7e000f57994ba57cdedcc31d5c5b9b94f6c7b14e 100644 --- a/app/controllers/Login.php +++ b/app/controllers/Login.php @@ -38,7 +38,6 @@ class Login extends Controller if (password_verify($password, $temp["password"]) or ($password==$temp['password'])){ session_start(); $id = $model->readUserIdByUsername($temp['username'])['userID']; - setcookie('id', $id, time() + 1800, '/'); $token = substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 1).substr(md5(time()),1); setcookie('access_token', $token , time() + 1800, '/'); @@ -58,4 +57,28 @@ class Login extends Controller echo "<script>window.location.href='/login'; alert('Unknown Username'); </script>"; } } + + public function tokensignin() + { + header("Access-Control-Allow-Methods: POST"); + header("Content-Type: application/json; charset=UTF-8"); + $id_token = json_decode(file_get_contents("php://input"))->id_token; + $payload = json_decode(file_get_contents('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' . $id_token)); + + $user_model = $this->model('User'); + if ($user = $user_model->readUserByEmail($payload->email)) { + $token = substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 1).substr(md5(time()),1); + setcookie('access_token', $token , time() + 1800, '/'); + $model_token = $this->model('Token'); + $temp_token = $model_token->insertToken($user['userID'], $token); + + if (!isset($_SESSION)) { + session_start(); + } + $_SESSION['username'] = $user['username']; + } + else { + header("HTTP/1.1 404 Not Found"); + } + } } diff --git a/app/controllers/Profile.php b/app/controllers/Profile.php index 53c534708ffabdbc8de8fc1f9ca3206c4e3a9c8f..6e02e777bb645d0b94b5abf276a7da1c733b710c 100644 --- a/app/controllers/Profile.php +++ b/app/controllers/Profile.php @@ -3,13 +3,10 @@ class Profile extends Controller { public function index() - { - if (!isset($_SESSION)) { - session_start(); - } - + { if (isset($_COOKIE['access_token'])) { - if ($this->model('Token')->validateToken($_COOKIE['access_token'])) { + $user_id = $this->model('Token')->validateToken($_COOKIE['access_token']); + if ($user_id) { $access_valid = true; } else { $access_valid = false; @@ -22,7 +19,7 @@ class Profile extends Controller header('Location: /login'); } else { - $data = $this->model("User")->readUserById($_COOKIE['id']); + $data = $this->model("User")->readUserById($user_id); $data["navigation"] = "Profile"; $this->view("profile",$data); } @@ -30,10 +27,9 @@ class Profile extends Controller public function edit() { - session_start(); - + $id_user = $this->model('Token')->validateToken($_COOKIE['access_token']); if (isset($_COOKIE['access_token'])) { - if ($this->model('Token')->validateToken($_COOKIE['access_token'])) { + if ($id_user) { $access_valid = true; } else { $access_valid = false; @@ -46,7 +42,7 @@ class Profile extends Controller header('Location: /login'); exit(); } else { - $data = $this->model("User")->readUserById($_COOKIE['id']); + $data = $this->model("User")->readUserById($id_user); $data["navigation"] = "Profile"; $this->view("editprofile", $data); } @@ -67,7 +63,7 @@ class Profile extends Controller } if ($access_valid){ - $user['id'] = $_COOKIE['id']; + $user['id'] = $user_id; $user['name'] = $_POST['name']; $user['address'] = $_POST['address']; $user['phone'] = $_POST['phone']; diff --git a/app/controllers/Register.php b/app/controllers/Register.php index 9129cfd49585b1433c5b8b7872d07d81c9cb518f..a41e3719da1831fc32d6139a016963b497dd9601 100644 --- a/app/controllers/Register.php +++ b/app/controllers/Register.php @@ -45,7 +45,6 @@ class Register extends Controller } $id = $model->readUserIdByUsername($user['username'])['userID']; - setcookie('id', $id, time() + 3600,'/'); $token = substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 1).substr(md5(time()),1); setcookie('access_token', $token , time() + 1800, '/'); diff --git a/app/models/SoapHelper.php b/app/models/SoapHelper.php index e391860a458b5d2b5ae7e8b14eda5752e78db206..bac9da126db9435c86ad7c6c599a2559406d11e8 100644 --- a/app/models/SoapHelper.php +++ b/app/models/SoapHelper.php @@ -49,8 +49,8 @@ class SoapHelper { return $output; } - public function buyBook($id, $quantity, $no_rek) { - $data = $this->conn->buyBook($id, $quantity, $no_rek); + public function buyBook($id, $quantity, $no_rek, $token) { + $data = $this->conn->buyBook($id, $quantity, $no_rek, $token); $array = json_decode(json_encode($data), True); return $array; } diff --git a/app/models/Token.php b/app/models/Token.php index 5828e971c9381943ff8c3b36d274884983fbf89e..0bbed2b6d60999544a7d6228980c3cc29118fc9b 100644 --- a/app/models/Token.php +++ b/app/models/Token.php @@ -24,6 +24,8 @@ class Token extends Model $ip_address = $_SERVER['REMOTE_ADDR']; $result = $this->conn->query($sql)->fetch_assoc(); if ($result['id'] && $result['browser'] == $browser && $result['ip_address'] == $ip_address && time() < (int)$result['time']) { + $sql = "UPDATE access_token SET time = " . (time() + (int)1200) . " WHERE token = '" . $token . "';" ; + $this->conn->query($sql); return $result['id']; } else { return null; diff --git a/app/views/detail.php b/app/views/detail.php index 6ca7e33b22fead9478d58c8054c58dc11511151b..7aa9695ac8c4891e3c375573f5fe8ce6077bdc9a 100644 --- a/app/views/detail.php +++ b/app/views/detail.php @@ -5,6 +5,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Detail</title> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com"> <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" /> </head> <body> @@ -52,6 +53,11 @@ <?php } ?> </select> </div> + <label for="token" class="inp"> + <input type="text" id="token" placeholder=" "> + <span class="label">Token</span> + <span class="border"></span> + </label> <button onclick="order()" <?php if ($data['book']['price'] == -1) {echo "disabled";}?>>Order</button> </section> <section id="reviews"> @@ -98,12 +104,25 @@ <div id="dialog-msg"> <img id="check-notif" src="/public/images/check.png"> <div id="msg-detail"> - <p>Pemesanan Berhasil!</p> + <p id="text-msg-detail">Pemesanan Berhasil!</p> <p>Nomor Transaksi : <span id="transaction-number">3<span></p> </div> <img id="close-notif" onclick="close_notif()" src="/public/images/close.png"> </div> </div> <script src="/public/js/detail.js"></script> + <script> + function onLoad() { + gapi.load('auth2', function() { + gapi.auth2.init(); + }); + } + function signOut() { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut().then(function () { + console.log('User signed out.'); + }); + } + </script> </body> </html> diff --git a/app/views/editprofile.php b/app/views/editprofile.php index ce3fb91f9833d68022259ad46b1ade4c2e5dd068..9f5f5ac92349ba1cd14d4e4b7e8b3969254b33c6 100644 --- a/app/views/editprofile.php +++ b/app/views/editprofile.php @@ -5,51 +5,66 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Profile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com"> <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" /> </head> <body> <?php require_once('navbar.php') ?> - <div id="edit-profile-container" class="container"> - <h1 id="edit-profile-header"class="page-header">Edit Profile</h1> - <div id ="edit-profile-section"> - <form action="/Profile/save" method="POST" id="edit-profile-form" enctype="multipart/form-data"> - <div class ="edit-profile-row"> - <img id="edit-profile-image" src="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>"> - <div class="edit-profile-label"> - <label for="avatar"> Update Profile Picture </label> - <div id="image-input"> - <div id="filebox"></div> - <input type="button" id="browse-button" onclick="document.getElementById('ava').click()" value="Browse ..." /> - <input id = "avaHidden" class ="edit-profile-input" type="text" name="avaHidden" value="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>" hidden> - <input type="file" id="ava" name="avatar" onchange="FileSelected()" accept="image/*"> - </div> + <div id="edit-profile-container" class="container"> + <h1 id="edit-profile-header"class="page-header">Edit Profile</h1> + <div id ="edit-profile-section"> + <form action="/Profile/save" method="POST" id="edit-profile-form" enctype="multipart/form-data"> + <div class ="edit-profile-row"> + <img id="edit-profile-image" src="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>"> + <div class="edit-profile-label"> + <label for="avatar"> Update Profile Picture </label> + <div id="image-input"> + <div id="filebox"></div> + <input type="button" id="browse-button" onclick="document.getElementById('ava').click()" value="Browse ..." /> + <input id = "avaHidden" class ="edit-profile-input" type="text" name="avaHidden" value="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>" hidden> + <input type="file" id="ava" name="avatar" onchange="FileSelected()" accept="image/*"> </div> </div> - <div class ="edit-profile-row"> - <label class = "edit-profile-label" for="name"> Name </label> - <input id = "nameinput" class ="edit-profile-input" type="text" name="name" value="<?php echo $data['name']?>"> - </div> - <div class ="edit-profile-row"> - <label id = "addresslabel" class = "edit-profile-label" for="address"> Address </label> - <textarea id = "addressinput" class ="edit-profile-input" form="edit-profile-form" name="address"><?php echo $data['address']?></textarea> - </div> - <div class ="edit-profile-row"> - <label class = "edit-profile-label" for="phone"> Phone Number </label> - <input id = "phoneinput" class ="edit-profile-input" type="text" name="phone" value="<?php echo $data['phone']?>" > - </div> - <div class ="edit-profile-row"> - <label class = "edit-profile-label" for="bank-account"> Bank Account </label> - <input id = "bankinput" class ="edit-profile-input" type="text" name="bank-account" value="<?php echo $data['no_kartu']?>" > - </div> - <div class ="edit-profile-row" id = "edit-profile-button-row"> - <!-- <button onclick="location.href='/Profile'" id="cancel-button" type="button">Back</button> --> - <a href="/Profile" id = "cancel-button">Back</a> - <!-- <button id="save-button" type="button">Save</button> --> - <input type="submit" value="Save" id="save-button" class="save-button"> - </div> - </form> - </div> + </div> + <div class ="edit-profile-row"> + <label class = "edit-profile-label" for="name"> Name </label> + <input id = "nameinput" class ="edit-profile-input" type="text" name="name" value="<?php echo $data['name']?>"> + </div> + <div class ="edit-profile-row"> + <label id = "addresslabel" class = "edit-profile-label" for="address"> Address </label> + <textarea id = "addressinput" class ="edit-profile-input" form="edit-profile-form" name="address"><?php echo $data['address']?></textarea> + </div> + <div class ="edit-profile-row"> + <label class = "edit-profile-label" for="phone"> Phone Number </label> + <input id = "phoneinput" class ="edit-profile-input" type="text" name="phone" value="<?php echo $data['phone']?>" > + </div> + <div class ="edit-profile-row"> + <label class = "edit-profile-label" for="bank-account"> Bank Account </label> + <input id = "bankinput" class ="edit-profile-input" type="text" name="bank-account" value="<?php echo $data['no_kartu']?>" > + </div> + <div class ="edit-profile-row" id = "edit-profile-button-row"> + <!-- <button onclick="location.href='/Profile'" id="cancel-button" type="button">Back</button> --> + <a href="/Profile" id = "cancel-button">Back</a> + <!-- <button id="save-button" type="button">Save</button> --> + <input type="submit" value="Save" id="save-button" class="save-button"> + </div> + </form> </div> - <script src="../../public/js/editprofile.js"></script> + </div> + <script src="../../public/js/editprofile.js"></script> + <script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script> + <script> + function onLoad() { + gapi.load('auth2', function() { + gapi.auth2.init(); + }); + } + function signOut() { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut().then(function () { + console.log('User signed out.'); + }); + } + </script> </body> </html> diff --git a/app/views/history.php b/app/views/history.php index f2bb20b116e7084ea9726a008f4a62fdfc05c4e5..dea3911f216c266cfb6049d8afb6d13b6d6d41b1 100644 --- a/app/views/history.php +++ b/app/views/history.php @@ -5,6 +5,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Home</title> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com"> <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" /> </head> <body> @@ -42,5 +43,19 @@ </div> <?php } }?> </div> + <script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script> + <script> + function onLoad() { + gapi.load('auth2', function() { + gapi.auth2.init(); + }); + } + function signOut() { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut().then(function () { + console.log('User signed out.'); + }); + } + </script> </body> </html> diff --git a/app/views/home.php b/app/views/home.php index 65728654087d038c1371d7adfc7ce8a36c92330f..f1956131ffd07999b621274a78c3179187aac527 100644 --- a/app/views/home.php +++ b/app/views/home.php @@ -5,6 +5,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Home</title> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com"> <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" /> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script> <link rel="stylesheet" href="https://loading.io/css/loading.css"> @@ -50,5 +51,19 @@ </div> </div> <script src="/public/js/search.js"></script> + <script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script> + <script> + function onLoad() { + gapi.load('auth2', function() { + gapi.auth2.init(); + }); + } + function signOut() { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut().then(function () { + console.log('User signed out.'); + }); + } + </script> </body> </html> diff --git a/app/views/login.php b/app/views/login.php index ce7a1a07f3cdd885d6e42b2c638ef81629bfdeee..dc52728fff8818ce2ac024298e21103b0ae7d7d1 100644 --- a/app/views/login.php +++ b/app/views/login.php @@ -5,6 +5,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Login</title> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com"> <link rel="stylesheet" type="text/css" media="screen" href="../../public/css/main.css" /> </head> <body> @@ -22,13 +23,17 @@ <label for="password">Password</label> <input type="password" name="password"> </div> - <a href="/register">Don't have an account?</a> - <button id="submit-btn" class="disabled-btn" type="submit" disabled>LOGIN</button> + <div class="register-google-login"> + <button id="submit-btn" class="disabled-btn" type="submit" disabled>LOGIN</button> + <div class="g-signin2" data-onsuccess="onSignIn">Google</div> + </div> + <p><a href="/register">Don't have an account? register </form> </div> </div> </section> </div> + <script src="https://apis.google.com/js/platform.js" async defer></script> <script src="../../public/js/login.js"></script> </body> </html> \ No newline at end of file diff --git a/app/views/navbar.php b/app/views/navbar.php index 715ef0d89a9725d5449f6840b7268afd64b6f0cc..671ff1a22234adf0a5f350d3f46a08955f660599 100644 --- a/app/views/navbar.php +++ b/app/views/navbar.php @@ -10,7 +10,7 @@ </div> <div id="logout-button"> - <a href="/Home/logout"> + <a href="/Home/logout" onclick="signOut()"> <img src="../../public/images/logout.png"> </a> </div> @@ -29,4 +29,4 @@ </li></a> </ul> </div> -</nav> +</nav> \ No newline at end of file diff --git a/app/views/profile.php b/app/views/profile.php index 3cc038945d52d3b4bc9c9781311b5de287afba8a..582e17741d7c169b23b3e19f247a710efcf60785 100644 --- a/app/views/profile.php +++ b/app/views/profile.php @@ -5,6 +5,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Profile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com"> <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" /> </head> <body> @@ -43,9 +44,30 @@ <div class ="profile-row"> <div class ="profile-icon"><img src="../../public/images/bank.png"></div> <div class ="profile-label">Bank account</div> - <div class ="profile-value"><?php echo $data['no_kartu']?></div> + <div class ="profile-value"><?php echo $data['no_kartu']?></div> + <input id="no-kartu" type="button" value="<?php echo $data['no_kartu']?>" hidden/> + </div> + <div class="qr-code"> + <p>QR code</p> + <img id="qr-img" src=""> </div> </div> + + </div> + <script src="../../public/js/profile.js"></script> + <script> + function onLoad() { + gapi.load('auth2', function() { + gapi.auth2.init(); + }); + } + function signOut() { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut().then(function () { + console.log('User signed out.'); + }); + } + </script> </body> </html> \ No newline at end of file diff --git a/app/views/review.php b/app/views/review.php index 5b2471262209df6c8762895221a05e41bee09211..9f7139613f0636eb506951e9d24e545131132707 100644 --- a/app/views/review.php +++ b/app/views/review.php @@ -5,43 +5,57 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Review</title> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com"> <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" /> </head> <body> <?php require_once('navbar.php') ?> - <div class="container"> - <div id="review-detail"> - <div id=review-desc> - <h1 class="page-header"><?php echo $data['title']?></h1> - <p id="author"><?php echo $data['author']?></p> - </div> - <img src=<?php echo $data['bookPicture']?> class="review-image"> + <div class="container"> + <div id="review-detail"> + <div id=review-desc> + <h1 class="page-header"><?php echo $data['title']?></h1> + <p id="author"><?php echo $data['author']?></p> </div> - <form action="/review/submit" id ="review-form" method="POST"> - <div class="review-rate"> - <p id="review-addrate">Add Rating</p> - <div class="rating-star-wrapper"> - <img src="/public/images/star.png" class="filled-star star star1"> - <img src="/public/images/star-unfilled.png" class="unfilled-star star star1"> - <img src="/public/images/star.png" class="filled-star star star2"> - <img src="/public/images/star-unfilled.png" class="unfilled-star star star2"> - <img src="/public/images/star.png" class="filled-star star star3"> - <img src="/public/images/star-unfilled.png" class="unfilled-star star star3"> - <img src="/public/images/star.png" class="filled-star star star4"> - <img src="/public/images/star-unfilled.png" class="unfilled-star star star4"> - <img src="/public/images/star.png" class="filled-star star star5"> - <img src="/public/images/star-unfilled.png" class="unfilled-star star star5"> - </div> - <input id="ratinginput" type="number" name="rating" value=""> - <p id="review-addrate">Add Comment</p> - <textarea id = "commentinput" class ="comment-input" form ="review-form" name="comment"></textarea> - </div> - <div class ="comment-row"> - <a href="/History" id = "back-button">Back</a> - <button id="save-button" class="disabled-save-button" type="submit" disabled>SAVE</button> - </div> - </form> + <img src=<?php echo $data['bookPicture']?> class="review-image"> </div> + <form action="/review/submit" id ="review-form" method="POST"> + <div class="review-rate"> + <p id="review-addrate">Add Rating</p> + <div class="rating-star-wrapper"> + <img src="/public/images/star.png" class="filled-star star star1"> + <img src="/public/images/star-unfilled.png" class="unfilled-star star star1"> + <img src="/public/images/star.png" class="filled-star star star2"> + <img src="/public/images/star-unfilled.png" class="unfilled-star star star2"> + <img src="/public/images/star.png" class="filled-star star star3"> + <img src="/public/images/star-unfilled.png" class="unfilled-star star star3"> + <img src="/public/images/star.png" class="filled-star star star4"> + <img src="/public/images/star-unfilled.png" class="unfilled-star star star4"> + <img src="/public/images/star.png" class="filled-star star star5"> + <img src="/public/images/star-unfilled.png" class="unfilled-star star star5"> + </div> + <input id="ratinginput" type="number" name="rating" value=""> + <p id="review-addrate">Add Comment</p> + <textarea id = "commentinput" class ="comment-input" form ="review-form" name="comment"></textarea> + </div> + <div class ="comment-row"> + <a href="/History" id = "back-button">Back</a> + <button id="save-button" class="disabled-save-button" type="submit" disabled>SAVE</button> + </div> + </form> + </div> <script src="/public/js/review.js"></script> + <script> + function onLoad() { + gapi.load('auth2', function() { + gapi.auth2.init(); + }); + } + function signOut() { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut().then(function () { + console.log('User signed out.'); + }); + } + </script> </body> </html> diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..707632dcae8d2de97e4d6883184911888438f4f6 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "sonata-project/google-authenticator": "^2.2" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000000000000000000000000000000000..f36ec219428a511a46ef23ef81b8d453f0d0bcde --- /dev/null +++ b/composer.lock @@ -0,0 +1,75 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "e2df69fcfa963bd7ffe8e358d7e93d70", + "packages": [ + { + "name": "sonata-project/google-authenticator", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/sonata-project/GoogleAuthenticator.git", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Google\\Authenticator\\": "src/", + "Sonata\\GoogleAuthenticator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Stocker", + "email": "me@chregu.tv" + }, + { + "name": "Andre DeMarre", + "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989" + }, + { + "name": "Thomas Rabaix", + "email": "thomas.rabaix@gmail.com" + } + ], + "description": "Library to integrate Google Authenticator into a PHP project", + "homepage": "https://github.com/sonata-project/GoogleAuthenticator", + "keywords": [ + "google authenticator" + ], + "time": "2018-07-18T22:08:02+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/example.php b/example.php new file mode 100644 index 0000000000000000000000000000000000000000..50366d2fa9c470a34eb2474568393a4642a0ad78 --- /dev/null +++ b/example.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +include_once __DIR__.'/../src/FixedBitNotation.php'; +include_once __DIR__.'/../src/GoogleAuthenticator.php'; +include_once __DIR__.'/../src/GoogleQrUrl.php'; + +$secret = 'XVQ2UIGO75XRUKJO'; +$code = '846474'; + +$g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator(); + +echo 'Current Code is: '; +echo $g->getCode($secret); + +echo "\n"; + +echo "Check if $code is valid: "; + +if ($g->checkCode($secret, $code)) { + echo "YES \n"; +} else { + echo "NO \n"; +} + +$secret = $g->generateSecret(); +echo "Get a new Secret: $secret \n"; +echo "The QR Code for this secret (to scan with the Google Authenticator App: \n"; + +echo \Sonata\GoogleAuthenticator\GoogleQrUrl::generate('chregu', $secret, 'GoogleAuthenticatorExample'); +echo "\n"; diff --git a/index.php b/index.php index afa4a1aba47c70e2c90aea4438d7feb3d3673b83..996aa32a611deba4c23295a3f6db6b5f0bf47503 100644 --- a/index.php +++ b/index.php @@ -1,5 +1,6 @@ <?php ini_set('display_errors', 0); +require_once 'vendor/autoload.php'; require_once 'app/init.php'; diff --git a/public/css/main.css b/public/css/main.css index 7bfd34b9d22487c2d17c8ba9e1c19c5793f86d2e..4d7b3da810e0365d2b1e65c0d32cb3cfdc3335d6 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -904,6 +904,13 @@ body { display: none; } +.register-google-login { + display: flex; + flex-flow: column; + /* justify-content: center; */ + align-items: center; +} + @keyframes lds-spinner { 0% { opacity: 1; @@ -1034,4 +1041,74 @@ body { height: 200px !important; -webkit-transform: translate(-100px, -100px) scale(1) translate(100px, 100px); transform: translate(-100px, -100px) scale(1) translate(100px, 100px); +} + +.qr-code{ + display: block; + text-align: center; + width: 100%; + height: 100%; + margin: auto; + border-radius: 3px; +} + +* .inp { + position: relative; + margin: auto; + width: 100%; + max-width: 280px; +} +* .inp .label { + position: absolute; + top: 16px; + left: 0; + font-size: 16px; + color: #9098a9; + font-weight: 500; + transform-origin: 0 0; + transition: all 0.2s ease; +} +* .inp .border { + position: absolute; + bottom: 0; + left: 0; + height: 2px; + width: 100%; + background: #07f; + transform: scaleX(0); + transform-origin: 0 0; + transition: all 0.15s ease; +} +* .inp input { + -webkit-appearance: none; + width: 100%; + border: 0; + font-family: inherit; + padding: 12px 0; + height: 48px; + font-size: 16px; + font-weight: 500; + border-bottom: 2px solid #c8ccd4; + background: none; + border-radius: 0; + color: #223254; + transition: all 0.15s ease; +} +* .inp input:hover { + background: rgba(34,50,84,0.03); +} +* .inp input:not(:placeholder-shown) + span { + color: #5a667f; + transform: translateY(-26px) scale(0.75); +} +* .inp input:focus { + background: none; + outline: none; +} +* .inp input:focus + span { + color: #07f; + transform: translateY(-26px) scale(0.75); +} +* .inp input:focus + span + .border { + transform: scaleX(1); } \ No newline at end of file diff --git a/public/images/profile/32 b/public/images/profile/32 new file mode 100644 index 0000000000000000000000000000000000000000..27cac80db760b8d73f20e7d0401ee5f3b1e5e530 Binary files /dev/null and b/public/images/profile/32 differ diff --git a/public/js/detail.js b/public/js/detail.js index f3d3a3cd777d45704ea1ef3b49b310fc7eaee87f..c0ddcce64d299c92f8c1ce866ed8c01267770417 100644 --- a/public/js/detail.js +++ b/public/js/detail.js @@ -1,5 +1,7 @@ function order() { var total_order_element = document.getElementById("total-order"); + var token = document.getElementById("token").value; + console.log("token: "+token); var sum_order = total_order_element.options[total_order_element.selectedIndex].value; var xhttp = new XMLHttpRequest(); xhttp.open("POST", "/detail/order"); @@ -7,11 +9,21 @@ function order() { xhttp.onreadystatechange = function() { console.log(this.responseText); if (this.readyState == 4 && this.status == 200 && this.responseText != -1) { + document.getElementById("text-msg-detail").innerHTML = "Pemesanan Berhasil!"; document.getElementById("transaction-number").innerHTML = this.responseText; document.getElementById("notif").setAttribute("style", "display: flex"); + document.getElementById("check-notif").src = "/public/images/check.png"; + } else { + document.getElementById("text-msg-detail").innerHTML = "Pemesanan Gagal!"; + document.getElementById("transaction-number").innerHTML = -1; + document.getElementById("notif").setAttribute("style", "display: flex"); + document.getElementById("check-notif").src = "/public/images/close.png"; + } } - xhttp.send(JSON.stringify({total : sum_order})); + xhttp.send(JSON.stringify({ + total : sum_order, + token : token})); } function close_notif() { diff --git a/public/js/login.js b/public/js/login.js index 2e3952bcc60fa7ea215d9b62ca5adbbf9c7d2039..110a46387507322503045887d92848def2e2fe3a 100644 --- a/public/js/login.js +++ b/public/js/login.js @@ -33,4 +33,22 @@ document.querySelector("#password input").addEventListener("input", function() { pass_valid = true; } toggle_btn(); -}) \ No newline at end of file +}); + +function onSignIn(googleUser) { + var id_token = googleUser.getAuthResponse().id_token; + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + window.location="/home"; + } + else { + var auth2 = gapi.auth2.getAuthInstance(); + auth2.signOut(); + } + } + + xhr.open('POST', '/login/tokensignin'); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.send(JSON.stringify({id_token: id_token})); +} \ No newline at end of file diff --git a/public/js/profile.js b/public/js/profile.js new file mode 100644 index 0000000000000000000000000000000000000000..d26153eb0e2a39618d3132309d02ee6a54f6d6b8 --- /dev/null +++ b/public/js/profile.js @@ -0,0 +1,11 @@ +// console.log("No kartu: " + document.getElementById("no-kartu").value); +var xhttp = new XMLHttpRequest(); +xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + var response = JSON.parse(this.responseText); + document.getElementById("qr-img").src = response.values + } +} +xhttp.open("POST", "http://localhost:3000/qrcode", true); +xhttp.setRequestHeader("Content-type", "application/json"); +xhttp.send(JSON.stringify({ "no_kartu": document.getElementById("no-kartu").value })); diff --git a/service/rest/api/controllers/key.js b/service/rest/api/controllers/key.js new file mode 100644 index 0000000000000000000000000000000000000000..d9256a71a3de2f953eb98d4df55c2de73116de50 --- /dev/null +++ b/service/rest/api/controllers/key.js @@ -0,0 +1,58 @@ +var response = require('../controllers/res'); +var nasabah = require('../models/nasabah'); +var QRCode = require('qrcode'); +var speakeasy = require('speakeasy'); + +exports.key = function(req, res) { + var no_kartu = req.body.no_kartu; + nasabah.getKeyByCard(no_kartu, function(error, rows) { + if (error) { + console.log(error) + return res.status(500).json({ + message : 'Internal server error' + }); + } + else { + if (rows.length == 0) { + console.log("TIDAK VALID: "+no_kartu); + response.ok(0, res); + } else { + console.log("KEY " + no_kartu + " IS " + rows[0].secret_key) + response.ok(rows[0].secret_key, res); + } + } + }); +}; + +exports.qrcode = function(req, res) { + var no_kartu = req.body.no_kartu; + nasabah.getKeyByCard(no_kartu, function(error, rows) { + if (error) { + console.log(error) + return res.status(500).json({ + message : 'Internal server error' + }); + } + else { + if (rows.length == 0) { + console.log("TIDAK VALID: "+no_kartu); + response.ok(0, res); + } else { + var secret = rows[0].secret_key; + var label = 'token'; + var issuer = 'probook' + const otpAuthUrl = speakeasy.otpauthURL({ + secret, + label, + issuer, + encoding: 'base32', + }); + QRCode.toDataURL(otpAuthUrl, function(err, image_data) { + // console.log(image_data); // A data URI for the QR code image + response.ok(image_data, res); + }); + + } + } + }); +}; \ No newline at end of file diff --git a/service/rest/api/controllers/transfer.js b/service/rest/api/controllers/transfer.js index 6031da828d8b2d9e2bddeb158d2c7cda45e8b42d..6b06d8b05eb93467f9bb8afec335ab41a28db89b 100644 --- a/service/rest/api/controllers/transfer.js +++ b/service/rest/api/controllers/transfer.js @@ -1,67 +1,97 @@ const response = require('../controllers/res'); const nasabah = require('../models/nasabah'); const transaksi = require('../models/transaksi'); +var speakeasy = require('speakeasy'); exports.transfer = function(req, res) { const pengirim = req.body.no_pengirim; const penerima = req.body.no_penerima; const jumlah = Number(req.body.jumlah); + const token = req.body.token; + console.log(token) + console.log(token[0]) + console.log(token[1]) - nasabah.getNasabahByCard(pengirim, function(error, rows) { - if (error) { - console.log(error); - return res.status(500).json({ - message : 'Internal server error' - }); - } - else { - if (rows.length == 0) { - response.notFound('No kartu pengirim tidak ditemukan', res); - } - else if (Number(rows[0].saldo) < jumlah) { - response.notAcceptable('Saldo pengirim tidak cukup', res); - } - else { - let saldo_pengirim = Number(rows[0].saldo); - nasabah.getNasabahByCard(penerima, function(error, rows) { - if (error) { - console.log(error); - return res.status(500).json({ - message : 'Internal server error' - }); - } - else { - if (rows.length == 0) { - response.notFound('No kartu penerima tidak ditemukan', res); - } - else { - let saldo_penerima = Number(rows[0].saldo); - console.log(saldo_penerima + jumlah); - nasabah.updateBalanceByCard(penerima, pengirim, (saldo_penerima + jumlah), (saldo_pengirim - jumlah), function(error) { - if (error) { - console.log(error); - return res.status(500).json({ - message : 'Internal server error' - }); - } - else { - transaksi.insertNewTransaction(penerima, pengirim, jumlah, function(error) { - if (error) { - console.log(error); - return res.status(500).json({ - message : error - }); - } - else { - response.ok('Transfer berhasil', res); - } - }) - } - }); - } - } - }) - } - } + nasabah.getKeyByCard(pengirim, function(error, rows) { + if (error) { + console.log(error) + return res.status(500).json({ + message : 'Internal server error' + }); + } + else { + if (rows.length == 0) { + console.log("TIDAK VALID: "+pengirim); + response.notFound('No kartu pengirim tidak ditemukan', res); + } else { + console.log("VALIDASI TOKEN: " + token) + if (speakeasy.totp.verify({ secret: rows[0].secret_key, + encoding: 'base32', + token: token })) { + + nasabah.getNasabahByCard(pengirim, function(error, rows) { + if (error) { + console.log(error); + return res.status(500).json({ + message : 'Internal server error' + }); + } + else { + if (rows.length == 0) { + response.notFound('No kartu pengirim tidak ditemukan', res); + } + else if (Number(rows[0].saldo) < jumlah) { + response.notAcceptable('Saldo pengirim tidak cukup', res); + } + else { + let saldo_pengirim = Number(rows[0].saldo); + nasabah.getNasabahByCard(penerima, function(error, rows) { + if (error) { + console.log(error); + return res.status(500).json({ + message : 'Internal server error' + }); + } + else { + if (rows.length == 0) { + response.notFound('No kartu penerima tidak ditemukan', res); + } + else { + let saldo_penerima = Number(rows[0].saldo); + console.log(saldo_penerima + jumlah); + nasabah.updateBalanceByCard(penerima, pengirim, (saldo_penerima + jumlah), (saldo_pengirim - jumlah), function(error) { + if (error) { + console.log(error); + return res.status(500).json({ + message : 'Internal server error' + }); + } + else { + transaksi.insertNewTransaction(penerima, pengirim, jumlah, function(error) { + if (error) { + console.log(error); + return res.status(500).json({ + message : error + }); + } + else { + response.ok('Transfer berhasil', res); + } + }) + } + }); + } + } + }) + } + } + }); + + } else { + response.notAcceptable('Token salah', res); + } + } + } }); + } diff --git a/service/rest/api/models/nasabah.js b/service/rest/api/models/nasabah.js index 841a4176fbf43726c112cdf15b6d4db2531dda1f..734982c1b16d27e443750e44d792d22d7b18942f 100644 --- a/service/rest/api/models/nasabah.js +++ b/service/rest/api/models/nasabah.js @@ -12,6 +12,17 @@ module.exports = { }); }, + getKeyByCard: function(card_num, callback) { + connection.query('SELECT secret_key FROM nasabah WHERE no_kartu = ? LIMIT 1', card_num, function (error, rows, fields) { + if (error) { + return callback(error); + } + else { + return callback(null, rows); + } + }); + }, + updateBalanceByCard: function(card_receiver, card_sender, receiver_new_balance, sender_new_balance, callback) { connection.getConnection(function(error, conn) { if (error) { diff --git a/service/rest/api/routes/route.js b/service/rest/api/routes/route.js index 622afa25afbac0a2db02c268bba72606d9952788..f72ec8692b8f736c801d080646449bef83fe8e91 100644 --- a/service/rest/api/routes/route.js +++ b/service/rest/api/routes/route.js @@ -1,5 +1,6 @@ module.exports = function(app) { const validasi = require('../controllers/validasi'); + const key = require('../controllers/key'); const nasabah = require('../controllers/transfer'); app.use(function(req, res, next) { @@ -20,4 +21,10 @@ module.exports = function(app) { app.route('/transfer') .post(nasabah.transfer); + + app.route('/key') + .post(key.key); + + app.route('/qrcode') + .post(key.qrcode); }; \ No newline at end of file diff --git a/service/rest/database/webservice_bank.sql b/service/rest/database/webservice_bank.sql index 3706ce0682722ab7b94126d8048ca2fc3e7e32dc..0946a5598d5b6141aebf8f3c8ffe29495524150b 100644 --- a/service/rest/database/webservice_bank.sql +++ b/service/rest/database/webservice_bank.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.13 Distrib 5.7.17, for macos10.12 (x86_64) +-- MySQL dump 10.16 Distrib 10.3.10-MariaDB, for Win64 (AMD64) -- -- Host: localhost Database: webservice_bank -- ------------------------------------------------------ --- Server version 5.7.21 +-- Server version 10.3.10-MariaDB /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -26,7 +26,8 @@ CREATE TABLE `nasabah` ( `id` int(11) NOT NULL AUTO_INCREMENT, `nama` varchar(255) NOT NULL, `no_kartu` varchar(16) NOT NULL, - `saldo` decimal(50,2) NOT NULL DEFAULT '0.00', + `saldo` decimal(50,2) NOT NULL DEFAULT 0.00, + `secret_key` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `no_kartu_UNIQUE` (`no_kartu`) ) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8; @@ -38,7 +39,7 @@ CREATE TABLE `nasabah` ( LOCK TABLES `nasabah` WRITE; /*!40000 ALTER TABLE `nasabah` DISABLE KEYS */; -INSERT INTO `nasabah` VALUES (1,'Charlotte Crane','5276132361916817',21394667.00),(2,'Ifeoma Dennis','5286723727626013',28139541.00),(3,'Keefe Stewart','5325129205703212',30465052.00),(4,'Rooney Warren','5227979660057598',10377602.00),(5,'Merrill Cook','5190360108515658',40538528.00),(6,'Holmes Cash','5177140223066341',12910688.00),(7,'Jorden Gutierrez','5285983471586166',7535644.00),(8,'Leroy Gay','5383964234862319',5244400.00),(9,'Frances Fuller','5527141327317884',34620348.00),(10,'Yeo Woodward','5103924062586793',40493665.00),(11,'Cole Ratliff','5379113508344093',34194939.00),(12,'Hammett Cline','5320135648090286',22486289.00),(13,'Priscilla Cross','5405128358909207',37758436.00),(14,'Fritz Mcintyre','5338433399779607',8039850.00),(15,'Kaden Page','5445962468685279',4612701.00),(16,'Autumn Davenport','5266297387437569',48670986.00),(17,'Rose Kaufman','5154262923084478',39986708.00),(18,'Mufutau Frazier','5350925176673724',30049129.00),(19,'Bryar Spence','5485026381319456',1499273.00),(20,'Rhonda Bentley','5397712060970290',35638064.00),(21,'Mara Joseph','5423839779395168',13923574.00),(22,'Daria Hale','5579185435634927',47652543.00),(23,'Candice Caldwell','5326739572440641',38261784.00),(24,'Gareth Dawson','5286083783472897',34018735.00),(25,'Azalia Camacho','5539663075005672',11962318.00),(26,'Grady Carlson','5448133600195182',16136156.00),(27,'Fredericka Brown','5272618031689226',36289974.00),(28,'Travis Curtis','5490283076847159',4512989.00),(29,'Kendall West','5538010661301886',1133173.00),(30,'Raphael Wallace','5416001472882112',37005267.00),(31,'Jeremy Riley','5304615022923772',37897128.00),(32,'Addison Hays','5199313088377376',8085189.00),(33,'Giacomo Gillespie','5390474676309531',32954873.00),(34,'Cleo Humphrey','5344531319507198',45993595.00),(35,'Ryan Nunez','5369734784023838',12806772.00),(36,'Sybil Mullen','5165912794190500',9735406.00),(37,'Sawyer Glover','5537167229140320',41045077.00),(38,'Hadassah Haynes','5268821798543794',5363287.00),(39,'Pearl Fitzpatrick','5559189881670850',23468519.00),(40,'Karyn Mcintyre','5591062763383894',47605383.00),(41,'Gavin Horn','5206803166426641',33293723.00),(42,'Rogan Bender','5410244319572407',8027515.00),(43,'Rigel House','5374615452371802',11362760.00),(44,'Roth Carey','5286312813966002',41241779.00),(45,'Bianca Conway','5544237251759510',19697493.00),(46,'Gray Olsen','5217134586897626',24166806.00),(47,'Ainsley Hatfield','5270443848826412',15670510.00),(48,'Aiko Calhoun','5534182104590724',37706349.00),(49,'Shelly Glass','5116273284860353',1669649.00),(50,'Regina Daniel','5417152860720403',40613394.00),(51,'Nerea Santana','5351672888845765',14199809.00),(52,'Ruth Miller','5459431609457710',17137056.00),(53,'Addison Mcclain','5480813762628380',45995104.00),(54,'Gail Shelton','5225789026415311',13914823.00),(55,'Kenneth Neal','5466848953504519',32152391.00),(56,'Ciaran Lewis','5549310920364809',16644423.00),(57,'Ocean Manning','5550067471358624',31729780.00),(58,'Erin Carson','5206305362172607',3800171.00),(59,'Samuel Irwin','5396926467981566',10050739.00),(60,'Herrod Howard','5133113883436198',28583062.00),(61,'Jescie Moon','5563590114328535',30836039.00),(62,'Claire Morin','5108326696983005',42664381.00),(63,'Lars Frank','5407906446397982',32697340.00),(64,'Summer Carroll','5499091893011854',4864930.00),(65,'Maite Santos','5477298142460180',28755025.00),(66,'Clark Fuentes','5349245581534385',49709793.00),(67,'Jordan Calhoun','5461689590615457',30631938.00),(68,'Audra Howell','5595960727394509',38964910.00),(69,'Carter Turner','5384289657823021',17501706.00),(70,'Kelsey Barry','5294109698613976',47103610.00),(71,'Chadwick Pace','5127999712180512',31375005.00),(72,'Shana Holland','5289830083014093',34205904.00),(73,'Eleanor Horn','5332808922265453',17735274.00),(74,'Jorden Farley','5559043425807397',22352248.00),(75,'Sebastian Johns','5174458385196463',24266219.00),(76,'Bevis Spence','5491952705015961',42504462.00),(77,'Hanae Rodriquez','5121081065598883',5348956.00),(78,'Malachi Moran','5334538526549352',1015059.00),(79,'Keely Moreno','5141292744575830',28593189.00),(80,'Xerxes Howe','5290282368400133',20999720.00),(81,'Dolan Howell','5185262453474290',4338581.00),(82,'Aretha Cooley','5346678478221588',19909990.00),(83,'Katelyn Ward','5532315662946555',44976212.00),(84,'Ahmed Kirkland','5519962750810297',40448438.00),(85,'Eve Clayton','5157084593586682',12252670.00),(86,'Baker Beck','5241166234480108',1543855.00),(87,'Carissa Gonzalez','5383436249380256',4001159.00),(88,'Darryl Cruz','5450352695891522',30342987.00),(89,'Lucius Terrell','5154786522417788',40738793.00),(90,'Xavier Smith','5122865966224170',20748740.00),(91,'Clarke Lancaster','5243967066072295',12564403.00),(92,'Adam Mcgee','5335172916188024',48521171.00),(93,'Colton Moore','5359008695947712',43491126.00),(94,'Kevin Glass','5354252342545025',7928903.00),(95,'Kiara Vaughan','5403970891646651',36640479.00),(96,'Harriet Cherry','5475997632056281',38104320.00),(97,'Armando Hardin','5548607767377427',20439149.00),(98,'Thor Dudley','5220642403737663',18897109.00),(99,'Chandler Alford','5534260752591994',44255207.00),(100,'Camden Head','5562991563901817',44065587.00); +INSERT INTO `nasabah` VALUES (1,'Charlotte Crane','5276132361916817',66394667.00,'KJ3CYOJXJRFHKJSJHISV2OJRMNKWS5B2'),(2,'Ifeoma Dennis','5286723727626013',28139541.00,'MRGG6ZKUOVYSGZ2WNV2CS3BYNN3HERKP'),(3,'Keefe Stewart','5325129205703212',30465052.00,'M5UTGYZJMFXHIWBOEZ5XCWZGGZ4HQ6LB'),(4,'Rooney Warren','5227979660057598',10377602.00,'GFGUI2DLKRFTMNBIK5OU6UZKGF5TOJTC'),(5,'Merrill Cook','5190360108515658',40538528.00,'H52XSTTPOIYCSUT2GZUUCMTJEMRVO6LU'),(6,'Holmes Cash','5177140223066341',12910688.00,'FZYUI3SQMFBGQ4ZVORAGWLBPOQWEG5D3'),(7,'Jorden Gutierrez','5285983471586166',7535644.00,'ORZGW3S5JRXUQRZMJQ2WWZLWEMXEUP23'),(8,'Leroy Gay','5383964234862319',5244400.00,'IJ3SK5RWJESV4U2NPE5UUI3SM42DS52T'),(9,'Frances Fuller','5527141327317884',34620348.00,'LVRDUS3ZHFNXAOC3OFGSMJKONZLF442I'),(10,'Yeo Woodward','5103924062586793',40493665.00,'FJSCYI3DHR2FCT3OJVAVEVCVHQSEMUZJ'),(11,'Cole Ratliff','5379113508344093',34194939.00,'KVRGOKJXOJWDIILRMI7HUM3OI5YDKQCI'),(12,'Hammett Cline','5320135648090286',22486289.00,'FA2TS2BXIIVFO5B2LN2DCRBJPM4EEOBQ'),(13,'Priscilla Cross','5405128358909207',37758436.00,'PBJUSR2RPBSEC7JEHZ3XWKT3EEUTYNCG'),(14,'Fritz Mcintyre','5338433399779607',8039850.00,'IJRFANZMIFZF4NKHM42TM6SHNEWFKP2U'),(15,'Kaden Page','5445962468685279',4612701.00,'I5EXIILQHJICST3MPEZFCP3ZPJCW2IZ6'),(16,'Autumn Davenport','5266297387437569',48670986.00,'ORRWO3R7IQRVE6TTLJ4TOSJMFZDDWUCL'),(17,'Rose Kaufman','5154262923084478',39986708.00,'HNTDAUSYOFDW2XS5EQXDIMTOI4SSCKC5'),(18,'Mufutau Frazier','5350925176673724',30049129.00,'LV4FEKBXJM2DK5T5JBUWKS3WG5NXGODM'),(19,'Bryar Spence','5485026381319456',1499273.00,'ENZTAQDPIJ6UELCUKR2UGVKQKR3GYQCK'),(20,'Rhonda Bentley','5397712060970290',35638064.00,'ONUUQ4SSLNFEUWTXJQWDGY25NUVEELSI'),(21,'Mara Joseph','5423839779395168',13923574.00,'ORCCM7KILBAEO6LBLMUVQYRZMJFHQYTU'),(22,'Daria Hale','5579185435634927',47652543.00,'KRWCGMRMENTHW23QHA2FCUC5HJBU2KR4'),(23,'Candice Caldwell','5326739572440641',38261784.00,'KUUDK4DFMFFDMOCUOJ5FEVCNMJOTCT25'),(24,'Gareth Dawson','5286083783472897',34018735.00,'KZSFI3J4IV5DCPZUNURTIVBOJM3EARDG'),(25,'Azalia Camacho','5539663075005672',11962318.00,'OI7DU5C6F5PG6IKIEZLEKNCIJBNWMPZ2'),(26,'Grady Carlson','5448133600195182',16136156.00,'OJSCQ7L5LBETG42IJISD4ZTSFYZSGYSU'),(27,'Fredericka Brown','5272618031689226',36289974.00,'KRNFEXK5OEUCU5JWJ5ICGVDEHBHXSZKW'),(28,'Travis Curtis','5490283076847159',4512989.00,'JZLUYVTYNZPH2JCKGBWEQJJBOEUS6PSC'),(29,'Kendall West','5538010661301886',1133173.00,'NNTTSJLVIZJTCNS3HJVTY33MOJXUGNR6'),(30,'Raphael Wallace','5416001472882112',37005267.00,'OB3VAOLUM5IUA2CDLAUVCMBFHF3FMOST'),(31,'Jeremy Riley','5304615022923772',37897128.00,'LVCWEOJUINKGKMCNPI5EQ23INMZTG6K5'),(32,'Addison Hays','5199313088377376',8085189.00,'HRQT4NZRHZ5UE33LIQRUMZLMO4UVWM2Q'),(33,'Giacomo Gillespie','5390474676309531',32954873.00,'HE7VGWZEPBQTKR2RHBFHMW3FIJVX2OCM'),(34,'Cleo Humphrey','5344531319507198',45993595.00,'PNBXCJDYHZATGY2WF5RUOVLGHF2VOKK2'),(35,'Ryan Nunez','5369734784023838',12806772.00,'EZAGIZBBJAWH24RENEUGYVLYOEZWI2ZJ'),(36,'Sybil Mullen','5165912794190500',9735406.00,'PI3EETL2FA2DC6SNIMYWONK2F5XTM6JQ'),(37,'Sawyer Glover','5537167229140320',41045077.00,'KARUUJDIHZ5TUI2FGVHEK4KEKRRX22J2'),(38,'Hadassah Haynes','5268821798543794',5363287.00,'G52VOSJFORGW6ULQOYSCKNZGOUVDGWBE'),(39,'Pearl Fitzpatrick','5559189881670850',23468519.00,'JZJTITLGNYSFQ6TDMEYCMVBXHFTDUORR'),(40,'Karyn Mcintyre','5591062763383894',47605383.00,'HY7TQ6RXIR6UC5SWNR4DUOK6KREUYQTZ'),(41,'Gavin Horn','5206803166426641',33293723.00,'HBFGOLZ2OI2VI4CMFYXWMS2MHZZW6VDP'),(42,'Rogan Bender','5410244319572407',8027515.00,'KF2W4LBEHJLFI4KBLVEGM2CBERIG4TRS'),(43,'Rigel House','5374615452371802',11362760.00,'HBDG4R2KGFRVKOZVMRWVOL2GMJGE4QTP'),(44,'Roth Carey','5286312813966002',41241779.00,'PJ5TS626JFTV4TKGJJGFC4JVMFTCMJLW'),(45,'Bianca Conway','5544237251759510',19697493.00,'EY4GQZJXIEUWMSDIF5UGQ6DTKRUF46LT'),(46,'Gray Olsen','5217134586897626',24166806.00,'JNQWW5CKFQ7DKURMMN3UG5JSOQ2DIQTN'),(47,'Ainsley Hatfield','5270443848826412',15670510.00,'LJRDA3LMKQ4GIZSMIIXWCJD5ONZVEQLY'),(48,'Aiko Calhoun','5534182104590724',37706349.00,'PAXFUKJGIRNHUJKLJNEE6TS2FQ6EGNBM'),(49,'Shelly Glass','5116273284860353',1669649.00,'KVKVUUS2FBNWMILHOI5DAVSPNFQVIPRK'),(50,'Regina Daniel','5417152860720403',40613394.00,'EQSTOMCXGZEDC7K5GFATWSZIEZFCSJCJ'),(51,'Nerea Santana','5351672888845765',14199809.00,'KFFDCSDBEFNWMTCTJ5GEUSS6PFETYJKI'),(52,'Ruth Miller','5459431609457710',17137056.00,'MRYUY6JGLBBCGKKBNRDFCPCLMVBUM7KI'),(53,'Addison Mcclain','5480813762628380',45995104.00,'HRZXWL2LHFEVEIZYOJRG6W3NH4WEYIKV'),(54,'Gail Shelton','5225789026415311',13914823.00,'LV3WEMJKOJNS4YKUOYVHWRCEKRLUWURK'),(55,'Kenneth Neal','5466848953504519',32152391.00,'MFPEATTHMRJUGZ3JPA5DI2J6IRTCM6TI'),(56,'Ciaran Lewis','5549310920364809',16644423.00,'LYZSUMSEEQ7H2YZGNBRG6ZCPNRFX23DV'),(57,'Ocean Manning','5550067471358624',31729780.00,'KE7DE3LGMJLSSS2UER2TSVB3IJAHEZK3'),(58,'Erin Carson','5206305362172607',3800171.00,'JBZUOZLOJ47FIT2XMVBDYVCWM4XE2KJW'),(59,'Samuel Irwin','5396926467981566',10050739.00,'EZYGYYSWKU2UW4TJKZUGETJFONKDOLCY'),(60,'Herrod Howard','5133113883436198',28583062.00,'ORIWCJTBFFAHMXLUINLVGY3IIJDVQI2M'),(61,'Jescie Moon','5563590114328535',30836039.00,'OEZGOVDOG5PFOL3QFFUHIUDCMERTUVBO'),(62,'Claire Morin','5108326696983005',42664381.00,'GBVV2I3LJ5GEYVDMMN5EAOCVNZGWQY3U'),(63,'Lars Frank','5407906446397982',32697340.00,'HZDHKRJQKV4DE3S6GZLUEJJ6GBJXQRBY'),(64,'Summer Carroll','5499091893011854',4864930.00,'INBFEQLCFE7HUMBKLJTEWKTHJNWDAUJM'),(65,'Maite Santos','5477298142460180',28755025.00,'JRZF4NZXFJJFOULFMZ2FGWTLEQ4W2ZLD'),(66,'Clark Fuentes','5349245581534385',49709793.00,'H5IDQLDJFZWHOVKXGA2SI22XLB3WWPRE'),(67,'Jordan Calhoun','5461689590615457',30631938.00,'HEVHOVZRO5BXUUCRPAYES3S5O45VI7JY'),(68,'Audra Howell','5595960727394509',38964910.00,'EZNT62LLHJTFIW2IGUXWETLDPISTCYJP'),(69,'Carter Turner','5384289657823021',17501706.00,'IUVDOVCAOMTGI6SOKFKHCZREME7EUOC3'),(70,'Kelsey Barry','5294109698613976',47103610.00,'G54WCTTDGVDUS2LOHI3G6QBDGBFW2XS2'),(71,'Chadwick Pace','5127999712180512',31375005.00,'MNWCQ2BMMN3UCY3IGJVUEMB6IY7S4WZD'),(72,'Shana Holland','5289830083014093',34205904.00,'KBEUKJKLKVLFIR2LGI7SI4TUEQVHMKCC'),(73,'Eleanor Horn','5332808922265453',17735274.00,'J5YHENRMON5TKYSJERKDEP2BOUQTK43T'),(74,'Jorden Farley','5559043425807397',22352248.00,'OBFCQN2CJMXCU3KPGI4CYQ2THBUHSVZQ'),(75,'Sebastian Johns','5174458385196463',24266219.00,'GNXEC6BOOJZTQRKONMYTSP2UGJTHWW25'),(76,'Bevis Spence','5491952705015961',42504462.00,'HFJVUR2KKQQWE2L5F5RW4UR2HBQSIXLI'),(77,'Hanae Rodriquez','5121081065598883',5348956.00,'MJKEM3ZSEE3SUSZKNVQWOVCLONTXSNTB'),(78,'Malachi Moran','5334538526549352',1015059.00,'OE2U2ORFNB3WQ52FGQXVCTK6J43FKNSK'),(79,'Keely Moreno','5141292744575830',28593189.00,'FBVVCKTLHRRXEPBPERHWW3JOMRTWIRKN'),(80,'Xerxes Howe','5290282368400133',20999720.00,'HZCHS3DFHFGGWMTGMYXWCYROPAYG4PDC'),(81,'Dolan Howell','5185262453474290',4338581.00,'KN4U4XKKPE5GMVTBKY4TSMLENFLHINLT'),(82,'Aretha Cooley','5346678478221588',19909990.00,'NNTC4MRTOBGXC2DILJ4XKLTGENSXIVSD'),(83,'Katelyn Ward','5532315662946555',44976212.00,'EZFVWS3QKIYU6JREOMZUU3TFI5NGKWTL'),(84,'Ahmed Kirkland','5519962750810297',40448438.00,'J5BUSQREEVKFIXKEJA2FOMTMLJVTAQSP'),(85,'Eve Clayton','5157084593586682',12252670.00,'KIUXM6TSKI6GO7LSLJBS4V2MHNCV4RBW'),(86,'Baker Beck','5241166234480108',1543855.00,'PNQTE23DGZQSM6DVPBJHMQBVOZBX2LDT'),(87,'Carissa Gonzalez','5383436249380256',4001159.00,'ENTFAN3QKQWCKXRSJF4TE6BFFJNGQ3RE'),(88,'Darryl Cruz','5450352695891522',30342987.00,'NBSW4TRQLVOUAV25GF2FKOK5FZJF47JS'),(89,'Lucius Terrell','5154786522417788',40738793.00,'EYQWMI2HJJAEYJDRIR4XGUSKGRDTYLBY'),(90,'Xavier Smith','5122865966224170',20748740.00,'MZRVEZ2UMJ6UII3DOZJDKOCDKIZVM6RX'),(91,'Clarke Lancaster','5243967066072295',12564403.00,'HAYEIILHGEZT6T2YNM5F43LVIZSWMSJR'),(92,'Adam Mcgee','5335172916188024',48521171.00,'OQ7EIQ2JHQ3UKSBRMFNXGP3IOI2TCJCP'),(93,'Colton Moore','5359008695947712',43491126.00,'LIYFEKLPIVJSGLZYEETGW3LIIJCFKSKT'),(94,'Kevin Glass','5354252342545025',7928903.00,'LN5WEZDSGQ2VO632ER4CKYSUHY2TGVSC'),(95,'Kiara Vaughan','5403970891646651',36640479.00,'G5NC4JRXJ5IVK3S5HRIFIZTSNMTEKMRJ'),(96,'Harriet Cherry','5475997632056281',38104320.00,'MI2E4ZKOONFGIPTSNU4T6UTYEU7EQ5SD'),(97,'Armando Hardin','5548607767377427',20439149.00,'OQ7H25CCOAYUIOLFNMSGWY2GHRQTSMLQ'),(98,'Thor Dudley','5220642403737663',18897109.00,'MZ5DYZZFN4YGEZJWO5IWGZ2CEQYWGMZR'),(99,'Chandler Alford','5534260752591994',44255207.00,'GFVXITRRMEVCQ3RVPAXXOV35NZRGE52R'),(100,'Camden Head','5562991563901817',1000000.00,'HJEXOVRENA5DAUJKJQ3VORKAHBXCSSCW'); /*!40000 ALTER TABLE `nasabah` ENABLE KEYS */; UNLOCK TABLES; @@ -53,14 +54,14 @@ CREATE TABLE `transaksi` ( `id` int(11) NOT NULL AUTO_INCREMENT, `no_pengirim` varchar(16) NOT NULL, `no_penerima` varchar(16) NOT NULL, - `jumlah` decimal(50,2) NOT NULL DEFAULT '0.00', - `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `jumlah` decimal(50,2) NOT NULL DEFAULT 0.00, + `timestamp` timestamp NOT NULL DEFAULT current_timestamp(), PRIMARY KEY (`id`), KEY `nomor kartu_idx` (`no_pengirim`,`no_penerima`), KEY `nomor kartu 2` (`no_penerima`), CONSTRAINT `nomor kartu` FOREIGN KEY (`no_pengirim`) REFERENCES `nasabah` (`no_kartu`), CONSTRAINT `nomor kartu 2` FOREIGN KEY (`no_penerima`) REFERENCES `nasabah` (`no_kartu`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -69,6 +70,7 @@ CREATE TABLE `transaksi` ( LOCK TABLES `transaksi` WRITE; /*!40000 ALTER TABLE `transaksi` DISABLE KEYS */; +INSERT INTO `transaksi` VALUES (1,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:33:42'),(2,'5562991563901817','5276132361916817',10000000.00,'2018-11-30 04:34:14'),(3,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:37:39'),(4,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:42:59'),(5,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:43:31'),(6,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:43:32'),(7,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:44:16'),(8,'5562991563901817','5276132361916817',25000000.00,'2018-11-30 04:44:36'),(9,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:46:19'),(10,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:53:26'),(11,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:53:47'),(12,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 07:35:58'); /*!40000 ALTER TABLE `transaksi` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; @@ -81,4 +83,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2018-11-21 18:07:41 +-- Dump completed on 2018-11-30 14:40:11 diff --git a/service/rest/package-lock.json b/service/rest/package-lock.json index 76e3e835d51c1566251f9726f9127b8a6aa86d7d..e624a70c204ff0d8a13d33c10542b6ce137e9409 100644 --- a/service/rest/package-lock.json +++ b/service/rest/package-lock.json @@ -31,8 +31,7 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "ansi-styles": { "version": "3.2.1", @@ -161,6 +160,11 @@ } } }, + "base32.js": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz", + "integrity": "sha1-0EVzalex9sE58MffQlGKhOkbsro=" + }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -264,6 +268,11 @@ } } }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, "bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", @@ -289,8 +298,15 @@ "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "can-promise": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz", + "integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==", + "requires": { + "window-or-global": "1.0.1" + } }, "capture-stack-trace": { "version": "1.0.1", @@ -365,6 +381,54 @@ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -460,7 +524,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, "requires": { "lru-cache": "4.1.4", "shebang-command": "1.2.0", @@ -481,6 +544,11 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -544,6 +612,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dijkstrajs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz", + "integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs=" + }, "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", @@ -575,6 +648,14 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "0.2.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -611,7 +692,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, "requires": { "cross-spawn": "5.1.0", "get-stream": "3.0.0", @@ -852,6 +932,14 @@ "unpipe": "1.0.0" } }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "2.0.0" + } + }, "flatmap-stream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.2.tgz", @@ -1418,11 +1506,15 @@ } } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-value": { "version": "2.0.6", @@ -1482,8 +1574,7 @@ "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha1-/7cD4QZuig7qpMi4C6klPu77+wA=", - "dev": true + "integrity": "sha1-/7cD4QZuig7qpMi4C6klPu77+wA=" }, "has-flag": { "version": "3.0.0", @@ -1523,6 +1614,11 @@ } } }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -1571,6 +1667,11 @@ "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", "dev": true }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, "ipaddr.js": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", @@ -1596,6 +1697,11 @@ } } }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -1611,6 +1717,14 @@ "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "dev": true }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "1.1.1" + } + }, "is-ci": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", @@ -1674,8 +1788,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { "version": "4.0.0", @@ -1761,8 +1874,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-windows": { "version": "1.0.2", @@ -1778,8 +1890,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -1802,6 +1913,41 @@ "package-json": "4.0.1" } }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "1.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "requires": { + "graceful-fs": "4.1.15", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -1818,7 +1964,6 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.4.tgz", "integrity": "sha1-UcxG6ObZUwdxyFfiTMxyDs28wDE=", - "dev": true, "requires": { "pseudomap": "1.0.2", "yallist": "3.0.2" @@ -1859,6 +2004,14 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "1.2.0" + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -1908,6 +2061,11 @@ "mime-db": "1.37.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2047,6 +2205,17 @@ "abbrev": "1.1.1" } }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "requires": { + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "5.6.0", + "validate-npm-package-license": "3.0.4" + } + }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", @@ -2060,11 +2229,15 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, "requires": { "path-key": "2.0.1" } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -2127,11 +2300,41 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "1.3.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, "package-json": { "version": "4.0.1", @@ -2145,6 +2348,14 @@ "semver": "5.6.0" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "1.3.2" + } + }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -2162,6 +2373,11 @@ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2177,14 +2393,28 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -2200,6 +2430,11 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, + "pngjs": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz", + "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==" + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -2238,8 +2473,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "pstree.remy": { "version": "1.1.0", @@ -2250,6 +2484,25 @@ "ps-tree": "1.1.0" } }, + "qrcode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.2.tgz", + "integrity": "sha512-sYo+Tf7nx14YZSNPqnyEWefVrjUEpM4/NxOrxOk+jcQ9NhM2LCNuzGmQctDhGTQdO2YJorKY55dypx+hvVo5jw==", + "requires": { + "can-promise": "0.0.1", + "dijkstrajs": "1.0.1", + "isarray": "2.0.4", + "pngjs": "3.3.3", + "yargs": "8.0.2" + }, + "dependencies": { + "isarray": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", + "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==" + } + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -2288,6 +2541,25 @@ "strip-json-comments": "2.0.1" } }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -2360,6 +2632,16 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -2394,8 +2676,7 @@ "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=", - "dev": true + "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=" }, "semver-diff": { "version": "2.1.0", @@ -2437,6 +2718,11 @@ "send": "0.16.2" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -2469,7 +2755,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, "requires": { "shebang-regex": "1.0.0" } @@ -2477,14 +2762,12 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "snapdragon": { "version": "0.8.2", @@ -2618,6 +2901,42 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "spdx-correct": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", + "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", + "requires": { + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.2" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "2.2.0", + "spdx-license-ids": "3.0.2" + } + }, + "spdx-license-ids": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", + "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==" + }, + "speakeasy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz", + "integrity": "sha1-hckaBxsJpcuGQlkNmDVmFl9XYTo=", + "requires": { + "base32.js": "0.0.1" + } + }, "split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", @@ -2681,7 +3000,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", - "dev": true, "requires": { "is-fullwidth-code-point": "2.0.0", "strip-ansi": "4.0.0" @@ -2699,16 +3017,19 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "3.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-json-comments": { "version": "2.0.1", @@ -2965,6 +3286,15 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "3.0.2", + "spdx-expression-parse": "3.0.0" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -2974,11 +3304,15 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", - "dev": true, "requires": { "isexe": "2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, "widest-line": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", @@ -2988,6 +3322,53 @@ "string-width": "2.1.1" } }, + "window-or-global": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz", + "integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, "write-file-atomic": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", @@ -3005,11 +3386,43 @@ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "requires": { + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.3", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "requires": { + "camelcase": "4.1.0" + } } } } diff --git a/service/rest/package.json b/service/rest/package.json index b8762f323dc82c544153945cdecffc03cb77c8b1..32a297186ea84ab03219ca70cf787772b536f1df 100644 --- a/service/rest/package.json +++ b/service/rest/package.json @@ -13,7 +13,9 @@ "body-parser": "~1.0.1", "express": "^4.16.4", "morgan": "^1.9.1", - "mysql": "^2.16.0" + "mysql": "^2.16.0", + "qrcode": "^1.3.2", + "speakeasy": "^2.0.0" }, "devDependencies": { "nodemon": "^1.18.6" diff --git a/service/soap/src/example/HelloWorld.java b/service/soap/src/example/HelloWorld.java index 677198d2de8147bb963711268077e67bc8010857..cb070dcba84796cecf8ac200e7fd3e4893b76df3 100644 --- a/service/soap/src/example/HelloWorld.java +++ b/service/soap/src/example/HelloWorld.java @@ -35,10 +35,10 @@ public class HelloWorld { public static void main(String[] argv) { // // test getBookByTitle - ArrayList<Book> testGetBookByTitle = getBookByTitle("kue"); - ArrayList<Book> testGetBookByCategory = getBookByCategory("Cooking"); - System.out.println("\n\n Test balikan1, Judul:" + testGetBookByTitle.get(0).getTitle()); - System.out.println("\n\n Test balikan2, Judul:" + testGetBookByCategory.get(0).getTitle()); +// ArrayList<Book> testGetBookByTitle = getBookByTitle("kue"); +// ArrayList<Book> testGetBookByCategory = getBookByCategory("Cooking"); +// System.out.println("\n\n Test balikan1, Judul:" + testGetBookByTitle.get(0).getTitle()); +// System.out.println("\n\n Test balikan2, Judul:" + testGetBookByCategory.get(0).getTitle()); // // // test getBookDetailByID // Book testGetBookDetailByID = getBookDetailByID("Xl9nDwAAQBAJ"); @@ -46,31 +46,31 @@ public class HelloWorld { // test BookRepository - DaftarHarga dummyDaftarHarga1 = new DaftarHarga("napeDwAAQBAJ", 130000); - DaftarPenjualan dummyDaftarPejualan1 = new DaftarPenjualan("napeDwAAQBAJ", "Cooking", 3); // test daftar baru - DaftarPenjualan dummyDaftarPejualan2 = new DaftarPenjualan("Xl9nDwAAQBAJ", "Cooking", 1); // test daftar yang sudah ada - BookRepository testBookRepository = new BookRepository(); - try { - testBookRepository.connect(); - DaftarHarga test1 = testBookRepository.getDaftarHarga("Xl9nDwAAQBAJ"); - System.out.println("\nTest\nHarga buku: " + test1.getHarga()); - - test1 = testBookRepository.getDaftarHarga("tubeswbdacac"); - System.out.println("\nTest\nHarga buku: " + test1.getHarga()); - - DaftarPenjualan test2 = testBookRepository.getDaftarPenjualan("Xl9nDwAAQBAJ"); - System.out.println("\nTest\nJumlah penjualan buku: " + test2.getJumlah()); - - testBookRepository.insertDaftarHarga(dummyDaftarHarga1); - testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan1); - testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan2); - - - } catch (SQLException e) { - e.printStackTrace(); - } finally { - testBookRepository.disconnect(); - } +// DaftarHarga dummyDaftarHarga1 = new DaftarHarga("napeDwAAQBAJ", 130000); +// DaftarPenjualan dummyDaftarPejualan1 = new DaftarPenjualan("napeDwAAQBAJ", "Cooking", 3); // test daftar baru +// DaftarPenjualan dummyDaftarPejualan2 = new DaftarPenjualan("Xl9nDwAAQBAJ", "Cooking", 1); // test daftar yang sudah ada +// BookRepository testBookRepository = new BookRepository(); +// try { +// testBookRepository.connect(); +// DaftarHarga test1 = testBookRepository.getDaftarHarga("Xl9nDwAAQBAJ"); +// System.out.println("\nTest\nHarga buku: " + test1.getHarga()); +// +// test1 = testBookRepository.getDaftarHarga("tubeswbdacac"); +// System.out.println("\nTest\nHarga buku: " + test1.getHarga()); +// +// DaftarPenjualan test2 = testBookRepository.getDaftarPenjualan("Xl9nDwAAQBAJ"); +// System.out.println("\nTest\nJumlah penjualan buku: " + test2.getJumlah()); +// +// testBookRepository.insertDaftarHarga(dummyDaftarHarga1); +// testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan1); +// testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan2); +// +// +// } catch (SQLException e) { +// e.printStackTrace(); +// } finally { +// testBookRepository.disconnect(); +// } // System.out.println(WebServiceBankApi.transfer("5276132361916817", "5285983471586166", 7535644F)); @@ -82,6 +82,8 @@ public class HelloWorld { Object implementor2 = new BookServiceImpl(); String address2 = "http://localhost:9000/BookService"; Endpoint.publish(address2, implementor2); + +// System.out.println(((BookServiceImpl) implementor2).buyBook("NP1wDwAAQBAJ", 25, "5562991563901817")); } } diff --git a/service/soap/src/model/Book.java b/service/soap/src/model/Book.java index 1177bb9206122e4a772e7ad93fee2fd327dd2e1d..6012076f8e5f5b4e3dc58e3a9f6b89fcb6a4f322 100644 --- a/service/soap/src/model/Book.java +++ b/service/soap/src/model/Book.java @@ -43,7 +43,7 @@ public class Book implements Serializable { this.title = STRING_UNDEFINED; this.author = STRING_UNDEFINED; this.description = STRING_UNDEFINED; - this.image = "https://almasoem.sch.id/wp-content/uploads/2016/02/IMG_9738-Large-1024x683.jpg"; + this.image = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/No_picture_available.png/401px-No_picture_available.png"; this.price = -1; } diff --git a/service/soap/src/service/BookService.java b/service/soap/src/service/BookService.java index 0feef7496cc76ac303e905ed7448e3b6bd8d0727..62fa029dd1785c2c64dea5e8f3964c150f387657 100644 --- a/service/soap/src/service/BookService.java +++ b/service/soap/src/service/BookService.java @@ -22,7 +22,7 @@ public interface BookService { Book getRecommendation(String... categories); @WebMethod - long buyBook(String idBuku, Integer jumlah, String norek); + long buyBook(String idBuku, Integer jumlah, String norek, String token); @WebMethod Book getBookDetailByID(String idBuku) throws SQLException; diff --git a/service/soap/src/service/BookServiceImpl.java b/service/soap/src/service/BookServiceImpl.java index b2d638ae2c0ed44e3908d22a8dc5bc9bce9bc13a..ce4c69c04cc12ed03951e23beadc6adbca1c1eac 100644 --- a/service/soap/src/service/BookServiceImpl.java +++ b/service/soap/src/service/BookServiceImpl.java @@ -76,7 +76,7 @@ public class BookServiceImpl implements BookService { } @Override - public long buyBook(String idBuku, Integer jumlah, String norek) { + public long buyBook(String idBuku, Integer jumlah, String norek, String token) { BookRepository bookRepository = new BookRepository(); bookRepository.connect(); @@ -92,7 +92,7 @@ public class BookServiceImpl implements BookService { return -1; } - if (WebServiceBankApi.transfer(norek, BookServiceImpl.norek_juragan, (float) (jumlah*harga.getHarga()))) { + if (WebServiceBankApi.transfer(norek, BookServiceImpl.norek_juragan, (float) (jumlah*harga.getHarga()), token)) { Book book = GoogleBooksApi.getBookDetailByID(idBuku); DaftarPenjualan penjualan = new DaftarPenjualan(book.getId(), book.getCategory(), jumlah); try { diff --git a/service/soap/src/service/WebServiceBankApi.java b/service/soap/src/service/WebServiceBankApi.java index 15e40c7bb83d64c30cd943c097b719e2d23b73a3..d747c4743fba5aeabb39e4350153398f3014d494 100644 --- a/service/soap/src/service/WebServiceBankApi.java +++ b/service/soap/src/service/WebServiceBankApi.java @@ -12,7 +12,7 @@ import java.net.URL; public class WebServiceBankApi { private static final String url = "http://localhost:3000/transfer"; - public static Boolean transfer(String pengirim, String penerima, Float jumlah) { + public static Boolean transfer(String pengirim, String penerima, Float jumlah, String token) { URL object = null; try { object = new URL(WebServiceBankApi.url); @@ -39,6 +39,7 @@ public class WebServiceBankApi { request.put("no_pengirim", pengirim); request.put("no_penerima", penerima); request.put("jumlah", jumlah); + request.put("token", token); OutputStreamWriter wr = null; try { @@ -57,8 +58,7 @@ public class WebServiceBankApi { } if (HttpResult == HttpURLConnection.HTTP_OK) { return true; - } - else { + } else { return false; } diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000000000000000000000000000000000000..46ffb4ce11adee005348f0eb20d6189078f6ac8f --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ +<?php + +// autoload.php @generated by Composer + +require_once __DIR__ . '/composer/autoload_real.php'; + +return ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c::getLoader(); diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000000000000000000000000000000000000..fce8549f0781bafdc7da2301b84d048286757445 --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,445 @@ +<?php + +/* + * This file is part of Composer. + * + * (c) Nils Adermann <naderman@naderman.de> + * Jordi Boggiano <j.boggiano@seld.be> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Jordi Boggiano <j.boggiano@seld.be> + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f27399a042d95c4708af3a8c74d35d338763cf8f --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000000000000000000000000000000000000..7a91153b0d8ea10bc693176a81d8a9eb96883a76 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ +<?php + +// autoload_classmap.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000000000000000000000000000000000000..b7fc0125dbca56fd7565ad62097672a59473e64e --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ +<?php + +// autoload_namespaces.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( +); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php new file mode 100644 index 0000000000000000000000000000000000000000..c57cc2d8fb15c2c170b30ca1e7176b7b427ac591 --- /dev/null +++ b/vendor/composer/autoload_psr4.php @@ -0,0 +1,11 @@ +<?php + +// autoload_psr4.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( + 'Sonata\\GoogleAuthenticator\\' => array($vendorDir . '/sonata-project/google-authenticator/src'), + 'Google\\Authenticator\\' => array($vendorDir . '/sonata-project/google-authenticator/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000000000000000000000000000000000000..6cd5c1f9cc557f31c8346d95f0dfe9cea48f8d7a --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,52 @@ +<?php + +// autoload_real.php @generated by Composer + +class ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c +{ + private static $loader; + + public static function loadClassLoader($class) + { + if ('Composer\Autoload\ClassLoader' === $class) { + require __DIR__ . '/ClassLoader.php'; + } + } + + public static function getLoader() + { + if (null !== self::$loader) { + return self::$loader; + } + + spl_autoload_register(array('ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c', 'loadClassLoader'), true, true); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + spl_autoload_unregister(array('ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c', 'loadClassLoader')); + + $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + return $loader; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000000000000000000000000000000000000..20899b7474c2f76f372ce0ab6b481a0fb5ceb782 --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,39 @@ +<?php + +// autoload_static.php @generated by Composer + +namespace Composer\Autoload; + +class ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c +{ + public static $prefixLengthsPsr4 = array ( + 'S' => + array ( + 'Sonata\\GoogleAuthenticator\\' => 27, + ), + 'G' => + array ( + 'Google\\Authenticator\\' => 21, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Sonata\\GoogleAuthenticator\\' => + array ( + 0 => __DIR__ . '/..' . '/sonata-project/google-authenticator/src', + ), + 'Google\\Authenticator\\' => + array ( + 0 => __DIR__ . '/..' . '/sonata-project/google-authenticator/src', + ), + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::$prefixDirsPsr4; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000000000000000000000000000000000000..b6b2711fd134f8028c4db588065cf44c51ab00ff --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,61 @@ +[ + { + "name": "sonata-project/google-authenticator", + "version": "2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sonata-project/GoogleAuthenticator.git", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.0" + }, + "time": "2018-07-18T22:08:02+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Google\\Authenticator\\": "src/", + "Sonata\\GoogleAuthenticator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Stocker", + "email": "me@chregu.tv" + }, + { + "name": "Andre DeMarre", + "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989" + }, + { + "name": "Thomas Rabaix", + "email": "thomas.rabaix@gmail.com" + } + ], + "description": "Library to integrate Google Authenticator into a PHP project", + "homepage": "https://github.com/sonata-project/GoogleAuthenticator", + "keywords": [ + "google authenticator" + ] + } +] diff --git a/vendor/sonata-project/google-authenticator/LICENSE b/vendor/sonata-project/google-authenticator/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..0135e0b6aebb4addef7246658ac1af1e56ba9b53 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2010 Thomas Rabaix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/sonata-project/google-authenticator/Makefile b/vendor/sonata-project/google-authenticator/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4ae0c63d810bcc5615b7da192ff0145a5a7e72c8 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/Makefile @@ -0,0 +1,60 @@ +# DO NOT EDIT THIS FILE! +# +# It's auto-generated by sonata-project/dev-kit package. + +all: + @echo "Please choose a task." +.PHONY: all + +lint: lint-composer lint-yaml lint-composer lint-xml lint-php +.PHONY: lint + +lint-composer: + composer validate +.PHONY: lint-composer + +lint-yaml: + yaml-lint --ignore-non-yaml-files --quiet --exclude vendor . + +.PHONY: lint-yaml + +lint-xml: + find . \( -name '*.xml' -or -name '*.xliff' \) \ + -not -path './vendor/*' \ + -not -path './src/Resources/public/vendor/*' \ + | while read xmlFile; \ + do \ + XMLLINT_INDENT=' ' xmllint --encode UTF-8 --format "$$xmlFile"|diff - "$$xmlFile"; \ + if [ $$? -ne 0 ] ;then exit 1; fi; \ + done + +.PHONY: lint-xml + +lint-php: + php-cs-fixer fix --ansi --verbose --diff --dry-run +.PHONY: lint-php + +cs-fix: cs-fix-php cs-fix-xml +.PHONY: cs-fix + +cs-fix-php: + php-cs-fixer fix --verbose +.PHONY: cs-fix-php + +cs-fix-xml: + find . \( -name '*.xml' -or -name '*.xliff' \) \ + -not -path './vendor/*' \ + -not -path './src/Resources/public/vendor/*' \ + | while read xmlFile; \ + do \ + XMLLINT_INDENT=' ' xmllint --encode UTF-8 --format "$$xmlFile" --output "$$xmlFile"; \ + done +.PHONY: cs-fix-xml + +test: + phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml +.PHONY: test + +docs: + cd docs && sphinx-build -W -b html -d _build/doctrees . _build/html +.PHONY: docs diff --git a/vendor/sonata-project/google-authenticator/composer.json b/vendor/sonata-project/google-authenticator/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..4fbb160acfb53ae77238e3a9b848ea7c7ae8277c --- /dev/null +++ b/vendor/sonata-project/google-authenticator/composer.json @@ -0,0 +1,50 @@ +{ + "name": "sonata-project/google-authenticator", + "type": "library", + "description": "Library to integrate Google Authenticator into a PHP project", + "keywords": [ + "google authenticator" + ], + "homepage": "https://github.com/sonata-project/GoogleAuthenticator", + "license": "MIT", + "authors": [ + { + "name": "Thomas Rabaix", + "email": "thomas.rabaix@gmail.com" + }, + { + "name": "Christian Stocker", + "email": "me@chregu.tv" + }, + { + "name": "Andre DeMarre", + "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989" + } + ], + "require": { + "php": "^7.1" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.0" + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Google\\Authenticator\\": "src/", + "Sonata\\GoogleAuthenticator\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Google\\Authenticator\\Tests\\": "tests/", + "Sonata\\GoogleAuthenticator\\Tests\\": "tests/" + } + } +} diff --git a/vendor/sonata-project/google-authenticator/phpunit.xml.dist b/vendor/sonata-project/google-authenticator/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..492b036db0eb7cfd428de82bdf6e1303b56eefec --- /dev/null +++ b/vendor/sonata-project/google-authenticator/phpunit.xml.dist @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- +DO NOT EDIT THIS FILE! + +It's auto-generated by sonata-project/dev-kit package. +--> + +<phpunit backupGlobals="false" + backupStaticAttributes="false" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false" + bootstrap="tests/bootstrap.php" +> + <testsuites> + <testsuite name="Sonata Google Authenticator Test Suite"> + <directory suffix="Test.php">./tests/</directory> + </testsuite> + </testsuites> + + <listeners> + <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" /> + </listeners> + + <filter> + <whitelist> + <directory suffix=".php">./src/</directory> + </whitelist> + </filter> + + <php> + <ini name="precision" value="8"/> + </php> + +</phpunit> diff --git a/vendor/sonata-project/google-authenticator/sample/example.php b/vendor/sonata-project/google-authenticator/sample/example.php new file mode 100644 index 0000000000000000000000000000000000000000..50366d2fa9c470a34eb2474568393a4642a0ad78 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/example.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +include_once __DIR__.'/../src/FixedBitNotation.php'; +include_once __DIR__.'/../src/GoogleAuthenticator.php'; +include_once __DIR__.'/../src/GoogleQrUrl.php'; + +$secret = 'XVQ2UIGO75XRUKJO'; +$code = '846474'; + +$g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator(); + +echo 'Current Code is: '; +echo $g->getCode($secret); + +echo "\n"; + +echo "Check if $code is valid: "; + +if ($g->checkCode($secret, $code)) { + echo "YES \n"; +} else { + echo "NO \n"; +} + +$secret = $g->generateSecret(); +echo "Get a new Secret: $secret \n"; +echo "The QR Code for this secret (to scan with the Google Authenticator App: \n"; + +echo \Sonata\GoogleAuthenticator\GoogleQrUrl::generate('chregu', $secret, 'GoogleAuthenticatorExample'); +echo "\n"; diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php b/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php new file mode 100644 index 0000000000000000000000000000000000000000..f3e06d477e22f323426f97ae46c67be8b7a2efba --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php @@ -0,0 +1,23 @@ + +<h1>please otp</h1> +<p> +<form method="post" action="./"> +<?php if ($debug) { + ?> + <br/> + (Set $debug in index.php to false, if you don't want to have the OTP prefilled (for real life application, for example ;))<br/> +<?php +} +?> + +otp: <input name="otp" +value="<?php +if ($debug) { + $g = new GoogleAuthenticator(); + echo $g->getCode($user->getSecret()); +} +?>"/><br/> +<input type="checkbox" name="remember" id="remember" /><label for="remember"> Remember verification for this computer for 1 day.</label> <br/> +<input type="submit"/> + +</form> diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php b/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php new file mode 100644 index 0000000000000000000000000000000000000000..2a19032ca4f1527b6e49c0128c4103799746e547 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php @@ -0,0 +1,19 @@ + +<p> +Hello <?php echo $user->getUsername(); ?> +</p> +<?php +if (!isset($_GET['showqr'])) { + ?> + +<p> +<a href="?showqr=1">Show QR Code</a> +</p> + +<?php +} +?> + +<p> +<a href="?logout=1">Logout</a> +</p> diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php b/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php new file mode 100644 index 0000000000000000000000000000000000000000..8d23fd33c7bd09a636c365d3c56e91fa70f08c59 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php @@ -0,0 +1,6 @@ +<p> +Wrong username or password or token. +</p> +<p> +<a href="./">try again</a> +</p> diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/login.php b/vendor/sonata-project/google-authenticator/sample/tmpl/login.php new file mode 100644 index 0000000000000000000000000000000000000000..fd81623009b4b8620b6d3c8e21111c93df2c182c --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/tmpl/login.php @@ -0,0 +1,8 @@ + +<h1>please login</h1> +<p> +<form method="post" action="./"> +username: <input name="username"/><br/> +password: <input name="password" type="password"/><br/> +<input type="submit"/> +</form> diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php b/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php new file mode 100644 index 0000000000000000000000000000000000000000..774a298deed8458831ed231cb0680441223a4d0a --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php @@ -0,0 +1,11 @@ +<h1>Please scan this </h1> + +<p> with <a href="http://www.google.com/support/a/bin/answer.py?hl=en&answer=1037451">the Google Authenticator App</a></p> + +<p> +<?php +$link = \Sonata\GoogleAuthenticator\GoogleQrUrl::generate($user->getUsername(), $secret, 'GoogleAuthenticatorExample'); +?> + +<a href="<?php echo $link; ?>"><img style="border: 0; padding:10px" src="<?php echo $link; ?>"/></a> +</p> diff --git a/vendor/sonata-project/google-authenticator/sample/users.dat b/vendor/sonata-project/google-authenticator/sample/users.dat new file mode 100644 index 0000000000000000000000000000000000000000..fdcc130c43b31f2d7b337b977705a164826b9688 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/users.dat @@ -0,0 +1 @@ +{"chregu":{"password":"foobar"}} \ No newline at end of file diff --git a/vendor/sonata-project/google-authenticator/sample/web/Users.php b/vendor/sonata-project/google-authenticator/sample/web/Users.php new file mode 100644 index 0000000000000000000000000000000000000000..fb169def9245eae78279bf2c6a7357c6fc5e71a8 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/web/Users.php @@ -0,0 +1,155 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class Users +{ + public function __construct(string $file = '../users.dat') + { + $this->userFile = $file; + + $this->users = json_decode(file_get_contents($file), true); + } + + public function hasSession() + { + session_start(); + if (isset($_SESSION['username'])) { + return $_SESSION['username']; + } + + return false; + } + + public function storeData(User $user): void + { + $this->users[$user->getUsername()] = $user->getData(); + file_put_contents($this->userFile, json_encode($this->users)); + } + + public function loadUser($name) + { + if (isset($this->users[$name])) { + return new User($name, $this->users[$name]); + } + + return false; + } +} + +class User +{ + public function __construct($user, $data) + { + $this->data = $data; + $this->user = $user; + } + + public function auth($pass) + { + if ($this->data['password'] === $pass) { + return true; + } + + return false; + } + + public function startSession(): void + { + $_SESSION['username'] = $this->user; + } + + public function doLogin(): void + { + session_regenerate_id(); + $_SESSION['loggedin'] = true; + $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT']; + } + + public function doOTP(): void + { + $_SESSION['OTP'] = true; + } + + public function isOTP() + { + if (isset($_SESSION['OTP']) && true == $_SESSION['OTP']) { + return true; + } + + return false; + } + + public function isLoggedIn() + { + if (isset($_SESSION['loggedin']) && true == $_SESSION['loggedin'] && + isset($_SESSION['ua']) && $_SESSION['ua'] == $_SERVER['HTTP_USER_AGENT'] + ) { + return $_SESSION['username']; + } + + return false; + } + + public function getUsername() + { + return $this->user; + } + + public function getSecret() + { + if (isset($this->data['secret'])) { + return $this->data['secret']; + } + + return false; + } + + public function generateSecret() + { + $g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator(); + $secret = $g->generateSecret(); + $this->data['secret'] = $secret; + + return $secret; + } + + public function getData() + { + return $this->data; + } + + public function setOTPCookie(): void + { + $time = floor(time() / (3600 * 24)); // get day number + //about using the user agent: It's easy to fake it, but it increases the barrier for stealing and reusing cookies nevertheless + // and it doesn't do any harm (except that it's invalid after a browser upgrade, but that may be even intented) + $cookie = $time.':'.hash_hmac('sha1', $this->getUsername().':'.$time.':'.$_SERVER['HTTP_USER_AGENT'], $this->getSecret()); + setcookie('otp', $cookie, time() + (30 * 24 * 3600), null, null, null, true); + } + + public function hasValidOTPCookie() + { + // 0 = tomorrow it is invalid + $daysUntilInvalid = 0; + $time = (string) floor((time() / (3600 * 24))); // get day number + if (isset($_COOKIE['otp'])) { + list($otpday, $hash) = explode(':', $_COOKIE['otp']); + + if ($otpday >= $time - $daysUntilInvalid && $hash == hash_hmac('sha1', $this->getUsername().':'.$otpday.':'.$_SERVER['HTTP_USER_AGENT'], $this->getSecret())) { + return true; + } + } + + return false; + } +} diff --git a/vendor/sonata-project/google-authenticator/sample/web/index.php b/vendor/sonata-project/google-authenticator/sample/web/index.php new file mode 100644 index 0000000000000000000000000000000000000000..c039674c53f53691340e7c3a6f4bb1c236d94335 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/sample/web/index.php @@ -0,0 +1,119 @@ +<?php declare(strict_types=1); +ob_start(); //i'm too lazy to check when is sent what ;) +//set session cookie to be read only via http and not by JavaScript +ini_set('session.cookie_httponly', '1'); + +include_once __DIR__.'/../../src/GoogleAuthenticator.php'; +include_once __DIR__.'/../../src/GoogleQrUrl.php'; +include_once __DIR__.'/../../src/FixedBitNotation.php'; +include_once 'Users.php'; + +?> +<!DOCTYPE HTML> +<html> +<head> + <title>Google Authenticator in PHP demo</title> +</head> +<body> +<?php + +//set this to false, if you don't want the token prefilled +$debug = true; + +$users = new Users(); +//check if the user has a session, if not, show the login screen +if ($username = $users->hasSession()) { + //load the user data from the json storage. + $user = $users->loadUser($username); + //if he clicked logout, destroy the session and redirect to the startscreen. + if (isset($_GET['logout'])) { + session_destroy(); + header('Location: ./'); + } + // check if the user is logged in. + if ($user->isLoggedIn()) { + include __DIR__.'/../tmpl/loggedin.php'; + //show the QR code if whished so + if (isset($_GET['showqr'])) { + $secret = $user->getSecret(); + include __DIR__.'/../tmpl/show-qr.php'; + } + } + //if the user is in the OTP phase and submit the OTP. + else { + if ($user->isOTP() && isset($_POST['otp'])) { + $g = new \Google\Authenticator\GoogleAuthenticator(); + // check if the submitted token is the right one and log in + if ($g->checkCode($user->getSecret(), $_POST['otp'])) { + // do log-in the user + $user->doLogin(); + //if the user clicked the "remember the token" checkbox, set the cookie + if (isset($_POST['remember']) && $_POST['remember']) { + $user->setOTPCookie(); + } + include __DIR__.'/../tmpl/loggedin.php'; + } + //if the OTP is wrong, destroy the session and tell the user to try again + else { + session_destroy(); + include __DIR__.'/../tmpl/login-error.php'; + } + } + // if the user is neither logged in nor in the OTP phase, show the login form + else { + session_destroy(); + include __DIR__.'/../tmpl/login.php'; + } + } + die(); +} + //if the username is set in _POST, then we assume the user filled in the login form. + + if (isset($_POST['username'])) { + // check if we can load the user (ie. the user exists in our db) + $user = $users->loadUser($_POST['username']); + if ($user) { + //try to authenticate the password and start the session if it's correct. + if ($user->auth($_POST['password'])) { + $user->startSession(); + //check if the user has a valid OTP cookie, so we don't have to + // ask for the current token and can directly log in + if ($user->hasValidOTPCookie()) { + include __DIR__.'/../tmpl/loggedin.php'; + $user->doLogin(); + } + // try to get the users' secret from the db, + // if he doesn't have one, generate one, store it and show it. + else { + if (!$user->getSecret()) { + include __DIR__.'/../tmpl/loggedin.php'; + + $secret = $user->generateSecret(); + $users->storeData($user); + $user->doLogin(); + include __DIR__.'/../tmpl/show-qr.php'; + } + // if the user neither has a valid OTP cookie nor it's the first login + // ask for the OTP + else { + $user->doOTP(); + include __DIR__.'/../tmpl/ask-for-otp.php'; + } + } + + die(); + } + } + // if we're here, something went wrong, destroy the session and show a login error + session_destroy(); + + include __DIR__.'/../tmpl/login-error.php'; + die(); + } + +// if neither a session nor tried to submit the login credentials -> login screen +include __DIR__.'/../tmpl/login.php'; + +?> +</body> +</html> diff --git a/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php b/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php new file mode 100644 index 0000000000000000000000000000000000000000..3f5ff558b05e8a721dc4589796154bcd2d65d574 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php @@ -0,0 +1,296 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\GoogleAuthenticator; + +/** + * FixedBitNotation. + * + * The FixedBitNotation class is for binary to text conversion. It + * can handle many encoding schemes, formally defined or not, that + * use a fixed number of bits to encode each character. + * + * @author Andre DeMarre + */ +final class FixedBitNotation +{ + /** + * @var string + */ + private $chars; + + /** + * @var int + */ + private $bitsPerCharacter; + + /** + * @var int + */ + private $radix; + + /** + * @var bool + */ + private $rightPadFinalBits; + + /** + * @var bool + */ + private $padFinalGroup; + + /** + * @var string + */ + private $padCharacter; + + /** + * @var string[] + */ + private $charmap; + + /** + * @param int $bitsPerCharacter Bits to use for each encoded character + * @param string $chars Base character alphabet + * @param bool $rightPadFinalBits How to encode last character + * @param bool $padFinalGroup Add padding to end of encoded output + * @param string $padCharacter Character to use for padding + */ + public function __construct(int $bitsPerCharacter, string $chars = null, bool $rightPadFinalBits = false, bool $padFinalGroup = false, string $padCharacter = '=') + { + // Ensure validity of $chars + if (!is_string($chars) || ($charLength = strlen($chars)) < 2) { + $chars = + '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,'; + $charLength = 64; + } + + // Ensure validity of $bitsPerCharacter + if ($bitsPerCharacter < 1) { + // $bitsPerCharacter must be at least 1 + $bitsPerCharacter = 1; + $radix = 2; + } elseif ($charLength < 1 << $bitsPerCharacter) { + // Character length of $chars is too small for $bitsPerCharacter + // Set $bitsPerCharacter to greatest acceptable value + $bitsPerCharacter = 1; + $radix = 2; + + while ($charLength >= ($radix <<= 1) && $bitsPerCharacter < 8) { + ++$bitsPerCharacter; + } + + $radix >>= 1; + } elseif ($bitsPerCharacter > 8) { + // $bitsPerCharacter must not be greater than 8 + $bitsPerCharacter = 8; + $radix = 256; + } else { + $radix = 1 << $bitsPerCharacter; + } + + $this->chars = $chars; + $this->bitsPerCharacter = $bitsPerCharacter; + $this->radix = $radix; + $this->rightPadFinalBits = $rightPadFinalBits; + $this->padFinalGroup = $padFinalGroup; + $this->padCharacter = $padCharacter[0]; + } + + /** + * Encode a string. + * + * @param string $rawString Binary data to encode + * + * @return string + */ + public function encode($rawString): string + { + // Unpack string into an array of bytes + $bytes = unpack('C*', $rawString); + $byteCount = count($bytes); + + $encodedString = ''; + $byte = array_shift($bytes); + $bitsRead = 0; + + $chars = $this->chars; + $bitsPerCharacter = $this->bitsPerCharacter; + $rightPadFinalBits = $this->rightPadFinalBits; + $padFinalGroup = $this->padFinalGroup; + $padCharacter = $this->padCharacter; + + // Generate encoded output; + // each loop produces one encoded character + for ($c = 0; $c < $byteCount * 8 / $bitsPerCharacter; ++$c) { + // Get the bits needed for this encoded character + if ($bitsRead + $bitsPerCharacter > 8) { + // Not enough bits remain in this byte for the current + // character + // Save the remaining bits before getting the next byte + $oldBitCount = 8 - $bitsRead; + $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount); + $newBitCount = $bitsPerCharacter - $oldBitCount; + + if (!$bytes) { + // Last bits; match final character and exit loop + if ($rightPadFinalBits) { + $oldBits <<= $newBitCount; + } + $encodedString .= $chars[$oldBits]; + + if ($padFinalGroup) { + // Array of the lowest common multiples of + // $bitsPerCharacter and 8, divided by 8 + $lcmMap = [1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1]; + $bytesPerGroup = $lcmMap[$bitsPerCharacter]; + $pads = $bytesPerGroup * 8 / $bitsPerCharacter + - ceil((strlen($rawString) % $bytesPerGroup) + * 8 / $bitsPerCharacter); + $encodedString .= str_repeat($padCharacter[0], $pads); + } + + break; + } + + // Get next byte + $byte = array_shift($bytes); + $bitsRead = 0; + } else { + $oldBitCount = 0; + $newBitCount = $bitsPerCharacter; + } + + // Read only the needed bits from this byte + $bits = $byte >> 8 - ($bitsRead + $newBitCount); + $bits ^= $bits >> $newBitCount << $newBitCount; + $bitsRead += $newBitCount; + + if ($oldBitCount) { + // Bits come from seperate bytes, add $oldBits to $bits + $bits = ($oldBits << $newBitCount) | $bits; + } + + $encodedString .= $chars[$bits]; + } + + return $encodedString; + } + + /** + * Decode a string. + * + * @param string $encodedString Data to decode + * @param bool $caseSensitive + * @param bool $strict Returns null if $encodedString contains + * an undecodable character + * + * @return string + */ + public function decode($encodedString, $caseSensitive = true, $strict = false): string + { + if (!$encodedString || !is_string($encodedString)) { + // Empty string, nothing to decode + return ''; + } + + $chars = $this->chars; + $bitsPerCharacter = $this->bitsPerCharacter; + $radix = $this->radix; + $rightPadFinalBits = $this->rightPadFinalBits; + $padCharacter = $this->padCharacter; + + // Get index of encoded characters + if ($this->charmap) { + $charmap = $this->charmap; + } else { + $charmap = []; + + for ($i = 0; $i < $radix; ++$i) { + $charmap[$chars[$i]] = $i; + } + + $this->charmap = $charmap; + } + + // The last encoded character is $encodedString[$lastNotatedIndex] + $lastNotatedIndex = strlen($encodedString) - 1; + + // Remove trailing padding characters + while ($encodedString[$lastNotatedIndex] == $padCharacter[0]) { + $encodedString = substr($encodedString, 0, $lastNotatedIndex); + --$lastNotatedIndex; + } + + $rawString = ''; + $byte = 0; + $bitsWritten = 0; + + // Convert each encoded character to a series of unencoded bits + for ($c = 0; $c <= $lastNotatedIndex; ++$c) { + if (!isset($charmap[$encodedString[$c]]) && !$caseSensitive) { + // Encoded character was not found; try other case + if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) { + $charmap[$encodedString[$c]] = $charmap[$cUpper]; + } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) { + $charmap[$encodedString[$c]] = $charmap[$cLower]; + } + } + + if (isset($charmap[$encodedString[$c]])) { + $bitsNeeded = 8 - $bitsWritten; + $unusedBitCount = $bitsPerCharacter - $bitsNeeded; + + // Get the new bits ready + if ($bitsNeeded > $bitsPerCharacter) { + // New bits aren't enough to complete a byte; shift them + // left into position + $newBits = $charmap[$encodedString[$c]] << $bitsNeeded + - $bitsPerCharacter; + $bitsWritten += $bitsPerCharacter; + } elseif ($c != $lastNotatedIndex || $rightPadFinalBits) { + // Zero or more too many bits to complete a byte; + // shift right + $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount; + $bitsWritten = 8; //$bitsWritten += $bitsNeeded; + } else { + // Final bits don't need to be shifted + $newBits = $charmap[$encodedString[$c]]; + $bitsWritten = 8; + } + + $byte |= $newBits; + + if (8 == $bitsWritten || $c == $lastNotatedIndex) { + // Byte is ready to be written + $rawString .= pack('C', $byte); + + if ($c != $lastNotatedIndex) { + // Start the next byte + $bitsWritten = $unusedBitCount; + $byte = ($charmap[$encodedString[$c]] + ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten; + } + } + } elseif ($strict) { + // Unable to decode character; abort + return null; + } + } + + return $rawString; + } +} + +// NEXT_MAJOR: Remove class alias +class_alias('Sonata\GoogleAuthenticator\FixedBitNotation', 'Google\Authenticator\FixedBitNotation', false); diff --git a/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php new file mode 100644 index 0000000000000000000000000000000000000000..145739c54bc369c59d8863649968cb5c980b3230 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php @@ -0,0 +1,173 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\GoogleAuthenticator; + +/** + * @see https://github.com/google/google-authenticator/wiki/Key-Uri-Format + */ +final class GoogleAuthenticator implements GoogleAuthenticatorInterface +{ + /** + * @var int + */ + private $passCodeLength; + + /** + * @var int + */ + private $secretLength; + + /** + * @var int + */ + private $pinModulo; + + /** + * @var \DateTimeInterface + */ + private $now; + + /** + * @var int + */ + private $codePeriod = 30; + + /** + * @param int $passCodeLength + * @param int $secretLength + * @param \DateTimeInterface|null $now + */ + public function __construct(int $passCodeLength = 6, int $secretLength = 10, \DateTimeInterface $now = null) + { + $this->passCodeLength = $passCodeLength; + $this->secretLength = $secretLength; + $this->pinModulo = 10 ** $passCodeLength; + $this->now = $now ?? new \DateTimeImmutable(); + } + + /** + * @param string $secret + * @param string $code + */ + public function checkCode($secret, $code): bool + { + /** + * The result of each comparison is accumulated here instead of using a guard clause + * (https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html). This is to implement + * constant time comparison to make side-channel attacks harder. See + * https://cryptocoding.net/index.php/Coding_rules#Compare_secret_strings_in_constant_time for details. + * Each comparison uses hash_equals() instead of an operator to implement constant time equality comparison + * for each code. + */ + $result = 0; + + // current period + $result += hash_equals($this->getCode($secret, $this->now), $code); + + // previous period, happens if the user was slow to enter or it just crossed over + $dateTime = new \DateTimeImmutable('@'.($this->now->getTimestamp() - $this->codePeriod)); + $result += hash_equals($this->getCode($secret, $dateTime), $code); + + // next period, happens if the user is not completely synced and possibly a few seconds ahead + $dateTime = new \DateTimeImmutable('@'.($this->now->getTimestamp() + $this->codePeriod)); + $result += hash_equals($this->getCode($secret, $dateTime), $code); + + return $result > 0; + } + + /** + * NEXT_MAJOR: add the interface typehint to $time and remove deprecation. + * + * @param string $secret + * @param float|string|int|null|\DateTimeInterface $time + */ + public function getCode($secret, /* \DateTimeInterface */$time = null): string + { + if (null === $time) { + $time = $this->now; + } + + if ($time instanceof \DateTimeInterface) { + $timeForCode = floor($time->getTimestamp() / $this->codePeriod); + } else { + @trigger_error( + 'Passing anything other than null or a DateTimeInterface to $time is deprecated as of 2.0 '. + 'and will not be possible as of 3.0.', + E_USER_DEPRECATED + ); + $timeForCode = $time; + } + + $base32 = new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', true, true); + $secret = $base32->decode($secret); + + $timeForCode = str_pad(pack('N', $timeForCode), 8, chr(0), STR_PAD_LEFT); + + $hash = hash_hmac('sha1', $timeForCode, $secret, true); + $offset = ord(substr($hash, -1)); + $offset &= 0xF; + + $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF; + + return str_pad((string) ($truncatedHash % $this->pinModulo), $this->passCodeLength, '0', STR_PAD_LEFT); + } + + /** + * NEXT_MAJOR: Remove this method. + * + * @param string $user + * @param string $hostname + * @param string $secret + * + * @deprecated deprecated as of 2.1 and will be removed in 3.0. Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead. + */ + public function getUrl($user, $hostname, $secret): string + { + @trigger_error(sprintf( + 'Using %s() is deprecated as of 2.1 and will be removed in 3.0. '. + 'Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead.', + __METHOD__ + ), E_USER_DEPRECATED); + + $issuer = func_get_args()[3] ?? null; + $accountName = sprintf('%s@%s', $user, $hostname); + + // manually concat the issuer to avoid a change in URL + $url = GoogleQrUrl::generate($accountName, $secret); + + if ($issuer) { + $url .= '%26issuer%3D'.$issuer; + } + + return $url; + } + + public function generateSecret(): string + { + return (new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', true, true)) + ->encode(random_bytes($this->secretLength)); + } + + /** + * @param string $bytes + * @param int $start + */ + private function hashToInt(string $bytes, int $start): int + { + return unpack('N', substr(substr($bytes, $start), 0, 4))[1]; + } +} + +// NEXT_MAJOR: Remove class alias +class_alias('Sonata\GoogleAuthenticator\GoogleAuthenticator', 'Google\Authenticator\GoogleAuthenticator', false); diff --git a/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..70219f2090524c8549410973634cc5d7c06fe05a --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php @@ -0,0 +1,44 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\GoogleAuthenticator; + +interface GoogleAuthenticatorInterface +{ + /** + * @param string $secret + * @param string $code + */ + public function checkCode($secret, $code): bool; + + /** + * NEXT_MAJOR: add the interface typehint to $time and remove deprecation. + * + * @param string $secret + * @param float|string|int|null|\DateTimeInterface $time + */ + public function getCode($secret, /* \DateTimeInterface */$time = null): string; + + /** + * NEXT_MAJOR: Remove this method. + * + * @param string $user + * @param string $hostname + * @param string $secret + * + * @deprecated deprecated as of 2.1 and will be removed in 3.0. Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead. + */ + public function getUrl($user, $hostname, $secret): string; + + public function generateSecret(): string; +} diff --git a/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php b/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php new file mode 100644 index 0000000000000000000000000000000000000000..7e9a537a4aa63f764233da690e2dd40e14851ef9 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php @@ -0,0 +1,94 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\GoogleAuthenticator; + +/** + * Responsible for QR image url generation. + * + * @see https://developers.google.com/chart/infographics/docs/qr_codes + * @see https://github.com/google/google-authenticator/wiki/Key-Uri-Format + * + * @author Iltar van der Berg <kjarli@gmail.com> + */ +final class GoogleQrUrl +{ + /** + * Private by design. + */ + private function __construct() + { + } + + /** + * Generates a URL that is used to show a QR code. + * + * Account names may not contain a double colon (:). Valid account name + * examples: + * - "John.Doe@gmail.com" + * - "John Doe" + * - "John_Doe_976" + * + * The Issuer may not contain a double colon (:). The issuer is recommended + * to pass along. If used, it will also be appended before the accountName. + * + * The previous examples with the issuer "Acme inc" would result in label: + * - "Acme inc:John.Doe@gmail.com" + * - "Acme inc:John Doe" + * - "Acme inc:John_Doe_976" + * + * The contents of the label, issuer and secret will be encoded to generate + * a valid URL. + * + * @param string $accountName The account name to show and identify + * @param string $secret The secret is the generated secret unique to that user + * @param string|null $issuer Where you log in to + * @param int $size Image size in pixels, 200 will make it 200x200 + * + * @return string + */ + public static function generate(string $accountName, string $secret, string $issuer = null, int $size = 200): string + { + if ('' === $accountName || false !== strpos($accountName, ':')) { + throw RuntimeException::InvalidAccountName($accountName); + } + + if ('' === $secret) { + throw RuntimeException::InvalidSecret(); + } + + $label = $accountName; + $otpauthString = 'otpauth://totp/%s?secret=%s'; + + if (null !== $issuer) { + if ('' === $issuer || false !== strpos($issuer, ':')) { + throw RuntimeException::InvalidIssuer($issuer); + } + + // use both the issuer parameter and label prefix as recommended by Google for BC reasons + $label = $issuer.':'.$label; + $otpauthString .= '&issuer=%s'; + } + + $otpauthString = rawurlencode(sprintf($otpauthString, $label, $secret, $issuer)); + + return sprintf( + 'https://chart.googleapis.com/chart?chs=%1$dx%1$d&chld=M|0&cht=qr&chl=%2$s', + $size, + $otpauthString + ); + } +} + +// NEXT_MAJOR: Remove class alias +class_alias('Sonata\GoogleAuthenticator\GoogleQrUrl', 'Google\Authenticator\GoogleQrUrl', false); diff --git a/vendor/sonata-project/google-authenticator/src/RuntimeException.php b/vendor/sonata-project/google-authenticator/src/RuntimeException.php new file mode 100644 index 0000000000000000000000000000000000000000..4d3cf5f63f2d3137dd8e9bc7095e8410ff49414d --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/RuntimeException.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the Sonata Project package. + * + * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\GoogleAuthenticator; + +/** + * Contains runtime exception templates. + * + * @author Iltar van der Berg <kjarli@gmail.com> + */ +final class RuntimeException extends \RuntimeException +{ + public static function InvalidAccountName(string $accountName): self + { + return new self(sprintf( + 'The account name may not contain a double colon (:) and may not be an empty string. Given "%s".', + $accountName + )); + } + + public static function InvalidIssuer(string $issuer): self + { + return new self(sprintf( + 'The issuer name may not contain a double colon (:) and may not be an empty string. Given "%s".', + $issuer + )); + } + + public static function InvalidSecret(): self + { + return new self('The secret name may not be an empty string.'); + } +} + +// NEXT_MAJOR: Remove class alias +class_alias('Sonata\GoogleAuthenticator\RuntimeException', 'Google\Authenticator\RuntimeException', false);