diff options
author | enahum <nahumhbl@gmail.com> | 2016-07-05 15:49:00 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-05 15:49:00 -0400 |
commit | 5f04dc4f45b9657d46380499f92ae6e5c1bf5506 (patch) | |
tree | 86670cb20e038f9716eca3ab9dcb7f2f71263286 /webapp/components/admin_console/saml_settings.jsx | |
parent | f91b9d4a654ff27777580651d853b6372a425af6 (diff) | |
download | chat-5f04dc4f45b9657d46380499f92ae6e5c1bf5506.tar.gz chat-5f04dc4f45b9657d46380499f92ae6e5c1bf5506.tar.bz2 chat-5f04dc4f45b9657d46380499f92ae6e5c1bf5506.zip |
SAML support (#3494)
* PLT-3073: Implement SAML/Okta Server side (EE) (#3422)
* PLT-3137 Support for SAML configuration
* PLT-3410 SAML Database Store
* PLT-3411 CLI to add Identity Provider Certificate and Service Provider Private Key
* PLT-3409 SAML Interface for EE
* PLT-3139 Handle SAML authentication server side
* Add localization messages
* PLT-3443 SAML Obtain SP metadata
* PLT-3142 Login & Switch to/from SAML
* Remove Certs for Database & Clean SAML Request
* Make required Username, FirstName and LastName
* PLT-3140 Add SAML to System Console (#3476)
* PLT-3140 Add SAML to System Console
* Move web_client functions to client.jsx
* Fix issues found by PM
* update package.json mattermost driver
* Fix text messages for SAML
Diffstat (limited to 'webapp/components/admin_console/saml_settings.jsx')
-rw-r--r-- | webapp/components/admin_console/saml_settings.jsx | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/webapp/components/admin_console/saml_settings.jsx b/webapp/components/admin_console/saml_settings.jsx new file mode 100644 index 000000000..db841aa83 --- /dev/null +++ b/webapp/components/admin_console/saml_settings.jsx @@ -0,0 +1,518 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; + +import AdminSettings from './admin_settings.jsx'; +import BooleanSetting from './boolean_setting.jsx'; +import TextSetting from './text_setting.jsx'; +import FileUploadSetting from './file_upload_setting.jsx'; +import RemoveFileSetting from './remove_file_setting.jsx'; + +import {FormattedMessage, FormattedHTMLMessage} from 'react-intl'; +import SettingsGroup from './settings_group.jsx'; + +import Client from 'utils/web_client.jsx'; +import * as Utils from 'utils/utils.jsx'; + +export default class SamlSettings extends AdminSettings { + constructor(props) { + super(props); + + this.getConfigFromState = this.getConfigFromState.bind(this); + + this.renderSettings = this.renderSettings.bind(this); + this.uploadCertificate = this.uploadCertificate.bind(this); + this.removeCertificate = this.removeCertificate.bind(this); + + const settings = props.config.SamlSettings; + + this.state = Object.assign(this.state, { + enable: settings.Enable, + verify: settings.Verify, + encrypt: settings.Encrypt, + idpUrl: settings.IdpUrl, + idpDescriptorUrl: settings.IdpDescriptorUrl, + assertionConsumerServiceURL: settings.AssertionConsumerServiceURL, + idpCertificateFile: settings.IdpCertificateFile, + publicCertificateFile: settings.PublicCertificateFile, + privateKeyFile: settings.PrivateKeyFile, + firstNameAttribute: settings.FirstNameAttribute, + lastNameAttribute: settings.LastNameAttribute, + emailAttribute: settings.EmailAttribute, + usernameAttribute: settings.UsernameAttribute, + nicknameAttribute: settings.NicknameAttribute, + localeAttribute: settings.LocaleAttribute, + loginButtonText: settings.LoginButtonText + }); + } + + getConfigFromState(config) { + config.SamlSettings.Enable = this.state.enable; + config.SamlSettings.Verify = this.state.verify; + config.SamlSettings.Encrypt = this.state.encrypt; + config.SamlSettings.IdpUrl = this.state.idpUrl; + config.SamlSettings.IdpDescriptorUrl = this.state.idpDescriptorUrl; + config.SamlSettings.AssertionConsumerServiceURL = this.state.assertionConsumerServiceURL; + config.SamlSettings.IdpCertificateFile = this.state.idpCertificateFile; + config.SamlSettings.PublicCertificateFile = this.state.publicCertificateFile; + config.SamlSettings.PrivateKeyFile = this.state.privateKeyFile; + config.SamlSettings.FirstNameAttribute = this.state.firstNameAttribute; + config.SamlSettings.LastNameAttribute = this.state.lastNameAttribute; + config.SamlSettings.EmailAttribute = this.state.emailAttribute; + config.SamlSettings.UsernameAttribute = this.state.usernameAttribute; + config.SamlSettings.NicknameAttribute = this.state.nicknameAttribute; + config.SamlSettings.LocaleAttribute = this.state.localeAttribute; + config.SamlSettings.LoginButtonText = this.state.loginButtonText; + + return config; + } + + uploadCertificate(id, file, callback) { + Client.uploadCertificateFile( + file, + () => { + const fileName = file.name; + this.handleChange(id, fileName); + this.setState({[id]: fileName}); + if (callback && typeof callback === 'function') { + callback(); + } + }, + (error) => { + if (callback && typeof callback === 'function') { + callback(error.message); + } + } + ); + } + + removeCertificate(id, callback) { + Client.removeCertificateFile( + this.state[id], + () => { + this.handleChange(id, ''); + this.setState({[id]: null}); + }, + (error) => { + if (callback && typeof callback === 'function') { + callback(error.message); + } + } + ); + } + + renderTitle() { + return ( + <h3> + <FormattedMessage + id='admin.authentication.saml' + defaultMessage='SAML' + /> + </h3> + ); + } + + renderSettings() { + const licenseEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.SAML === 'true'; + if (!licenseEnabled) { + return null; + } + + let idpCert; + let privKey; + let pubCert; + + if (this.state.idpCertificateFile) { + idpCert = ( + <RemoveFileSetting + id='idpCertificateFile' + label={ + <FormattedMessage + id='admin.saml.idpCertificateFileTitle' + defaultMessage='Identity Provider Public Certificate:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.idpCertificateFileRemoveDesc' + defaultMessage='Remove the public authentication certificate issued by your Identity Provider.' + /> + } + removeButtonText={Utils.localizeMessage('admin.saml.remove.idp_certificate', 'Remove Identity Provider Certificate')} + removingText={Utils.localizeMessage('admin.saml.removing.certificate', 'Removing Certificate...')} + fileName={this.state.idpCertificateFile} + onSubmit={this.removeCertificate} + disabled={!this.state.enable} + /> + ); + } else { + idpCert = ( + <FileUploadSetting + id='idpCertificateFile' + label={ + <FormattedMessage + id='admin.saml.idpCertificateFileTitle' + defaultMessage='Identity Provider Public Certificate:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.idpCertificateFileDesc' + defaultMessage='The public authentication certificate issued by your Identity Provider.' + /> + } + uploadingText={Utils.localizeMessage('admin.saml.uploading.certificate', 'Uploading Certificate...')} + disabled={!this.state.enable} + fileType='.crt' + onSubmit={this.uploadCertificate} + /> + ); + } + + if (this.state.privateKeyFile) { + privKey = ( + <RemoveFileSetting + id='privateKeyFile' + label={ + <FormattedMessage + id='admin.saml.privateKeyFileTitle' + defaultMessage='Service Provider Private Key:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.privateKeyFileFileRemoveDesc' + defaultMessage='Remove the private key used to decrypt SAML Assertions from the Identity Provider.' + /> + } + removeButtonText={Utils.localizeMessage('admin.saml.remove.privKey', 'Remove Service Provider Private Key')} + removingText={Utils.localizeMessage('admin.saml.removing.privKey', 'Removing Private Key...')} + fileName={this.state.privateKeyFile} + onSubmit={this.removeCertificate} + disabled={!this.state.enable || !this.state.encrypt} + /> + ); + } else { + privKey = ( + <FileUploadSetting + id='privateKeyFile' + label={ + <FormattedMessage + id='admin.saml.privateKeyFileTitle' + defaultMessage='Service Provider Private Key:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.privateKeyFileFileDesc' + defaultMessage='The private key used to decrypt SAML Assertions from the Identity Provider.' + /> + } + uploadingText={Utils.localizeMessage('admin.saml.uploading.privateKey', 'Uploading Private Key...')} + disabled={!this.state.enable || !this.state.encrypt} + fileType='.key' + onSubmit={this.uploadCertificate} + /> + ); + } + + if (this.state.publicCertificateFile) { + pubCert = ( + <RemoveFileSetting + id='publicCertificateFile' + label={ + <FormattedMessage + id='admin.saml.publicCertificateFileTitle' + defaultMessage='Service Provider Public Certificate:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.publicCertificateFileRemoveDesc' + defaultMessage='Remove the certificate used to generate the signature on a SAML request to the Identity Provider for a service provider initiated SAML login, when Mattermost is the Service Provider.' + /> + } + removeButtonText={Utils.localizeMessage('admin.saml.remove.sp_certificate', 'Remove Service Provider Certificate')} + removingText={Utils.localizeMessage('admin.saml.removing.certificate', 'Removing Certificate...')} + fileName={this.state.publicCertificateFile} + onSubmit={this.removeCertificate} + disabled={!this.state.enable || !this.state.encrypt} + /> + ); + } else { + pubCert = ( + <FileUploadSetting + id='publicCertificateFile' + label={ + <FormattedMessage + id='admin.saml.publicCertificateFileTitle' + defaultMessage='Service Provider Public Certificate:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.publicCertificateFileDesc' + defaultMessage='The certificate used to generate the signature on a SAML request to the Identity Provider for a service provider initiated SAML login, when Mattermost is the Service Provider.' + /> + } + uploadingText={Utils.localizeMessage('admin.saml.uploading.certificate', 'Uploading Certificate...')} + disabled={!this.state.enable || !this.state.encrypt} + fileType='.crt' + onSubmit={this.uploadCertificate} + /> + ); + } + + return ( + <SettingsGroup> + <BooleanSetting + id='enable' + label={ + <FormattedMessage + id='admin.saml.enableTitle' + defaultMessage='Enable Login With SAML:' + /> + } + helpText={ + <FormattedHTMLMessage + id='admin.saml.enableDescription' + defaultMessage='When true, Mattermost allows login using SAML. Please see <a href="http://docs.mattermost.com/deployment/sso-saml.html" target="_blank">documentation</a> to learn more about configuring SAML for Mattermost.' + /> + } + value={this.state.enable} + onChange={this.handleChange} + /> + <TextSetting + id='idpUrl' + label={ + <FormattedMessage + id='admin.saml.idpUrlTitle' + defaultMessage='SAML SSO URL:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.idpUrlEx', 'Ex "https://idp.example.org/SAML2/SSO/Login"')} + helpText={ + <FormattedMessage + id='admin.saml.idpUrlDesc' + defaultMessage='The URL where Mattermost sends a SAML request to start login sequence.' + /> + } + value={this.state.idpUrl} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + <TextSetting + id='idpDescriptorUrl' + label={ + <FormattedMessage + id='admin.saml.idpDescriptorUrlTitle' + defaultMessage='Identity Provider Issuer URL:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.idpDescriptorUrlEx', 'Ex "https://idp.example.org/SAML2/issuer"')} + helpText={ + <FormattedMessage + id='admin.saml.idpDescriptorUrlDesc' + defaultMessage='The issuer URL for the Identity Provider you use for SAML requests.' + /> + } + value={this.state.idpDescriptorUrl} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + {idpCert} + <BooleanSetting + id='verify' + label={ + <FormattedMessage + id='admin.saml.verifyTitle' + defaultMessage='Verify Signature:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.verifyDescription' + defaultMessage='When true, Mattermost verifies that the signature sent from the SAML Response matches the Service Provider Login URL' + /> + } + value={this.state.verify} + disabled={!this.state.enable} + onChange={this.handleChange} + /> + <TextSetting + id='assertionConsumerServiceURL' + label={ + <FormattedMessage + id='admin.saml.assertionConsumerServiceURLTitle' + defaultMessage='Service Provider Login URL:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.assertionConsumerServiceURLEx', 'Ex "https://<your-mattermost-url>/login/sso/saml"')} + helpText={ + <FormattedMessage + id='admin.saml.assertionConsumerServiceURLDesc' + defaultMessage='Enter https://<your-mattermost-url>/login/sso/saml. Make sure you use HTTP or HTTPS in your URL depending on your server configuration. This field is also known as the Assertion Consumer Service URL.' + /> + } + value={this.state.assertionConsumerServiceURL} + onChange={this.handleChange} + disabled={!this.state.enable || !this.state.verify} + /> + <BooleanSetting + id='encrypt' + label={ + <FormattedMessage + id='admin.saml.encryptTitle' + defaultMessage='Enable Encryption:' + /> + } + helpText={ + <FormattedMessage + id='admin.saml.encryptDescription' + defaultMessage='When true, Mattermost will decrypt SAML Assertions encrypted with your Service Provider Public Certificate.' + /> + } + value={this.state.encrypt} + disabled={!this.state.enable} + onChange={this.handleChange} + /> + {privKey} + {pubCert} + <TextSetting + id='emailAttribute' + label={ + <FormattedMessage + id='admin.saml.emailAttrTitle' + defaultMessage='Email Attribute:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.emailAttrEx', 'Ex "Email" or "PrimaryEmail"')} + helpText={ + <FormattedMessage + id='admin.saml.emailAttrDesc' + defaultMessage='The attribute in the SAML Assertion that will be used to populate the email addresses of users in Mattermost.' + /> + } + value={this.state.emailAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + <TextSetting + id='usernameAttribute' + label={ + <FormattedMessage + id='admin.saml.usernameAttrTitle' + defaultMessage='Username Attribute:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.usernameAttrEx', 'Ex "Username"')} + helpText={ + <FormattedMessage + id='admin.saml.usernameAttrDesc' + defaultMessage='The attribute in the SAML Assertion that will be used to populate the username field in Mattermost.' + /> + } + value={this.state.usernameAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + <TextSetting + id='firstNameAttribute' + label={ + <FormattedMessage + id='admin.saml.firstnameAttrTitle' + defaultMessage='First Name Attribute:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.firstnameAttrEx', 'Ex "FirstName"')} + helpText={ + <FormattedMessage + id='admin.saml.firstnameAttrDesc' + defaultMessage='The attribute in the SAML Assertion that will be used to populate the first name of users in Mattermost.' + /> + } + value={this.state.firstNameAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + <TextSetting + id='lastNameAttribute' + label={ + <FormattedMessage + id='admin.saml.lastnameAttrTitle' + defaultMessage='Last Name Attribute:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.lastnameAttrEx', 'Ex "LastName"')} + helpText={ + <FormattedMessage + id='admin.saml.lastnameAttrDesc' + defaultMessage='The attribute in the SAML Assertion that will be used to populate the last name of users in Mattermost.' + /> + } + value={this.state.lastNameAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + <TextSetting + id='nicknameAttribute' + label={ + <FormattedMessage + id='admin.saml.nicknameAttrTitle' + defaultMessage='Nickname Attribute:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.nicknameAttrEx', 'Ex "Nickname"')} + helpText={ + <FormattedMessage + id='admin.saml.nicknameAttrDesc' + defaultMessage='(Optional) The attribute in the SAML Assertion that will be used to populate the nickname of users in Mattermost.' + /> + } + value={this.state.nicknameAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + <TextSetting + id='localeAttribute' + label={ + <FormattedMessage + id='admin.saml.localeAttrTitle' + defaultMessage='Preferred Language Attribute:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.localeAttrEx', 'Ex "Locale" or "PrimaryLanguage"')} + helpText={ + <FormattedMessage + id='admin.saml.localeAttrDesc' + defaultMessage='(Optional) The attribute in the SAML Assertion that will be used to populate the language of users in Mattermost.' + /> + } + value={this.state.localeAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + <TextSetting + id='loginButtonText' + label={ + <FormattedMessage + id='admin.saml.loginButtonTextTitle' + defaultMessage='Login Button Text:' + /> + } + placeholder={Utils.localizeMessage('admin.saml.loginButtonTextEx', 'Ex "With OKTA"')} + helpText={ + <FormattedMessage + id='admin.saml.loginButtonTextDesc' + defaultMessage='(Optional) The text that appears in the login button on the login page. Defaults to "With SAML".' + /> + } + value={this.state.loginButtonText} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + </SettingsGroup> + ); + } +}
\ No newline at end of file |