Внедрение SAML с существующей базой пользователей

В текущей установке Discourse регистрация новых пользователей отключена, а вход осуществляется через плагин LDAP (GitHub - jonmbake/discourse-ldap-auth: Discourse plugin to enable LDAP/Active Directory authentication. · GitHub). В результате многие внутренние пользователи уже существуют с созданным ими контентом.

Мне поручили установить или выполнить миграцию на SAML для интеграции с нашим процессом, использующим Okta. Я установил плагин (GitHub - discourse/discourse-saml: Support for SAML in Discourse · GitHub) с опцией DISCOURSE_SAML_AUTO_CREATE_ACCOUNT: true. Требуется создавать пользователя, если он ещё не существует. В противном случае, если пользователь уже существует (например, был создан ранее через LDAP), необходимо использовать эту же учётную запись. Однако в моём случае система пытается создать нового пользователя с уже существующим именем, что приводит к ошибке. Если я отключаю опцию автоматического создания, появляется всплывающее окно, где можно отредактировать данные регистрации: имя пользователя предзаполнено верно, но к нему добавляется числовой префикс, чтобы избежать конфликта. Очевидно, что в моём случае такой вариант не подходит.

Есть ли у вас какие-либо предложения?

Возможно, этот старый пост имеет отношение к проблеме, но там нет ответа:
https://meta.discourse.org/t/verify-if-user-is-already-logged-sso-with-saml2-discourse-saml/73213
(К сожалению, я не могу добавить более двух ссылок в сообщение).

Заранее спасибо.
AB

Пользователи обычно сопоставляются по адресу электронной почты. Разве LDAP и SAML не используют один и тот же адрес электронной почты?

Адреса электронной почты совпадают, вероятно, мне нужно проверить, что содержится в полезной нагрузке SAML.

Спасибо за подсказку. Я проверю.

Получаю следующую ошибку:

Started POST "/auth/saml" for 188.114.103.216 at 2020-08-04 16:46:20 +0000
(saml) Request phase initiated.
Started POST "/auth/saml/callback" for 188.114.103.216 at 2020-08-04 16:46:21 +0000
(saml) Callback phase initiated.
Processing by Users::OmniauthCallbacksController#complete as HTML
  Parameters: {"SAMLResponse"=>"........", "RelayState"=>"", "provider"=>"saml"}
Completed 422 Unprocessable Entity in 16ms (ActiveRecord: 0.0ms | Allocations: 4546)
ActiveRecord::RecordInvalid (Validation failed: Primary email is invalid.)
lib/distributed_mutex.rb:33:in `block in synchronize'
lib/distributed_mutex.rb:29:in `synchronize'
lib/distributed_mutex.rb:29:in `synchronize'
lib/distributed_mutex.rb:14:in `synchronize'
app/controllers/users/omniauth_callbacks_controller.rb:37:in `complete'
app/controllers/application_controller.rb:350:in `block in with_resolved_locale'
app/controllers/application_controller.rb:350:in `with_resolved_locale'
lib/middleware/omniauth_bypass_middleware.rb:47:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/anonymous_cache.rb:328:in `call'
config/initializers/100-quiet_logger.rb:19:in `call'
config/initializers/100-silence_logger.rb:31:in `call'
lib/middleware/enforce_hostname.rb:22:in `call'
lib/middleware/request_tracker.rb:176:in `call'
Failed to handle exception in exception app middleware : Validation failed: Primary email is invalid.

Я проверил полезную нагрузку ответа SAML, и она выглядит следующим образом (некоторые данные удалены по понятным причинам):

<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema" Destination="https://example.com/auth/saml/callback" ID="id123" IssueInstant="2020-08-04T16:39:45.888Z" Version="2.0">
   <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/xyz</saml2:Issuer>
   <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <ds:SignedInfo>
         <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
         <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
         <ds:Reference URI="#id123">
            <ds:Transforms>
               <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
               <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                  <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs" />
               </ds:Transform>
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
            <ds:DigestValue>....</ds:DigestValue>
         </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>...</ds:SignatureValue>
      <ds:KeyInfo>
         <ds:X509Data>
            <ds:X509Certificate>...</ds:X509Certificate>
         </ds:X509Data>
      </ds:KeyInfo>
   </ds:Signature>
   <saml2p:Status>
      <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
   </saml2p:Status>
   <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="id123" IssueInstant="2020-08-04T16:39:45.888Z" Version="2.0">
      <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/xyz</saml2:Issuer>
      <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
         <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <ds:Reference URI="#id123">
               <ds:Transforms>
                  <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                  <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                     <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs" />
                  </ds:Transform>
               </ds:Transforms>
               <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
               <ds:DigestValue>...</ds:DigestValue>
            </ds:Reference>
         </ds:SignedInfo>
         <ds:SignatureValue>...</ds:SignatureValue>
         <ds:KeyInfo>
            <ds:X509Data>
               <ds:X509Certificate>...</ds:X509Certificate>
            </ds:X509Data>
         </ds:KeyInfo>
      </ds:Signature>
      <saml2:Subject>
         <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">my.name</saml2:NameID>
         <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <saml2:SubjectConfirmationData NotOnOrAfter="2020-08-04T16:44:45.888Z" Recipient="https://example.com/auth/saml/callback" />
         </saml2:SubjectConfirmation>
      </saml2:Subject>
      <saml2:Conditions NotBefore="2020-08-04T16:34:45.888Z" NotOnOrAfter="2020-08-04T16:44:45.888Z">
         <saml2:AudienceRestriction>
            <saml2:Audience>https://example.com</saml2:Audience>
         </saml2:AudienceRestriction>
      </saml2:Conditions>
      <saml2:AuthnStatement AuthnInstant="2020-08-04T06:53:32.462Z" SessionIndex="id789">
         <saml2:AuthnContext>
            <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
         </saml2:AuthnContext>
      </saml2:AuthnStatement>
      <saml2:AttributeStatement>
         <saml2:Attribute Name="screenName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">my.name@example.com</saml2:AttributeValue>
         </saml2:Attribute>
      </saml2:AttributeStatement>
   </saml2:Assertion>
</saml2p:Response>

Есть какие-нибудь идеи?

Спасибо,
AB

Похоже, ваш SAML не завершает адрес электронной почты? Или вы смотрите не в том поле?

Проблема заключается в том, что мой Okta/SAML отправляет имя пользователя, а не адрес электронной почты в этом поле:

<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">my.name</saml2:NameID>

Я исправил это, добавив новый атрибут под названием email, а затем форкнул проект, чтобы настроить result.mail, считывая его из этого нового атрибута (если он присутствует).

Я собираюсь создать запрос на слияние (pull request).

Спасибо,
AB