Процедуры обработки RADIUS-запросов#
О RADIUS-процедурах#
Процедуры обработки RADIUS-запросов - это процедуры на языке программирования Java, которые обрабатывают запросы из приложений, подключенных к Blitz Identity Provider по протоколу RADIUS. Процедуры определяют политики доступа в сетевые ресурсы и реализуют процесс аутентификации на разных факторах. Данный раздел описывает синтаксис создания RADIUS-процедур.
Интерфейс RadiusFlow#
RADIUS-процедура должна реализовать интерфейс RadiusFlow
, входящий в состав Blitz Development Kit (Blitz SDK):
package com.identityblitz.idp.radius.flow;
import java.util.Map;
public interface RadiusFlow {
String loginN12(String var1);
RadiusResult next(RadiusContext var1);
RadiusResult dialog(RadiusContext var1, String var2, Map<String, String> var3, String var4);
Интерфейс RadiusFlow
содержит следующие методы:
String loginN12(String var1)
: возвращает логин, введенный пользователем, и записывает в лог информацию о вводе логина.package com.identityblitz.idp.radius.flow; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import com.identityblitz.idp.radius.flow.RadiusResult.*; ... public class RadiusLog implements RadiusFlow { public String loginN12(final String login) { logger.debug("#### Start loginN12: {}", login); return login; } ... }
RadiusResult next(RadiusContext var1)
: метод, ссылающийся на интерфейсRadiusResult
, определяющий алгоритм аутентификации пользователя в зависимости от контекста входа.RadiusResult dialog(RadiusContext var1, String var2, Map<String, String> var3, String var4)
: метод, ссылающийся на интерфейсRadiusResult
, определяющий отображение диалогов на разных стадиях аутентификации.
Контекст входа RadiusContext#
Алгоритм аутентификации и отображение диалогов определяются в процедуре исходя из контекста входа по RADIUS. Для его описания используется интерфейс RadiusContext
, содержащий следующий набор методов:
int factor()
: возвращает порядковый номер фактора аутентификации.List<String> passedMethod()
: возвращает список пройденных методов аутентификации.String clientId()
: возвращает значение параметраclient_id
приложения.String subject()
: возвращает id пользователя.String claim(String var1)
: возвращает значение утверждения о пользователе по имени утверждения.String userProp(String var1)
: возвращает значение свойства пользователя по имени свойства.List<com.identityblitz.idp.radius.flow.RadiusContext.Group> groups()
: возвращает список групп пользователя.
package com.identityblitz.idp.radius.flow;
import java.util.List;
public interface RadiusContext {
int factor();
List<String> passedMethod();
String clientId();
String subject();
String claim(String var1);
String userProp(String var1);
List<com.identityblitz.idp.radius.flow.RadiusContext.Group> groups();
Интерфейс RadiusResult#
Интерфейс RadiusResult
определяет поведение системы при проведении аутентификации, включая алгоритмы аутентификации и отображение диалогов:
package com.identityblitz.idp.radius.flow;
import com.identityblitz.idp.radius.ChallengeCommunication;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
public interface RadiusResult {
static RadiusResult rejected(String cause) {
return new com.identityblitz.idp.radius.flow.RadiusResult.RejectedResult(cause);
}
static RadiusResult challenge(ChallengeCommunication chlg) {
return new com.identityblitz.idp.radius.flow.RadiusResult.ChallengesResult(Collections.singletonList(chlg));
}
static RadiusResult challenges(ChallengeCommunication... chlgs) {
return new com.identityblitz.idp.radius.flow.RadiusResult.ChallengesResult(Arrays.asList(chlgs));
}
static RadiusResult dialog(String message) {
return new com.identityblitz.idp.radius.flow.RadiusResult.DialogResult(message, Collections.emptyMap());
}
static RadiusResult dialog(String message, Map<String, String> answers) {
return new com.identityblitz.idp.radius.flow.RadiusResult.DialogResult(message, answers);
}
static com.identityblitz.idp.radius.flow.RadiusResult.VendorSpecificAttributes vendorSpecific() {
return new com.identityblitz.idp.radius.flow.RadiusResult.VendorSpecificAttributes();
}
static RadiusResult authenticated(String subjectId) {
return new com.identityblitz.idp.radius.flow.RadiusResult.AuthenticatedResult(subjectId, new com.identityblitz.idp.radius.flow.RadiusResult.VendorSpecificAttributes());
}
static RadiusResult authenticated(String subjectId, com.identityblitz.idp.radius.flow.RadiusResult.VendorSpecificAttributes avps) {
return new com.identityblitz.idp.radius.flow.RadiusResult.AuthenticatedResult(subjectId, avps);
}
}
RadiusResult rejected(String cause)
: вызов диалога об отказе в аутентификации на основании указанной причиныcause
.Реализация в Blitz SDK
Класс
RadiusResult.RejectedResult
package com.identityblitz.idp.radius.flow; public class RadiusResult$RejectedResult implements RadiusResult { private final String cause; public RadiusResult$RejectedResult(String cause) { this.cause = cause; } public String getCause() { return this.cause; } }
Пример использования в процедуре:
return RadiusResult.rejected("This method is temporarily unavailable");
RadiusResult challenge(ChallengeCommunication chlg)
иRadiusResult challenges(ChallengeCommunication... chlgs)
: вызов диалогов различных методов аутентификации (SMS, push, email, TOTP, HOTP, через Личный кабинет, по паролю, вызов метода по имени).Реализация в Blitz SDK
Класс
RadiusResult.Challenges
package com.identityblitz.idp.radius.flow; import com.identityblitz.idp.radius.ChallengeCommunication; import com.identityblitz.idp.radius.SmsChallenge; import com.identityblitz.idp.radius.EmailChallenge.; import scala.Option; public class RadiusResult$Challenges { /* Метод вызывает настроенный по умолчанию канал доставки разового кода SMS/push. Если канал не сработает, код будет отправлен по второму каналу */ public static ChallengeCommunication sms() { return new SmsChallenge(Option.apply((Object)null)); } /* Метод явно вызывает канал доставки разового кода SMS/push, указанный в переменной way. Можно передать: "sms", "push". Канал доставки должен быть настроен, иначе метод не сработает */ public static ChallengeCommunication sms(String way) { return new SmsChallenge(Option.apply(way)); } // Метод входа по email public static ChallengeCommunication email() { return .MODULE$; } // Метод входа через TOTP public static ChallengeCommunication totp() { return com.identityblitz.idp.radius.TotpChallenge..MODULE$; } // Метод входа через HOTP public static ChallengeCommunication hotp() { return com.identityblitz.idp.radius.HotpChallenge..MODULE$; } // Метод входа через Личный кабинет public static ChallengeCommunication profile() { return com.identityblitz.idp.radius.ProfileChallenge..MODULE$; } // Метод входа по паролю public static ChallengeCommunication password() { return com.identityblitz.idp.radius.PasswordChallenge..MODULE$; } /* Метод используется для вызова переданного в name метода входа. Можно передать: "sms", "push", "email", "totp", "hotp", "profile", "password", "mobile" (использование канала SMS/push, настроенного по умолчанию) */ public static ChallengeCommunication byDialogName(String name) { return com.identityblitz.idp.radius.ChallengeCommunication..MODULE$.byDialogName(name); } }
Примеры использования в процедуре:
return RadiusResult.challenge(Challenges.password())
return RadiusResult.challenge(Challenges.email())
final String challenge = "totp"; return RadiusResult.challenge(Challenges.byDialogName(challenge));
return RadiusResult.challenge(Challenges.sms("push"))
return RadiusResult.challenge(Challenges.sms("sms"))
Для доставки кода по настроенному по умолчанию каналу SMS/push можно использовать любой из следующих методов:
Примечание
Если канал по умолчанию не сработает, будет использован второй канал.
sms()
return RadiusResult.challenge(Challenges.sms());
byDialogName("mobile")
return RadiusResult.challenge(Challenges.byDialogName("mobile"));
Перегрузки метода
RadiusResult dialog
:RadiusResult dialog(String message)
: отображение сообщенияmessage
;RadiusResult dialog(String message, Map<String, String> answers)
: вызов диалога, состоящего из инструкции по прохождению фактораmessage
с возможностью выбрать нужный метод из словаряanswers
.
Примечание
Для задания текста конкретного сообщения
message
(в примере нижеchallengeChooseNew
) см. Тексты диалоговых сообщений.Пример использования в процедуре:
/* Создать словарь answers, задающий соответствие между ответами пользователя и методами входами */ final java.util.Map<String, String> answers = new java.util.HashMap<String, String>(); answers.put("1", "sms"); answers.put("2", "totp"); answers.put("3", "prfc"); /* Вызвать метод RadiusResult.dialog, который отображает диалог challengeChooseNew с вариантами ответов из словаря answers и принимает ответ пользователя */ return RadiusResult.dialog("challengeChooseNew", answers);
com.identityblitz.idp.radius.flow.RadiusResult.VendorSpecificAttributes vendorSpecific()
: работа с атрибутами, поступающими от сетевого обрудования определенного вендора.Реализация в Blitz SDK
Класс
RadiusResult$VendorSpecificAttributes
package com.identityblitz.idp.radius.flow; import java.util.HashMap; import java.util.Map; public class RadiusResult$VendorSpecificAttributes { private final Map<Long, Map<Integer, Object>> avps = new HashMap(); protected RadiusResult$VendorSpecificAttributes() { } public RadiusResult$VendorSpecificAttributes withAttribute(long vendorId, int vType, Object value) { Map<Integer, Object> byVendor = (Map)this.avps.get(vendorId); if (byVendor == null) { Map<Integer, Object> nByVendor = new HashMap(); this.avps.put(vendorId, nByVendor); nByVendor.put(vType, value); } else { byVendor.put(vType, value); } return this; } protected Map<Long, Map<Integer, Object>> getAvps() { return this.avps; } }
Перегрузки метода
RadiusResult authenticated
:RadiusResult authenticated(String subjectId)
: проведение аутентификации пользователя с указанным идентификатором;RadiusResult authenticated(String subjectId, com.identityblitz.idp.radius.flow.RadiusResult.VendorSpecificAttributes avps)
: проведение аутентификации пользователя с указанным идентификатором и значениями атрибутов сетевого оборудования определенного вендора.
Реализация в Blitz SDK
Класс
RadiusResult$AuthenticatedResult
package com.identityblitz.idp.radius.flow; import com.identityblitz.idp.radius.flow.RadiusResult.VendorSpecificAttributes; import java.util.Map; public class RadiusResult$AuthenticatedResult implements RadiusResult { private final String subjectId; private final VendorSpecificAttributes avps; public RadiusResult$AuthenticatedResult(String subjectId, VendorSpecificAttributes avps) { this.subjectId = subjectId; this.avps = avps; } public String getSubjectId() { return this.subjectId; } public Map<Long, Map<Integer, Object>> getAvps() { return this.avps.getAvps(); } }
Пример использования в процедуре:
public RadiusResult next(final RadiusContext context) { ... RadiusResult.authenticated(context.subject()); }
Пример процедуры#
Пример процедуры с комментариями и логированием на уровне DEBUG показан ниже. Руководствуясь данным примером и Blitz SDK, перепишите процедуру в соответствии со своими требования к входу в сетевые службы.
package com.identityblitz.idp.radius.flow;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import com.identityblitz.idp.radius.flow.RadiusResult.*;
public class RadFLOWFull implements RadiusFlow {
// Создать логгер для класса com.identityblitz.idp.flow.radius
private final Logger logger = LoggerFactory.getLogger("com.identityblitz.idp.flow.radius");
/* Метод возвращает введенный пользователем логин
и логирует информацию о вводе логина */
public String loginN12(final String login) {
logger.debug("#### Logging in via RADIUS. User entered login: {}", login);
return login;
}
/* Вызов метода, реализующего алгоритм аутентификации
в зависимости от контекста входа */
public RadiusResult next(final RadiusContext context) {
/* Логирование на уровне DEBUG: id пользователя и
порядковый номер фактора аутентификации из контекста */
logger.debug("### Getting subject id and factor number: subject = {}, factor = {}", context.subject(), context.factor());
/* Если пользователь в данный момент проходит первый
фактор аутентификации, запросить ввод пароля */
if (context.factor() == 1) {
return RadiusResult.challenge(Challenges.password());
/* Иначе проверить, проходит ли пользователь в данный момент второй фактор.
В этом случае создать словарь answers, задающий соответствие между
ответами пользователя и методами входами.
При вводе "1" будет реализован метод входа по SMS, при вводе "2" - подтверждение через Личный кабинет
*/
} else if (context.factor() == 2) {
final java.util.Map<String, String> answers = new java.util.HashMap<String, String>();
answers.put("1", "sms");
answers.put("2", "prfc");
/* Вызвать метод RadiusResult.dialog,
который отображает диалог challengeChoose с вариантами
ответа answers и принимает ответ пользователя */
return RadiusResult.dialog("challengeChoose", answers);
/* Если фактор не первый и не второй, значит пользователь прошел
оба фактора. Аутентифицировать пользователя с указанным идентификатором */
} else {
return RadiusResult.authenticated(context.subject());
}
}
/* Реализация метода RadiusResult.dialog. Метод принимает контекст,
диалоговое сообщение, словарь с вариантами ответа пользователя,
введенный ответ пользователя */
public RadiusResult dialog(final RadiusContext context,
final String message,
final java.util.Map<String, String> answers,
final String answer) {
// Логировать на уровне DEBUG вывод диалога для пользователя
logger.debug("#### Start dialog for user: {}", context.subject());
// Логировать вывод диалогового сообщения
logger.debug("#### Start dialog with message: {}", message);
/* Если передается сообщение challengeChoose,
записать в константу challenge ответ пользователя */
if(message.equals("challengeChoose")) {
final String challenge = answers.get(answer);
// Логировать на уровне DEBUG ответ пользователя
logger.debug("#### Get challenge by dialog: {}", challenge);
// Проверить, что пользователь отправил непустой ответ
if (challenge != null) {
// Если ответ равен prfc, создать словарь confirm
if (challenge.equals("prfc")) {
final java.util.Map<String, String> confirm = new java.util.HashMap<String, String>();
/* Добавить в словарь confirm соответствие между нажатием Enter
и отправкой подтверждения входа в Личный кабинет */
confirm.put("OK", "prfc_configrm");
/* Отобразить сообщение confirm_profile и
принять действие пользователя (нажатие Enter) */
return RadiusResult.dialog("confirm_profile", confirm);
/* Если ответ пользователя не совпадает с prfc,
вызвать метод входа, соответствующий ответу пользователя */
} else {
return RadiusResult.challenge(Challenges.byDialogName(challenge));
}
/* Если пользователь отправил пустой ответ,
повторно отобразить диалог с вариантами ответа answers */
} else {
return RadiusResult.dialog(message, answers);
}
/* Если передается сообщение, отличное от challengeChoose,
проверить его на соответствие confirm_profile.
Если это так, вызвать метод входа через Личный кабинет */
} else if (message.equals("confirm_profile")) {
return RadiusResult.challenge(Challenges.profile());
}
// В противном случае отказать в аутентификации по передаваемой в метод причине
else {
return RadiusResult.rejected("Login method not specified");
}
}
}
Тексты диалоговых сообщений#
Для того чтобы определить текст диалоговых сообщений, вызываемых в RADIUS-процедурах, руководствуйтесь стандартным алгоритмом внесения изменений в тексты интерфейса.
Идентификатор строки для задания диалогового сообщения message
из методов RadiusResult.dialog(message, answers)
и RadiusResult.dialog(message)
должен быть в формате radius.display.dialog.message.<message>
. Например, для реализации процедуры из примера в файл messages
вносятся следующие изменения:
radius.display.dialog.message.challengeChoose=Выберите способ подтверждения входа: \n 1 - Подтверждение кодом из SMS \n 2 - Подтверждение в Личном кабинете (https://sso.domain.ru/blitz/profile) \n Выбор:
radius.display.dialog.challenge.name.prfc=Подтверждение в профиле пользователя\n
radius.display.dialog.challenge.name.sms=Подтверждение кодом из SMS\n
radius.display.dialog.message.confirm_profile=Нажмите Enter/Ввод для подтверждения входа в Личном кабинете (https://sso.domain.ru/blitz/profile).
Сообщение radius.display.dialog.message.challengeChoose
вызывается в методе RadiusResult.dialog("challengeChoose", answers)
, а сообщение radius.display.dialog.message.confirm_profile
вызывается в методе RadiusResult.dialog("confirm_profile", confirm)
.
В строках radius.display.dialog.challenge.name.prfc
и radius.display.dialog.challenge.name.sms
перезаписываются диалоги, отображаемые по умолчанию для метода входа через Личный кабинет (prfc
) и по коду из SMS (sms
).
При необходимости вы можете менять следующие строки по умолчанию:
radius.display.vrf_code.sms=Введите код из SMS:
radius.display.vrf_code.sms.on_error=Неверный код. Попробуйте еще раз:
radius.display.vrf_code.push=Введите код из push-сообщения:
radius.display.vrf_code.push.on_error=Неверный код. Попробуйте еще раз:
radius.display.vrf_code.totp=Введите код из приложения {0}:
radius.display.vrf_code.totp.onError=Неверный код. Попробуйте еще раз:
radius.display.vrf_code.totp.noTotp=Вход по генератору разовых паролей не настроен в учетной записи.
radius.display.vrf_code.hotp=Введите код из приложения {0}:
radius.display.vrf_code.hotp.onError=Неверный код. Попробуйте еще раз:
radius.display.vrf_code.hotp.noHotp=Вход по генератору разовых паролей не настроен в учетной записи.
radius.display.vrf_code.email=Введите код из email:
radius.display.vrf_code.email.on_error=Неверный код. Попробуйте еще раз:
radius.display.dialog.message.challengeChoose=Выберите метод аутентификации: {0}
radius.display.dialog.challenge.name.password=пароль
radius.display.dialog.challenge.name.sms=sms
radius.display.dialog.challenge.name.push=push
radius.display.dialog.challenge.name.mobile=sms/push
radius.display.dialog.challenge.name.email=email
radius.display.dialog.challenge.name.totp=TOTP
radius.display.dialog.challenge.name.hotp=HOTP
radius.display.dialog.challenge.name.prfc=профайл
radius.display.dialog.challenge.name.trustKey=доверенное приложение