Вам предстоит реализовать систему авторизации и контроля за действиями пользователей.
Форкните этот репозиторий себе и реализуйте функции, которые определены в файле auth-lib.js
. Затем залейте свой код.
В системе работают пользователи.
Пользователи поделены на группы, каждая из которых обладает ограниченным набором прав.
Если более формально:
Право - способность осуществить какое-либо действие в системе.
Группа - общность, определяемая набором прав.
Множество пользователей связано со множеством прав через множество групп.
Один пользователь может состоять в нескольких группах. В одной группе может состоять несколько пользователей.
Группа определяется набором прав. Одно право может принадлежать нескольким группам.
Таким образом между тремя сущностями: "пользователь", "группа", "право" - существуют связи типа "много-много".
Решите, как в вашей системе будут представлены пользователи, группы и права.
Список пользователей как массив объектов:
var allUsers = [ {nickname: "admin", password: "1234", groups: ["admin", "manager", "basic"]}, {nickname: "sobakajozhec", password: "ekh228", groups: ["basic", "manager"]}, {nickname: "patriot007", password: "russiaFTW", groups: ["basic"]} ];
Список прав как массив из строк:
var allRights = ["manage content", "play games", "delete users", "view site"];
Группы как объект, где ключи - имена групп, значения - массивы прав.
var allGroups = { "admin": [rights[2]], "manager": [rights[0]], "basic": [rights[1], rights[3]] }
Вы можете смело использовать эти структуры в качестве реализации представления пользователей, групп и прав. Также допускается и приветствуется придуманная вами структура.
(Здесь и далее Any в сигнатуре функции означает любой тип данных)
function users(): Array<Any>
Возвращает массив всех пользователей.
function createUser(username: String, password: String): Any
Создает нового пользователя с указанным логином username
и паролем password
, возвращает созданного пользователя.
function deleteUser(user: Any): undefined
Удаляет пользователя user
function userGroups(user: Any): Array<Any>
Возвращает массив групп, к которым принадлежит пользователь user
function addUserToGroup(user: Any, group: Any): undefined
Добавляет пользователя user
в группу group
function removeUserFromGroup(user: Any, group: Any): undefined
Удаляет пользователя user
из группы group
. Должна бросить исключение, если пользователя user
нет в группе group
function rights(): Array<Any>
Возвращает массив прав
function createRight(name: String): Any
Создает новое право и возвращает его
function deleteRight(right: Any): undefined
Удаляет право right
function addRightToGroup(right: Any, group: Any) : undefined
Добавляет право right
к группе group
function removeRightFromGroup(right: Any, group: Any) : undefined
Удаляет право right
из группы group
. Должна бросить исключение, если права right
нет в группе group
function groups(): Array<Any>
Возвращает массив групп
function createGroup(name: String): Any
Создает новую группу и возвращает её.
function deleteGroup(group: Any): undefined
Удаляет группу group
function groupRights(group: Any): Array<Any>
Возвращает массив прав, которые принадлежат группе group
Каждая из этих функций должна выбрасывать исключение в случае, если ей в качестве аргумента было передано значение неверного типа (например, если в функцию groupRights передать пользователя, вместо группы), или если это значение предполагается более не существующим в системе (например, если в функцию groupRights в качестве аргумента передать удаленную группу).
(что такое исключение и как его выбрасывать? см. Примечания в конце этого файла)
Важно Вы только что прочитали часть спецификации системы, которую вам предстоит реализовать. Здесь и далее сигнатуры реализуемых вами функций должны быть идентичны функциям спецификации. Напомню, это означает точное соответствие имени функции, количества и типов аргументов и типа возвращаемого значения.
Почему это важно? Потому что я не знаю и не хочу знать заранее как именно вы реализуете эти функции, какие структуры при этом будете использовать. Оценка вашей работы будет производиться автоматическими инструментами (jsamine), которые будут опираться на спецификацию выше.
Здесь и далее под сессией пользователя понимается состояние системы, в котором становится возможным осуществление, определенных по некоторому правилу, набора действий.
Реализовать следующие функции:
function login(username: String, password: String): Boolean
- username - идентификатор пользователя.
- password - пароль.
- return -
true
, если пользователь с логиномusername
и паролемpassword
существует,false
в противном случае. Также функцияlogin
должна вернутьfalse
в случае, если сессия пользователя уже существует.
В случае, если пользователь с логином username
и паролем password
найден в системе, вызов данной функции должен создавать сессию данного пользователя.
function logout(): undefined
Вызов данной функции должен завершать сессию текущего пользователя
function currentUser() : Any | undefined
- return - Возвращает текущего пользователя или undefined в случае, если текущий пользователь не прошел аутентифицацию
Реализовать функцию:
function isAuthorized(user: Any, right: Any): Boolean
user
- пользовательright
- правоreturn
-true
в случае, если пользовательuser
обладает правомright
,false
в противном случае
Действия в системе представляются в виде обычных JavaScript функций.
Воспользуйтесть только что написанной библиотекой:
- Создайте несколько пользователей, групп и прав. Свяжите их друг с другом
- Реализуйте несколько простых действий: обычных JavaScript функций, которые выполняют проверки: обладает ли текущий пользователь необходимым набором прав.
- Авторизуйтесь, попытайсь выполнить несколько действий.
Цель данной части работы - продемонстрировать своё понимание того, как можно использовать реализованные функции в приложении. Я думаю, будет достаточно 30-60 строк кода.
Реализуйте возможность аутентификации без пароля в гостевой сессии.
Если следовать строго терминологии данного документа, то "гостевая сессия" выше означает вход от имени пользователя, который принадлежит к единственной группе "Гость", обладающей минимальным набором прав.
function loginAs(user: Any): undefined
user
- пользователь
Вызов функции loginAs
должен эмулировать аутентификацию от имени другого пользователя.
Доступ к функии loginAs
должны иметь только члены привелигированной группы (например: администраторы и тестировщики).
После вызова функии loginAs
вызов функции logout
должен прекратить эмуляцию (вместо закрытия текущей сессии).
Реализовать функцию:
function securityWrapper(action: Function, right: Any): Function
action
- JavaScript функция, действиеright
- правоreturn
- JavaScript функция
securityWrapper
должен возвращать функцию, вызов которой должен вернуть результат обращения к функции action
с тем же набором аргументов, но только в том случае, если текущий пользователь прошел процесс аутентификации и обладает правом right
.
Если пользователь "admin" обладает правом "canIncreaseCounter", в отличие от пользователя "guest", то.
var counter = 0; function increaseCounter(amount) { counter += amount }; var secureIncreaseCounter = securityWrapper(increaseCounter, "canIncreaseCounter"); login("admin", "1234"); secureIncreaseCounter(1); logout(); counter == 1; // -> true login("guest, ""); secureIncreaseCounter(1); logout(); counter == 2; // -> false
function addActionListener(listener: Function): undefined
listener
- функция с сигнатуройfunction(user: Any, action: Function): undefined
user
- пользовательaction
- функция-действие
Вызов функции addActionListener
должен добавить функцию listener
в список "наблюдателей" за действиями пользователя.
Все функции из данного списка должны быть вызваны при каждом обращении к функциям, возвращенным в качестве результата функции securityWrapper
var counter = 0; function increaseCounter(amount) { counter += amount }; var secureIncreaseCounter = securityWrapper(increaseCounter, "canIncreaseCounter"); addActionListener(function(user, action) { console.log("Пользователь " + user + " только что сделал " + action.name); }); addActionListener(function(user, action) { alert("Пользователь " + user + " только что сделал " + action.name); }); login("admin", "1234"); secureIncreaseCounter(1); // На этом моменте должно появиться всплывающее окно и // сообщение в консоль logout(); login("guest, ""); secureIncreaseCounter(1); // Аналогично, всплывающее окно и консоль logout();
Вы можете проверить корректность своей реализации при помощи автоматических тестов.
- Склонировть себе этот репозиторий
- Реализовать функци в файле auth-lib.js
- Открыть в браузере файл index.html
Тестирование производится при помощи библиотеки jasmine
Посмотреть содержание тесов можно в файле tests.js.
var user = createUser("admin", "1234"); deleteUser(user); users().indexOf(user); // -> должно вернуть -1 var right = createRight(); var group = creageGroup(); var user = createUser("user", "user"); addUserToGroup(user, group); addRightToGroup(right, group); isAuthorized(user, right); // -> должно быть true userGroups(user).indexOf(group); // -> должно вернуть что-то >= 0 groupRights(group).indexOf(right); // -> должно вернуть что-то >= 0 removeRightFromGroup(right, group); // ok removeRightFromGroup(right, group); // должен вылететь Error
То, чего не было на лекциях, но будет полезно при выполнении данного задания.
Удалить элемент из массива можно при помощи функции splice
Под "выбрасыванием" исключения понимается следующее выражение:
throw new Error('сообщение, описывающее возникшую ошибку')
Оператор delete
удаляет свойство объекта.
var foo = {bar: 'baz'} foo.bar; // -> 'baz' delete foo.bar; foo.bar; // -> undefined
Любую JavaScript функцию можно вызвать, передав ей большее или меньшее количество фактических параметорв, чем формальных. В случае, если количество фактических параметров меньше количества формальных, значения недостающих аргументов будут равны undefined
.
function identity(x) { return x; } identity(); // -> undefined identity(1, 2, 3, 4, 5); //-> 1
В теле каждой функии определена специальная переменная arguments
, которая содержит список всех фактических переменных, переданных функции при её вызове.
function sum() { var total = 0; for (var i = 0; i < arguments.length; i++) { total += arguments[i]; } return total; } sum(1, 2, 3); // -> 6
Для того, чтобы вызвать функцию со списком аргументов, количество которых заранее не извесно, используется функция apply
Первый аргумент функции apply
- объект, который станет значением this
в теле вызванной функции
Второй аргумент - массив фактических параметров, который будет передан функции в качестве аргументов.
sum.apply(null, [1,2,3]); // -> 6
arguments
не является массивом, но может быть легко преобразован к нему при помощи выражения Array.from(arguments)