или «Не нравится интерфейс от Cisco — сделай свой»
Беспроводные контроллеры 2500/ 5500 используются для управления точками доступа Cisco Aironet с прошивкой LWAPP в пределах корпоративной сети для обеспечения общей политики безопасности, гостевого доступа и поддерживают как стандартных компьютерных клиентов (ноутбуки, компьютеры, смартфоны), так и специализированные устройства с беспроводным доступом — ручные сканеры для торговых залов, беспроводные камеры наблюдения и т.д.
Не так давно, мне была поставлена задача организовать возможность выдачи гостевого доступа в интернет с использованием Cisco WLC. Доступ должен был выдавать наш «ресепшн» — то есть интерфейс должен быть максимально удобен и прост для людей далеких от IT. Само создание гостевого доступа должно было быть лишь частью процесса вместе с проверкой документов и выдачи временного бейджика и должно занимать не более 10 секунд.
В Cisco WLC для аутентификации пользователей есть возможность подключения внешнего RADIUS сервера (это может быть Cisco ISE или Windows NPS — но в нашем случае эти варианты отпадали) или воспользоваться локальной базой данных самого контроллера. Единственное ограничение локальной базы — это максимальное количество записей в базе: 2048.
В нашем случае, этого было более чем достаточно, и мы решили использовать возможности самого WLC. Для создания гостевых учетных записей можно создать специальный административный акаунт с ограниченными правами Lobby Admin (как видно из названия — предназначенный для целей сходных с нашими).
Почему нас не устроил стандартный Lobby Admin
Создав такой акаунт, мы решили посмотреть процесс создания гостевого юзера средствами Lobby Ambassador (так называется этот «урезанный» режим)
Шаг 1. Надо залогиниться — тут все понятно, надо ввести имя и пароль. В принципе, «ресепшн» может залогиниться в начале дня и не закрывать страницу, так что на скорость создания это влиять не будет
Шаг 2. Надо кликнуть на кнопку New
Шаг 3. Заполняем форму — здесь надо указать имя пользователя, сгенерировать пароль, указать время действия и выбрать сеть (гостевую)
Тут уже стало понятно, что в 10 секунд здесь никак не уложиться:
- Сгенерированный пароль показывается только в Javascript alert-e (в самой форме — звездочка) — чтобы распечатать, его надо или записывать на бумажке или делать скриншот
- Lifetime надо вписывать а не выбирать из списка
- Сеть надо выбирать каждый раз — невозможно установить default вариант
В итоге, стандартный интерфейс наш ресепшн не устроил, оно и понятно, UI в Cisco Web based приложениях традиционно не фонтан…
Как мы решили проблему
Достаточно просто, быстренько сваяли скрипт на PHP, коим и хотим поделиться
Прошу особо в код не вглядываться и не критиковать, человек пишет на PHP второй раз в жизни (и третий раз вообще программирует) — поэтому и вопросы стиля и security скрипта на рассматривались :).
<?php error_reporting(0); function generatePassword ($length = 8) { // start with a blank password $password = ""; // define possible characters - any character in this string can be // picked for use in the password, so if you want to put vowels back in // or add special characters such as exclamation marks, this is where // you should do it $possible = "2346789bcdfghjkmnpqrtvwxyzBCDFGHJKLMNPQRTVWXYZ"; // we refer to the length of $possible a few times, so let's grab it now $maxlength = strlen($possible); // check for length overflow and truncate if necessary if ($length > $maxlength) { $length = $maxlength; } // set up a counter for how many characters are in the password so far $i = 0; // add random characters to $password until $length is reached while ($i < $length) { // pick a random character from the possible ones $char = substr($possible, mt_rand(0, $maxlength-1), 1); // have we already used this character in $password? if (!strstr($password, $char)) { // no, so it's OK to add it onto the end of whatever we've already got... $password .= $char; // ... and increase the counter by one $i++; } } // done! return $password; } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Guest WIFI Access - Add a user</title> <script> function randomPassword(length) { chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; pass = ""; for(x=0;x<length;x++) { i = Math.floor(Math.random() * 62); pass += chars.charAt(i); } return pass; } </script> </head> <body > <div id="test-header" class="accordion_headings" >Guest WIFI Network </div><!--Heading of the accordion ( clicked to show n hide ) --> <!--Prefix of heading (the DIV above this) and content (the DIV below this) to be same... eg. foo-header & foo-content--> <div id="test2-content"><!--DIV which show/hide on click of header--> <p><br /> <? if ($_REQUEST["action"]=="send") { $headers = "MIME-Version: 1.0\n" ; $headers .= "Content-Type: text/html; charset=\"iso-8859-1\"\n"; $headers .= "Sensitivity: Personal\n"; $message= "<table width=466 border=0 cellpadding=0 cellspacing=0 bordercolor=#000000> <tr> <th colspan=2> Office Guest WIFI Access</th> </tr> <tr> <td width=128>Username</td> <td width=332><strong> ".$_REQUEST[User]." </strong></td> </tr> <tr> <td>Password</td> <td><strong> ".$_REQUEST[Pass]." </strong></td> </tr> <tr> <td>Life Time</td> <td><strong> ".$_REQUEST[life]." days <br /> <font size=-2> starting from ".$_REQUEST[Date]." </font></strong> </td> </tr> </table><br> Network Name is A_GUEST<br> <b>By using Office WIFI Guest network you agree to everything listed in our policy document</b>. <br> For any IT related issues call helpdesk"; $status = mail($_REQUEST["email"], "Access to Office WIFI guest network", $message,$headers); echo "<b>Info sent by Email"; } if ($_REQUEST["action"]=="submit") { $adduser="ok"; if (strlen($_REQUEST["User"])<2) { $adduser=""; $_REQUEST["action"]=""; $userermsg.="<br><font color=red>Username too short</font>"; } if (strlen($_REQUEST["Pass"])<2) { $adduser=""; $_REQUEST["action"]=""; $userermsg.="<br><font color=red>Password too short</font>"; } if ($adduser=="ok") { // Adding user $userermsg=""; $post = http_build_query(array( "buttonClicked" => "4", "userpwd" => $_REQUEST["Pass"] , "pwdconfirm" => $_REQUEST["Pass"], "lifetime_days" => $_REQUEST["life"], "lifetime_hours" => "0", "lifetime_mins" => "1", "lifetime_secs" => "1", "apply" => "apply", "description" => "Email:".$_REQUEST["email"]." - ".$_REQUEST["notes"], "GuestWlanID" => "0", "guest_roleselect_checkbox" => "0", "err_flag" => "0", "username" => $_REQUEST["User"] )); $context = stream_context_create(array("http"=>array( "method" => "POST", "header" => "Content-Type: application/x-www-form-urlencoded\r\n" . "Content-Length: ". strlen($post) . "\r\n", "content" => $post, ))); $page = file_get_contents("http://lobbyadmin:lobbypassword@10.24.32.61/screens/aaa/guestuser_create.html", true, $context); $usererr = strpos($page, 'ERROR: User Name', true); // As of PHP 5.3.0 if (intval($usererr)>1) { $_REQUEST["action"]=""; $userermsg.="<br><font color=red>User already exists! Please choose another name</font>"; } if ($userermsg=="") { //User created - give options - printout send by email ?> <script type="text/javascript"> var win=null; function printIt(printThis) { win = window.open(); win.focus(); win.document.open(); win.document.write('<'+'html'+'><'+'head'+'><'+'style'+'>'); win.document.write('body, td { font-family: Verdana; font-size: 10pt;} table { margin: 1em; border-collapse: collapse; } td, th { padding: .3em; border: 1px #ccc solid; }'); win.document.write('<'+'/'+'style'+'><'+'/'+'head'+'><'+'body'+'>'); win.document.write(printThis); win.document.write('By using Office WIFI Guest network you agree to everything listed in our policy document. <br> For any IT related issues call (+41)(022)(909) <b>5555</b> <'+'/'+'body'+'><'+'/'+'html'+'>'); win.document.close(); win.print(); win.close(); } </script> </p> <p class="style2">User has been successfully created </p> <table width="466" border="0" cellpadding="0" cellspacing="0" bordercolor="#000000"> <tr> <th colspan="2"> Office Guest WIFI Access</th> </tr> <tr> <td width="128">Username</td> <td width="332"><strong> <?=$_REQUEST["User"]?> </strong></td> </tr> <tr> <td>Password</td> <td><strong> <?=$_REQUEST["Pass"]?> </strong></td> </tr> <tr> <td>Life Time</td> <td><strong> <?=$_REQUEST["life"]?> days <br /> <font size="-2"> starting from <?=date("d/M/Y H:i:s")?> </font></strong> </td> </tr> </table> <br /> <a href="#" onclick="printIt(document.getElementById('printme').innerHTML); return false"> Print guest access leaflet </a> <? if (strlen($_REQUEST["email"])>5 ) { ?> <br /> <form action="index.php?action=send" method="post" id=sendemail> <input type=hidden name="Date" value="<?=date("d/M/Y H:i:s")?>"/> <input type=hidden name="User" value="<?=$_REQUEST["User"]?>"/> <input type=hidden value="<?=$_REQUEST["Pass"]?>" id="Pass" name="Pass" /> <input type=hidden name="life" id="life" size="5" value="<? if(intval($_REQUEST["life"]==0)) { echo 1; } else { echo $_REQUEST["life"]; } ?>" /> <input type=hidden name="email" id="email" value="<?=$_REQUEST["email"]?>" /><a href=# onclick="document.getElementById('sendemail').submit(); return false;" >Send guest access leaflet by email </a> </form> <? } ?> </p> <p> <style> table { margin: 1em; border-collapse: collapse; } td, th { padding: .3em; border: 1px #ccc solid; } </style> <div id="printme" style="display:none"> <table width="466" border="0" cellpadding="0" cellspacing="0" bordercolor="#000000"> <tr> <th colspan="2"> Office Guest WIFI Access</th> </tr> <tr> <td width="128">Username</td> <td width="332"><strong> <?=$_REQUEST["User"]?> </strong></td> </tr> <tr> <td>Password</td> <td><strong> <?=$_REQUEST["Pass"]?> </strong></td> </tr> <tr> <td>Life Time</td> <td><strong> <?=$_REQUEST["life"]?> days <br /> <font size="-2" > starting from <?=date("d/M/Y H:i:s")?> </font></strong></td> </tr> <tr> <td>Network Name <br /> (SSID)</td> <td><strong>A_GUEST</strong></td> </tr> <tr> <td colspan="2"><div align="center">Welcome to WiFi</div></td> </tr> </table> </div> <? } } } if ($_REQUEST["action"]=="") { if ($_REQUEST["Pass"]=="") { $_REQUEST["Pass"]=generatePassword(4); } ?> <form action="index.php" method="post" enctype="multipart/form-data"><?=$userermsg?> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td align="right">USERNAME</td> <td align="left"><input name=User value="<?=$_REQUEST["User"]?>"/> <input name="action" type="hidden" id="action" value="submit" /></td> </tr> <tr> <td align="right">PASSWORD</td> <td align="left"><input value="<?=$_REQUEST["Pass"]?>" id=Pass name=Pass /> [<a href=# onclick="document.getElementById('Pass').value=randomPassword(4); return false;">regenerate</a>] </td> </tr> <tr> <td align="right">LIFETIME*</td> <td align="left"> <input name="life" id="life" size="5" value="<? if(intval($_REQUEST["life"]==0)) { echo 1; } else { echo $_REQUEST["life"]; } ?>" /> <a href=# style="text-decoration:none" class="links" onclick="document.getElementById('life').value=parseInt(document.getElementById('life').value)+1; return false;">[+]</a> <a style="text-decoration:none" href=# class="links" onclick="if (parseInt(document.getElementById('life').value)>1){document.getElementById('life').value=parseInt(document.getElementById('life').value)-1; }return false;">[-]</a> <a style="text-decoration:none" href=# class="links" onclick=" document.getElementById('life').value=29;return false;">[month]</a> <a style="text-decoration:none" href=# class="links" onclick=" document.getElementById('life').value=0;return false;">[0]</a> </span></td> </tr> <tr> <td align="right"> </td> <td align="left">* days</td> </tr> <tr> <td align="right"> </td> <td align="left"> <input type="submit" value=Add /> </td> </tr> <tr> <td align="right" valign="top" > </td> <td align="left"> </td> </tr> <tr> <td align="right" valign="top" ><div align="center"></div></td> <td align="right" valign="top" ><div align="left"><strong>Optional information</strong></div></td> </tr> <tr> <td align="right" valign="top"> </td> <td align="left"> </td> </tr> <tr> <td align="right" valign="middle">Email</td> <td align="left"><input name="email" id="email" value="<?=$_REQUEST["email"]?>" /> <br /></td> </tr> <tr> <td align="right" valign="middle">Additional info</td> <td align="left"><input name="notes" type="text" id="notes" value="<?=$_REQUEST["notes"]?>" size="2" /> </td> </tr> <tr> <td align="right" valign="middle"> </td> <td align="left"><a href=# onclick="document.getElementById('notes').value='staff member'; return false;">SM</a> | <a href=# onclick="document.getElementById('notes').value='natcom user'; return false;">NC</a>| <a href=# onclick="document.getElementById('notes').value='consultant'; return false;">cons</a>| <a href=# onclick="document.getElementById('notes').value='field office user'; return false;">FO</a> | <a href=# onclick="document.getElementById('notes').value='partner company'; return false;">partn</a> </td> </tr> </table> <p align="right"> </p> <p><br /> <br /> </p> </form> <? } ?> </div> </div> <!--End of each accordion item--> <!--Start of each accordion item--> </div> </body> </html>
Принцип работы довольно простой, PHP генерирует все поля формы и посылает POST запрос на WLC
Доступ к скрипту нужно ограничить (например с помощью htpasswd). Мы это сделали с помощью mod_ntlm, что заодно избавило от необходимости вводить пароль — используется integrated authehtification
Также можно использовать curl и подключаться по https — в нашем случае это не критично, так как веб-сервер и WLC подключены между собой через изолированный management VLAN
Что получилось
Получился интерфейс, полностью устроивший персонал «ресепшена».
Вот что им надо сделать для создания гостевого акаунта:
1. Пройти по адресу (ярлык на рабочем столе)
2. Сразу открывается форма создания юзера (login не требуется, пароль прегенерирован)
3. Вписывается имя пользователя (здесь можно AJAX-ом проверять существует ли юзер с таким логином — мы пока это не реализовали)
4. В основном доступ выдается на день (что и выставлено default) — можно увеличить ссылочками + и — , или дать сразу на месяц или неделю
5. Засабмитить и распечатать
Больше 10 секунд это теперь не занимает!
ссылка на оригинал статьи http://habrahabr.ru/post/178985/
Добавить комментарий