Процедуры обработки 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())
    
    Вызов метода аутентификации по email#
    return RadiusResult.challenge(Challenges.email())
    
    Вызов фактора методом из константы challenge#
    final String challenge = "totp";
    return RadiusResult.challenge(Challenges.byDialogName(challenge));
    
    Явный вызов метода аутентификации по push#
    return RadiusResult.challenge(Challenges.sms("push"))
    
    Явный вызов метода аутентификации по SMS#
    return RadiusResult.challenge(Challenges.sms("sms"))
    

    Для доставки кода по настроенному по умолчанию каналу SMS/push можно использовать любой из следующих методов:

    Примечание

    Если канал по умолчанию не сработает, будет использован второй канал.

    1. sms()

      return RadiusResult.challenge(Challenges.sms());
      
    2. 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=доверенное приложение