This module installs and configures the PingFederate server using Puppet instead of the more typical interactive shell-script approach.
This module has been tested with PingFederate 8.x - 9.1 and related social adapters (in progress).
- Setup
- Usage
- Basic Usage
- Puppet Module Configuration
- Packages
- Service
- Logging
- Jetty web server settings
- Providing the License Key
- Run.properties
- Cross-Origin Resource Sharing (CORS)
- OGNL expressions
- Administration
- Server SSL/TLS Certificate
- Native SAML2 IdP
- SAML 2.0 SP Configuration
- SAML 2.0 Partner IdP Configuration
- OAuth JDBC configuration
- OAuth client manager
- OAuth server settings
- OAuth returned scope
- OAuth scope selectors
- OAuth Access Token Manager
- OAuth OpenID Connect Policy Contract
- Social Identity Adapters
- OAuth Clients
- Limitations
- Development
PingFederate is a big, complex package with lots of configuration. The intent of this Puppet module is to make it easier to automate installing and configuring the server, eliminating what are otheriwse a number of manual steps.
The module installs PingFederate, performs basic static configuration of the
server, that is, things that are changed prior to starting it up, and post-startup administration.
The static configuration includes the run.properties
and various configuration XML files,
and installation of the license key.
The administrative configuraton is done to the point of being able to build a completely configuration-as-code PingFederate instance that does real work.
If you have access to the RPMs (custom-built; not distributed by PingIdentity, but see this example SPEC file and this init.d script), this module will install them, if not, install it the usual way by downloading and unzipping; you can still use this module to manage the configuration.
This example will only work if you use Hiera to override the default parameters.
include pingfederate
Install PingFederate per the installation manual and disable RPM installation:
class {'pingfederate':
install_dir => '/usr/local/pingfederate-1',
package_ensure => false,
}
Using most of the defaults will just work to get the basic server installed and running. However, it will not do a heck of a lot. You'll need to set a number of the following parameters.
(string)
Path to installation directory of PingFederate server.
Default: '/opt/pingfederate'
(string)
Filesystem owner. Make sure this matches whatever the packaging system uses.
Default: 'pingfederate'
(string)
Filesystem group. Make sure this matches whatever the packaging system uses.
Default: 'pingfederate'
(array[string])
Name(s) of package(s) that contains the PingFederate server.
Default: 'pingfederate-server'
(string)
Ensure that the package is installed. Values are the same as those used by the
Package type
such as present (also called installed), absent, purged, held, latest or a
specfic version string.
Default: 'installed'
(string)
Ensure that the Java JRE package is installed.
Default: 'installed'
(string)
Name of the preferred Java JRE package under RHEL
Default: 'java-1.8.0-oracle'
(string)
Name of the preferred Java JRE package under CentOS
Default: jre1.8.0_111
(string) Service name. Default: 'pingfederate'
(string).
Ensure it is running. Values are the same as those used by the
service resource.
Default: true
(string) Directory for log files. Default: ${install_dir}/log
(integer) Number of days to retain log files. Default: 30
(Array[map]) List of log4j RollingFile overrides. Map keys:
- name: name of the logger
- fileName: log file name.
- filePattern: pattern for the rotated log file name
Default: []
Example:
pingfederate::log_files:
- name: FILE
fileName: 'server.log'
filePattern: 'server.log.%i'
- name: SamlTransaction
fileName: 'transaction.log'
filePattern: 'transaction.log.%i'
- name: SecurityAudit2File
fileName: 'audit.log
filePattern: 'audit.log.%i'
Hint: add extra keys to this map for your own purposes. For example, sumo: true
might be
used to flag this file for ingestion into sumologic (configured in your local profile module).
(Array[map]) List of log4j log level overrides. Map elements:
- name: name of the logger
- level: log level (
DEBUG
,INFO
, etc.)
Default: []
Example:
pingfederate::log_levels:
- name: org.sourceid
level: DEBUG
- name: com.pingidentity.appserver.jetty
level: DEBUG
There are a small number of web server settings that may need to be changed from the defaults. Currently there are only these two which are used to configure form content limits:
(Integer)
Sets the max form size. This is used to resolve the form too large error.
Default: 1000000
Example:
pingfederate::jetty_max_form_content_size: 5000000
(Integer)
Sets the [max form keys]. This is used to resolve the maxFormKeys limit exceeded error.
Default: 20000
Example:
pingfederate::jetty_max_form_keys: 50000
PingFederate is commercial licensed software and will not operate without a license key. Provide this either in your invocation of the module or, preferably, via Hiera.
Provide either a license file or the content as a multiline string.
(string) Content of the pflicense.lic file. Example:
$lic = @(LICENSE)
ID=12345
Organization=Columbia University
Product=PingFederate
Version=8.2
IssueDate=2016-11-3
EnforcementType=3
ExpirationDate=2016-12-3
Tier=Free
SaasProvisioning=true
WSTrustSTS=true
OAuth=true
SignCode=FF07
Signature=302C02141B733A755996FB354FAEDC5211E14E3BC2B4964602144EFBD282F20EF2B77AA8A87DCB17BE533A539720
| LICENSE
class {'pingfederate':
...
license_content => $lic,
...
}
(string) Path to the pflicense.lic file.
The following are used to configure run.properties
. See the
PingFederate documentation
for an explanation. The defaults are as distributed by PingIdentity.
(integer) Default: 9999
(string) No default.
(string) Default: '0.0.0.0'
(string) Default: 'PingFederate'
(integer) Default: 30
(string) Default 'multiple'
(string) Default: 'native'
(string) Default: 'native'
(integer) Default: -1
(none)
(integer) Default: 9031
(integer) Default: -1
(none)
(string) Default: '0.0.0.0'
(string) Default: '0.0.0.0'
(boolean) Default: false
(boolean) Default: false
(string) Default: 'STANDALONE'
(integer) Default 0
(string) No default.
(boolean) Default: false
(string) Default: 'NON_LOOPBACK'
(integer) Default 7600
(integer) Default 7700
(string) Default: 'tcp'
(string) Default: '239.16.96.69'
(integer) Default 7601
(array[string]) No default. Note that the cluster_bind_port
must be the same on the other hosts as this one.
(boolean) Default: false
(string) Default: '224.0.75.75'
(integer) Default 7500
(integer) Default 200000
CORS needs to be enabled as otherwise Javascript OAuth clients will throw an XHR error when attempting XMLHttpRequest (XHR). Most of these settings should be left with default values.
(string)
Comma-separated list of allowed origins for CORS. Default *
You might want to constrain the allowed origins (if you can figure out what the right list should be).
(string)
Allowed HTTP methods for CORS. Default GET,OPTIONS,POST
Deprecation: No longer applicable for PingFederate version >= 9.0
(string)
Allowed HTTP headers for CORS. Default X-Requested-With,Content-Type,Accept,Origin,Authorization
Deprecation: No longer applicable for PingFederate version >= 9.0
(string)
Allowed URL filter mappings for CORS. Default /*
Deprecation: No longer applicable for PingFederate version >= 9.0
(boolean)
Enable OGNL scripting. Default true
(string) Initial administrator user. Default: 'Administrator'
(and seems to be required).
(string) Administrator user's password. The adm_pass
and adm_hash
must match. Default: 'p@Ssw0rd'
(string) Hash of administrator user's password. Must match the password. (I don't currently know how to generate this, so make sure to copy it when you change the password)
(string) Base URL of the pf-admin-api.
Default: "https://${facts['fqdn']}:${admin_https_port}/pf-admin-api/v1"
(string) Base URL for the various services. Set this to your load-balancer's URL.
Default: "https://${facts['fqdn']}:${https_port}"
Use these settings to import a server SSL certificate (optionally signed by a chain of CAs).
You can use openssl
to create these certs and CSRs but make sure to use the
same ssl_cert_passphrase
.
TODO: document with full openssl examples.
(string) The passphrase used to protect the private key.
pingfederate::ssl_cert_passphrase: ThePassword
(string) Base 64 encoded string containing PKCS12 TLS certificate. Example:
pingfederate::ssl_cert_content: |
MIIKPwIBAzCCCfgGCSqGSIb3DQEHAaCCCekEggnlMIIJ4TCCBWUGCSqGSIb3DQEHAaCCBVYEggVS
MIIFTjCCBUoGCyqGSIb3DQEMCgECoIIE+zCCBPcwKQYKKoZIhvcNAQwBAzAbBBQWj4ebhqdFy6yp
...
AgMBhqA=
Produce this string like this and then insert it into your YAML.
$ base64 -b 76 170D457373C.p12 | sed -e 's/^/ /'
(string) PEM encoded string containing signed cert with CA chain.
pingfederate::ssl_cert_csr_content: |
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD
...
-----END CERTIFICATE-----
This file is provided by your CA as a Certificate (w/ chain), PEM encoded.
Produce this string like this and then insert it into your YAML:
sed -e 's/^/ /' pem_encoded.cer
These are the native SAML2 IdP settings used for native console_authentication and admin_api_authentication. The adm_user and adm_pass are used for HTTP Basic Auth.
(string)
SAML 2 EntityID for the native local IdP (that provides the adm_user authentication).
Default: "${facts['hostname']}-ping:urn:saml2"
(string)
SAML 1 issuerID for the native local IdP.
Default: ${facts['hostname']}-ping:urn:saml1
(string) Default: "${facts['hostname']}-ping:urn:wsfed"
(string) HTTP header identifying the IP address of the end-host when coming in via proxy.
You should set these if using a load-balancer, otherwise the source IP address logged will
be that of the load-balancer rather than the actual client.
Default: undefined. Example: X-Forwarded-For
(string) HTTP header identifying the name of the end-host when coming in via proxy.
Default: undefined. Example: X-Forwarded-Host
N.B. The current capability of this module is to configure PingFederate as an SP so as to federate a SAML 2.0 IdP for purposes of the OAuth 2.0 authorization code flow.
auth_policy_contract
(Array[map]) List of authentication policy contracts with these map keys:
saml2_idp
: (Array[map]) Multiple partner IdPs can be configure by this module. Each array item has multiple map keys:
(string)
URL for the SAML2 IDP. For example: https://shibboleth.example.com
(string)
URL-portion for the POST method. Concatenated to the url
. Default: idp/profile/SAML2/POST/SSO
(string)
URL-portion for the redirect. Concatenated to the url
. Default: idp/profile/SAML2/Redirect/SSO
(string)
Entity ID for the SAML2 IDP. For example: urn:mace:incommon:example.com
or https://shibboleth.example.com/idp/shibboleth
(string) User-friendly name for the IdP. Displayed in the authentication selector screen.
(string) SAML2 partner's metadata URL.
(Array[string])
Lost of virtual server entityIDs for the PingFederate SP. Used to override saml2_local_entityID
.
For example: columbia-ping-mfa:urn:saml2
. The first entityID in the list becomes the
defaultVirtualEntityID. Default: []
(map)
Contact info for the IdP operator. Default: {'firstName' => '', 'lastName' => '', 'email' => ''}
(Array[string])
List of allowed SAML2 IdP profiles. Default: ['SP_INITIATED_SSO']
(string) Name of the Authentication Policy Contract to use.
(string)
How IdP gets mapped (RTFM). Default: 'ACCOUNT_MAPPING'
(Array[string])
List of core attributes. Default: ['SAML_SUBJECT']
(Array[string])
List of extended attributes. Default: []
(Array[map])
List of attribute mappings with keys name, type, value. Default: []
Example:
pingfederate::saml2_idp:
- ...
attr_map:
- name: pingAffiliation
type: EXPRESSION
value: >-
#result = #this.get(\"urn:oid:1.3.6.1.4.1.5923.1.1.1.1.9\"),
#result = (#result? #result.toString() : \"\")
.replace(\"[\", \"[\\\"\")
.replace(\"]\", \"\\\"]\")
.replace(\",\", \"\\\",\\\"\")
.replace(\" \", \"\")
- name: subject
type: ASSERTION
value: SAML_SUBJECT
(Array[map])
List of attribute mappings from SAML2 to Oauth with keys name, type, value. Default: []
Example:
pingfederate::saml2_idp:
- ...
oauth_map:
- name: USER_KEY
type: ASSERTION
value: SAML_SUBJECT
- name: USER_NAME
type: ASSERTION
value: SAML_SUBJECT
(string) File path to IdP certificate. NOT IMPLEMENTED.
(string) String containing IdP certificate. Example:
pingfederate::saml2_idp:
- ...
cert_content: |
-----BEGIN CERTIFICATE-----
MIIDRzCCAi+gAwIBAgIUAb+rsLUvjwiVA2iVgiHAFGrtCPgwDQYJKoZIhvcNAQEF
BQAwIjEgMB4GA1UEAxMXc2hpYmJvbGV0aC5jb2x1bWJpYS5lZHUwHhcNMTMwODIy
MTQ1MzUzWhcNMzMwODIyMTQ1MzUzWjAiMSAwHgYDVQQDExdzaGliYm9sZXRoLmNv
...
-----END CERTIFICATE-----
(Array[map]) Mapping of OAuth attributes to fields in the access token(?). Example:
- ...
saml2_oauth_token_map:
- name: username
type: OAUTH_PERSISTENT_GRANT
value: USER_KEY
- name: group
type: OAUTH_PERSISTENT_GRANT
value: group
- name: uid
type: OAUTH_PERSISTENT_GRANT
value: USER_KEY
To enable use of an external JDBC data store, set oauth_jdbc_type to a value (see below).
If it is undef
then the default internal XML-based datastore will be used.
(string) Type of JDBC
OAuth Client Datastore
connector. One of undef
, mysql
,sqlserver
,oracle
,other
. Default: undef
. If other
, you'll need to fill in the following as well.
Otherwise they default to expected values for the given oauth_jdbc_type but can still be used to override the defaults.
N.B. currently only fully implemented for mysql
and sqlserver
.
(string)
JDBC database name (also found in oauth_jdbc_url
)
Default: 'pingfed'
(string)
JDBC user name.
Default: 'pingfed'
(string)
JDBC password.
Default: 'pingfed'
(string)
JDBC database host.
Default: localhost
(string)
JDBC database port.
Default: 3306
.
(string) Name of the JDBC driver class. Default: Set based on the oauth_jdbc_type
- mysql:
com.mysql.jdbc.Driver
- sqlserver:
com.microsoft.sqlserver.jdbc.SQLServerDriver
(string)
JDBC connector and command-line interface (CLI) pacakge(s). See also oauth_jdbc_maven
and oauth_jdbc_nexus
for alternative ways to find the JDBC connector JAR.
Default: Set based on the oauth_jdbc_type
- mysql:
['mysql','mysql-connector-java']
- sqlserver: none
(string)
Ensure that the package is installed.
Default: 'installed'
(map)
Identifies the nexus repo that contains the JDBC connector (when it's not available as a package).
Has three keys that roughly correspond to those used by archive::nexus
: url
, repo
and gav
(groupId:appId:version). Default: none.
Example:
use_v3 => true,
url => 'https://user:[email protected]/
repo => 'central',
gav => 'com.microsoft.sqlserver:mssql-jdbc:7.0.0.jre8',
(string) Identifies the maven URL for the JDBC connector (when it's not available as a package or in nexus).
Default: Set based on the oauth_jdbc_type
- mysql:
http://central.maven.org/maven2/mysql/mysql-connector-java/8.0.12/mysql-connector-java-8.0.12.jar
- sqlserver:
http://central.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/7.0.0.jre8/mssql-jdbc-7.0.0.jre8.jar
(string)
Directory where the JDBC jar file can be found after being installed from a package or JAR repo.
Default: /usr/share/java
(string) Name of the jar file. Default: Set based on the oauth_jdbc_type
- mysql:
mysql-connector-java.jar
- sqlserver:
mssql-jdbc-7.0.0.jre8.jar
(string) JDBC URL.
Default:
- mysql:
jdbc:mysql://<host>:<port>/<database>?autoReconnect=true
- sqlserver:
jdbc:sqlserver://<host>:<port>;databaseName=<database>
(string) JDBC validation test. Set based on the oauth_jdbc_type.
Default:
- mysql:
SELECT 1 from dual
- sqlserver:
SELECT getdate()
(string) Command to execute to create the database schema. Set based on the oauth_jdbc_type
(string) Command to execute to initialize the OAuth Client Manager database schema. Set based on the oauth_jdbc_type
(string) Command to execute to initialize the OAuth Access database schema. Set based on the oauth_jdbc_type
(string) Command to execute to initialize the Account Linking database schema. Set based on the oauth_jdbc_type
The OAuth client manager API is used to add OAuth clients to the PingFederate service. (This capability is required for MuleSoft AnyPoint API Manager functionality, for example.) You should override the user name and/or password when invoking the pingfederate class.
(string)
Oauth client manager user name. Default clientmgr
(If you need to have more than one client manager user, you'll need to enhance this module
to deal with that.)
(string)
Oauth client manager user password. Default ProviderP@55
Make sure the password you supply meets the minimum password requirements or you may see this:
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: (422, '')
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: {
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: "validationErrors": [
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: {
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: "fieldPath": "configuration.tables[0].rows[0].fields[1].value",
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: "message": "Password must contain at least 8 characters, at least 1 numeric character, at least 1 uppercase and 1 lowercase letter, at least 2 alphabetic characters.",
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: "errorId": "plugin_validation_error"
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: }
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: ],
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: "message": "Validation error(s) occurred. Please review the error(s) and address accordingly.",
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: "resultId": "validation_error"
Notice: /Stage[main]/Pingfederate::Server_settings/Exec[pf-admin-api POST ${pcv}]/returns: }
(array of hashes) OAuth Client Metadata. Each hash has the following keys:
- parameter: (string) The metadata name.
- description: (string) The metadata description.
- multiValued: (boolean) If the field should allow multiple values.
(map) OAuth Dynamic Client Registration (RFC 7691) settings. (These are directly mapped to the API parameters).
- initialAccessTokenScope: (string): Access Token scope required to allow dynamic client registration.
- restrictCommonScopes: (boolean) Restrict common scopes.
- restrictedCommonScopes: (array[string]): The common scopes to restrict.
- allowedExclusiveScopes (array[string]): The exclusive scopes to allow.
- requestPolicyRef: (link) The CIBA request policy.
- enforceReplayPrevention: (boolean) Enforce replay prevention.
- requireSignedRequests: (boolean) Require signed requests.
- defaultAccessTokenManagerRef: (link) The default access token manager for this client.
- persistentGrantExpirationType: (string)
INDEFINITE_EXPIRY
orSERVER_DEFAULT
orOVERRIDE_SERVER_DEFAULT
. Defaults toSERVER_DEFAULT
. - persistentGrantExpirationTime: (integer) The persistent grant expiration time.
- persistentGrantExpirationTimeUnit: (strng) =
MINUTES
orDAYS
orHOURS
. The persistent grant expiration time unit. - persistentGrantIdleTimeoutType: (string)
INDEFINITE_EXPIRY
orSERVER_DEFAULT
orOVERRIDE_SERVER_DEFAULT
. Defaults toSERVER_DEFAULT
. - persistentGrantIdleTimeout: (integer) The persistent grant idle timeout.
- persistentGrantIdleTimeoutTimeUnit:(string)
MINUTES
orDAYS
orHOURS
- clientCertIssuerType: (string) =
NONE
orTRUST_ANY
orCERTIFICATE
- clientCertIssuerRef: (link): Client TLS Certificate Issuer DN.
- refreshRolling: (string) =
SERVER_DEFAULT
orDONT_ROLL
orROLL
. Defaults toSERVER_DEFAULT
. - oidcPolicy: (string): Open ID Connect Policy settings.
- policyRefs: (array[link]) The client registration policies.
- deviceFlowSettingType: (string)
SERVER_DEFAULT
orOVERRIDE_SERVER_DEFAULT
- userAuthorizationUrlOverride: (string): The URL is used as
verification_url
andverification_url_complete
values in a device flow authorization request. - pendingAuthorizationTimeoutOverride: (integer) The
device_code
anduser_code
timeout, in seconds. - devicePollingIntervalOverride: (integer) The amount of time client should wait between polling requests, in seconds.
- bypassActivationCodeConfirmationOverride: (boolean) Indicates if the Activation Code Confirmation page should be bypassed if
verification_url_complete
is used by the end user to authorize a device. - requireProofKeyForCodeExchange: (boolean) Determines whether Proof Key for Code Exchange (PKCE) is required for the dynamically created client.
- cibaPollingInterval: (integer) The minimum amount of time in seconds that the Client must wait between polling requests to the token endpoint. The default is 3 seconds.
- cibaRequireSignedRequests: (boolean) Determines whether CIBA signed requests are required for this client.
Here's an example in a Hiera YAML file:
pingfederate::oauth_dynamic_client_registration:
allowedExclusiveScopes: []
cibaPollingInterval: 3
cibaRequireSignedRequests: false
clientCertIssuerType: NONE
deviceFlowSettingType: SERVER_DEFAULT
enforceReplayPrevention: false
initialAccessTokenScope: dyn-client
persistentGrantExpirationType: SERVER_DEFAULT
persistentGrantIdleTimeoutType: SERVER_DEFAULT
policyRefs: []
refreshRolling: SERVER_DEFAULT
requireProofKeyForCodeExchange: false
requireSignedRequests: false
restrictCommonScopes: true
restrictedCommonScopes:
- address
- create
- delete
- email
- openid
- profile
- read
- update
(array of hashes) Allowable OAuth scopes. Each hash has the following keys:
- name: (string) scope name
- description: (string) descriptive text that is displayed to the user for authorization_code flow. Here's an example in a Hiera YAML file:
pingfederate::oauth_svc_scopes:
- name: read
description: Can read stuff
- name: write
description: Can write stuff
(array of hashes) Groupings of OAuth scopes. Each hash has the following keys:
- name: (string) scope group name
- description: (string) descriptive text that is displayed to the user for authorization_code flow.
- scopes: (array[string]) Names of scopes defined in
oauth_svc_scopes
. Here's an example in a Hiera YAML file:
pingfederate::oauth_svc_scope_groups:
- name: readwrite
description: Can read and write stuff
scopes:
- read
- write
(array[string])
Oauth server persistent grant contract core attributes. Default: ['USER_KEY','USER_NAME']
(array[string]) Oauth server persistent grant contract extended attributes.
The response scope
for an Access Token request per
RFC 6749 section 4.1
is "OPTIONAL, if identical to the scope requested by the client; otherwise, REQUIRED."
(boolean)
Set to true
to always return the granted scopes in the response, even if identical.
Default: false
Example:
pingfederate::oauth_return_scope_always: true
Authentication scope selectors are used to match requested oauth scopes to select an authentication provider (e.g. which social identity adapter or IdP connection to use for the Authorization Code flow).
(Array[map]) with keys adapter, type and scopes, where adapter is an IdP Adapter or
IdP Connection name, type is one of IDP_ADAPTER
or IDP_CONNECTION
and
scopes is a list of one or more previosuly defined oauth scopes
in oauth_svc_scopes
and oauth_svc_scope_groups
that must match to trigger the given Authentication
Selector.
Default: []
Example:
pingfederate::oauth_scope_selectors:
- adapter: facebook
type: IDP_ADAPTER
scopes:
- auth-facebook
- adapter: google
type: IDP_ADAPTER
scopes:
- auth-google
- adapter: Columbia University Dev
type: IDP_CONNECTION
scopes:
- auth-columbia
(boolean)
Fail to login if no oauth_scope_selector
was selected. If false
an menu of available authentication
options is presented to the user. If false
no menu is presented. Default: false
Currently only a single Access Token Manager may be configured. If you need more, please submit a PR!
(string) ID of the access token manager. No default.
(array[string]) List of core attributes. No default.
(array[string]) List of extended attributes. No default.
Currently only a single OIDC Policy Contract may be configured. If you need more, please please submit a PR!
(string) The ID of the policy. No default.
(boolean)
true
to include userinfo
in id_token
. Default: false
(array of hashes) Mappings from token manager core attributes to OpenID Connect attributes. Each hash has the following keys:
- name: Name of the source
- type: type of the source: TOKEN or one of the other SourceTypeIdKey like TEXT, EXPRESSION and so on.
- value: Name of the attribute
Default:
[{name => 'sub', type => 'TOKEN', value =>'sub'}]
(array of hashes) Mappings from token manager core attributes to OpenID Connect attributes.
Default: []
. Here's an example:
...
oauth_oidc_policy_extd_map => [{'name' => 'group', 'type' => 'TOKEN', 'value' => 'group'}],
...
(hash of hashes)(PingFederate version 9+ only) Mappings of OpenID scopes to claims returned for those scopes.
Default: {}
. In PF versions before 9, mappings are fixed per the
OpenID connect standard (for profile, address,
phone, and email scopes). Here's an example:
oauth_oidc_policy_scope_attr_map => {
'profile' => ['given_name', 'family_name', 'name'],
'email' => ['email'],
'address' => ['address'],
'https://apis.example.com/scope/group' => ['https://apis.example.com/claim/group'], # a custom claim and scope to select it
},
OAuth Authentication policy contract mappings. Default []
. Example:
...
oauth_authn_policy_map => [{'name' => 'USER_KEY', 'type' => 'AUTHENTICATION_POLICY_CONTRACT', 'value' => 'subject'},
{'name' => 'USER_NAME', 'type' => 'AUTHENTICATION_POLICY_CONTRACT', 'value' => 'subject'},
{'name' => 'group', 'type' => 'AUTHENTICATION_POLICY_CONTRACT', 'value' => 'affiliation'},
],
...
(Map of maps) Social identity adapters have the following map keys. Example:
pingfederate::social_adapter:
facebook:
enable: true
package_ensure: 1.3.2-5.el6
app_id: 98765432112345
app_secret: xxf2b942ebb849f7abc629576bfe8
oauth_token_map:
- name: username
type: ADAPTER
value: name
- name: group
type: TEXT
value: facebook
- name: uid
type: ADAPTER
value: id
oauth_idp_map:
- name: USER_KEY
type: ADAPTER
value: id
- name: USER_NAME
type: ADAPTER
value: name
- name: group
type: TEXT
value: facebook
(string) Name of the adapter. One of: facebook, google, twitter, linkedin, windowslive.
(boolean)
Set to true to enable the CIC adapter. Default: false
(array[string])
Name of package(s) that contains the adapter.
Default: 'pingfederate-<name>-adapter'
(string)
Ensure that the package is installed.
Default: 'installed'
(string) application key or ID.
(string) Application secret.
(Map) Mapping of attributes to oauth token attributes consisting of name
,type
,value
triples.
type
is one of ADAPTER, TEXT, EXPRESSION, ...
(Map) Mapping of attributes to idp attributes consisting of name
,type
,value
triples.
type
is one of ADAPTER, TEXT, EXPRESSION, ...
(Map) Default values for each OAuth client. These values get overriden by the individual client definitions with these defaults (which you can override as you see fit):
pingfederate::oauth_client_default:
redirectUris: []
grantTypes: []
name: ''
description: ''
logoUrl: ''
validateUsingAllEligibleAtms: false
refreshRolling: SERVER_DEFAULT
persistentGrantExpirationType: SERVER_DEFAULT
persistentGrantExpirationTime: 0
persistentGrantExpirationTimeUnit: DAYS
bypassApprovalPage: true
restrictScopes: true
restrictedScopes: []
oidcPolicy:
grantAccessSessionRevocationApi: false
pingAccessLogoutCapable: false
logoutUris: []
jwksSettings:
jwks: ''
jwksUrl: ''
clientAuth:
type: SECRET
secret: ''
clientCertIssuerDn: ''
clientCertSubjectDn: ''
enforceReplayPrevention: false
(Map of maps) for each OAuth client, keyed by a unique client identifier.
See the oauth_client_default
description for details.
Example:
demo_trusted_client:
name: demo_trusted_client
description: training demo trusted auth-none or auth-columbia client
grantTypes:
- IMPLICIT
- AUTHORIZATION_CODE
- CLIENT_CREDENTIALS
- REFRESH_TOKEN
bypassApprovalPage: false
redirectUris:
- http://localhost:5432/oauth2client
- https://www.getpostman.com/oauth2/callback
restrictedScopes:
- auth-none
- auth-columbia
- demo-netphone-admin
- create
- read
- update
- delete
- openid
- profile
- email
clientAuth:
type: SECRET
secret: 73a7176Ab322549FCBEF46554d3381d5
clientAuth
type can be one of 'NONE', 'SECRET', 'CERTIFICATE', or 'PRIVATE_KEY_JWT'.
This has only been tested on EL 6 with Java 1.8. It might work elsewhere. Let me know!
Changes to certain configuration variables after an initial Puppet run do not properly get reflected in the PingFederate server state. The best way to resolve these is to wipe PingFederate off the system and re-run Puppet:
$ sudo service pingfederate stop
$ sudo yum -y erase pingfederate-server
$ sudo rm -rf /opt/pingfederate*
$ sudo puppet agent -t ...
Known issues include:
- Changing
pingfederate::service_api_baseURL
does not properly remove references to the old URL. - Setting a social media adapter like
pingfederate::facebook_adapter: true
and then setting itfalse
fails to properly remove the adapter references. - A failed JDBC connection at initial configuration (e.g. database not running or no permission) will not get fixed later, even if the database access is fixed.
- If you set some
pingfederate::log_levels
and then remove them, the last settings remain; original values are not restored. - Facebook adapter 2.x configuration is broken.
The package was built to use PingFederate as an OAuth2 Server with SAML and social identity federation for the authorization code flow. PingFederate has many other features which are not yet configured here.
Please fork and submit PRs on github as you add fixes and features.
See this Augeas blog post
for a great introduction, Use the augtool
command to test Augeas parsing before wasting too much
time with using it with Puppet.
Of note, the Properties lens appears to be broken for Java Properties files like run.properties. At least that's been my experience with the (out-of-date) version of the augeas-libs on CentOS 6. I ended up using puppetlabs-inifile for that.
However, it appears to work well for XML files. Here's an example of looking at the admin-user password file which will help write the puppet augeas expression:
[vagrant@localhost opt]$ augtool -A
augtool> set /augeas/load/Xml/lens Xml.lns
augtool> set /augeas/load/Xml/incl /opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml
augtool> load
augtool> ls /files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/
#declaration/ = (none)
adm:administrative-users/ = (none)
augtool> print /files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/#declaration
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/#declaration/#attribute
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/#declaration/#attribute/version = "1.0"
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/#declaration/#attribute/encoding = "UTF-8"
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/adm:administrative-users
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/adm:administrative-users/#attribute
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/adm:administrative-users/#attribute/multi-admin = "true"
/files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/adm:administrative-users/#attribute/xmlns:adm = "http://pingidentity.com/2006/01/admin-users"
...
augtool> ls /files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/adm:administrative-users/
#attribute/ = (none)
#text =
adm:user/ = (none)
augtool> ls /files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/adm:administrative-users/adm:user/
adm:active/ adm:description/ adm:user-name/ #text[14] #text[7]
adm:admin/ adm:email-address/ #text[1] #text[2] #text[8]
adm:admin-manager/ adm:hash/ #text[10] #text[3] #text[9]
adm:auditor/ adm:password-change-required/ #text[11] #text[4]
adm:crypto-manager/ adm:phone-number/ #text[12] #text[5]
adm:department/ adm:salt #text[13] #text[6]
augtool> ls /files/opt/pingfederate-8.2.2/pingfederate/server/default/data/pingfederate-admin-user.xml/adm:administrative-users/adm:user/adm:active/
#text = true
augtool>
Here's the corresponding password file (with the hash hosed-up just a little):
<?xml version="1.0" encoding="UTF-8"?>
<adm:administrative-users multi-admin="true" xmlns:adm="http://pingidentity.com/2006/01/admin-users">
<adm:user>
<adm:user-name>Administrator</adm:user-name>
<adm:salt/>
<adm:hash>k1H1o2jc66xxxDjJKq85Sr22QNk143S20oR2Yyt2kqo.5Cu-mnqB.2</adm:hash>
<adm:phone-number xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<adm:email-address xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<adm:department xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<adm:description>Initial administrator user.</adm:description>
<adm:admin-manager>true</adm:admin-manager>
<adm:admin>true</adm:admin>
<adm:crypto-manager>true</adm:crypto-manager>
<adm:auditor>false</adm:auditor>
<adm:active>true</adm:active>
<adm:password-change-required>false</adm:password-change-required>
</adm:user>
</adm:administrative-users>
N.B. If the XML file is not pre-existing, the Augeas XML lens will not "pretty-print" the XML; it will all be run together with no indentation. Do not fear as this is still valid XML.
See these additional notes for more background on how to develop configuration puppet modules based on diffs of XML config files.
Most configuration is done via the PingFederate Administrative API.
Unfortunately, one key configuration is done by editing XML files, using data returned from the API call. For example, the process for adding a mysql data store for oauth client management consists of:
-
Install the appropriate JDBC connector jar file in
<pf-install>/server/default/lib
. -
POST https://localhost:9999/pf-admin-api/v1/dataStores
with a JSON map to configure the JDBC connector. -
Edit
<pf-install>/server/default/conf/META-INF/hivemodule.xml
to enable the JDBC implementation. -
Edit
<pf-install>server/default/data/config-store/org.sourceid.oauth20.domain.ClientManagerJdbcImpl.xml
to include the JNDI-name that was returned by the POST.
pf-admin-api.erb is a templated Python script which invokes the REST API. The script is templated to embed the default name of "this" server's configuration file as a default value.
Input data for POSTs done by pf-admin-api are also templated. Installing the JSON file is the trigger to Execing the pf-admin-api script. The script waits for the server to come up so can be used as 'waiter' when the service has to be restarted, which unfortunately is necessary at a point in the configuration process (configuring the JDBC connector).
Usage: pf-admin-api [options] resource
Options:
-h, --help show this help message and exit
-c CONFIG, --config=CONFIG
Name of configuration file [default:
/opt/pingfederate/local/etc/pf-admin-cfg.json]
-m METHOD, --method=METHOD
HTTP method, one of GET,PUT,POST,PATCH,DELETE
[default: GET]
-j JSON, --json=JSON JSON file to POST
-i ID, --id=ID write resource id to file [default: none]
-k IDKEY, --idkey=IDKEY
resource id primary key [default: id]
-r RESPONSE, --response=RESPONSE
write succesful JSON response to file [default: -]
-s SUBST, --subst=SUBST
--subst key=filename. Substitutes the given @@key@@
in the JSON document with the content of the named file.
May be repeated to substitute multple keys.
--timeout=TIMEOUT Seconds before timeout [default: 10]
--retries=RETRIES Number of retries [default: 5]
--verify verify SSL/TLS server certificate [default: False]
If an API call returns an error (response status >= 400) then the input json file
(e.g. oauth_clients_demo_client.json
) will be renamed with a .fail
extension
(e.g. oauth_clients_demo_client.json.fail
). This will help trigger a subsequent
Puppet run (after what cause the error has been resolved).
Here's an example of GET of the server version:
$ sudo /opt/pingfederate/local/bin/pf-admin-api version
(200, 'OK')
{
"version": "8.2.2.0"
}
This shows up in the Puppet log as:
Notice: /Stage[main]/Pingfederate::Admin/Exec[pf-admin-api version]/returns: (200, 'OK')
Notice: /Stage[main]/Pingfederate::Admin/Exec[pf-admin-api version]/returns: "version": "8.2.2.0"
Notice: /Stage[main]/Pingfederate::Admin/Exec[pf-admin-api version]/returns: executed successfully
And here's an idempotent POST. In this example, the POST had previously happened so it gets changed to a PUT
just in case there are some changed values in dataStores.json
. This presupposes that dataStores.json.out
is present since it contains the id that was assigned by the initial POST. (A future improvement might be
to GET the collection and find the right id.)
$ sudo /opt/pingfederate/local/bin/pf-admin-api -m POST -j /opt/pingfederate/local/etc/dataStores.json -r /opt/pingfederate/local/etc/dataStores.json.out dataStores
Resource exists. Changing POST to PUT.
(200, 'OK')
This is just an example of GET of the dataStores collection:
$ sudo /opt/pingfederate/local/bin/pf-admin-api dataStores
(200, 'OK')
{
"items": [
{
"userName": "sa",
"encryptedPassword": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoia2RDODV1YnZhRyIsInZlcnNpb24iOiI4LjIuMi4wIn0..9PlaJ2A0UNIA-j2_k_e3yA.KgxQfRAnMxxxAiFKUrf-2Q.6DtxtZP15T_0fjVozcxNRw",
"connectionUrl": "jdbc:hsqldb:${pf.server.data.dir}${/}hypersonic${/}ProvisionerDefaultDB;hsqldb.lock_file=false",
"allowMultiValueAttributes": false,
"driverClass": "org.hsqldb.jdbcDriver",
"maskAttributeValues": false,
"type": "JDBC",
"id": "ProvisionerDS"
},
{
"userName": "pingfed",
"encryptedPassword": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoia2RDODV1YnZhRyIsInZlcnNpb24iOiI4LjIuMi4wIn0..wsr556B1qLMDo5s7g_3oTw.OPGaQh0bsC6zzzCQBicVvQ.HHqz9Ad7mswBRE3J1IEFyg",
"connectionUrl": "jdbc:mysql://localhost/pingfed?autoReconnect=true",
"allowMultiValueAttributes": true,
"driverClass": "com.mysql.jdbc.Driver",
"maskAttributeValues": false,
"type": "JDBC",
"id": "JDBC-282B7303FBE88768437753B22951A424E7F12068",
"validateConnectionSql": "SELECT 1 from dual"
}
]
}
And, if you want to use the script to use the admin API of a different PingFederate server, simply create a configuration file:
$ cat pf.json
{
"description": "configuration file for /opt/pingfederate/local/bin/pf-admin-api",
"baseURL": "https://oauth.example.com:9999/pf-admin-api/v1",
"user": "Administrator",
"pass": "password123"
}
$ /opt/pingfederate/local/bin/pf-admin-api -c pf.json version
(200, 'OK')
{
"version": "8.2.2.0"
}
Note that the --id file is useful with the concat
module to concatenate IDs
into templated JSON or XML fragments.
This script edits the XML files in an Exec notified by the API call rather than having Puppet manage them. This is ugly but the only way to make it happen in one unit of work. (It's really the fault of the app for not having a clean set of APIs that do everything.) It's kinda ugly, using augeas three times:
- pulls the jndi-name out of the API result file created by pf-admin-api.
- Edits several files in
<pf_install_dir>server/default/data/config-store
.
There's also an oauth_jdbc_revert_augeas
script that reverts back to the built-in non-JDBC datastore.
- Allow for multiple Access Token Managers
- Configure for different token lifetimes.
- Generally fix all instances of singleton configurations to allow for multiples.