This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/static/directives/config/config-setup-tool.html

1668 lines
76 KiB
HTML

<div class="config-setup-tool-element">
<div class="cor-loader" ng-if="!config"></div>
<div ng-show="config && config['SUPER_USERS']">
<form id="configform" name="configform">
<!-- Custom SSL certificates -->
<div class="co-panel" id="custom-ssl">
<div class="co-panel-heading">
<i class="fa fa-certificate"></i> Custom SSL Certificates
</div>
<div class="co-panel-body">
<div class="config-certificates-field"></div>
</div>
</div>
<!-- Basic Configuration -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-gears"></i> Basic Configuration
</div>
<div class="co-panel-body">
<table class="config-table">
<tr>
<td>Enterprise Logo URL:</td>
<td>
<span class="config-string-field" binding="config.ENTERPRISE_LOGO_URL"
placeholder="http://example.com/logo.png"></span>
<div class="help-text">
Enter the full URL to your company's logo.
</div>
</td>
<td>
<img class="registry-logo-preview" ng-src="{{ config.ENTERPRISE_LOGO_URL }}">
</td>
</tr>
<tr>
<td class="non-input">Contact Information:</td>
<td colspan="2">
<span class="config-contacts-field" binding="config.CONTACT_INFO"></span>
<div class="help-text" style="margin-top: 10px;">
Information to show in the Contact Page. If none specified, CoreOS contact information
is displayed.
</div>
</td>
</tr>
</table>
</div>
</div>
<!-- Server Configuration -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-cloud"></i> Server Configuration
</div>
<div class="co-panel-body">
<table class="config-table">
<tr>
<td>Server Hostname:</td>
<td>
<span class="config-string-field" binding="config.SERVER_HOSTNAME"
placeholder="Hostname (and optional port if non-standard)"
pattern="{{ HOSTNAME_REGEX }}"></span>
<div class="help-text">
The HTTP host (and optionally the port number if a non-standard HTTP/HTTPS port) of the location
where the registry will be accessible on the network
</div>
</td>
</tr>
<tr>
<td>TLS:</td>
<td>
<select class="form-control" ng-model="mapped.TLS_SETTING">
<option value="internal-tls">Quay Enterprise handles TLS</option>
<option value="external-tls">My own load balancer handles TLS (Not Recommended)</option>
<option value="none">None (Not For Production)</option>
</select>
<div class="co-alert co-alert-danger" ng-if="mapped.TLS_SETTING == 'none'" style="margin-bottom: 20px">
Running without TLS should not be used for production workloads!
</div>
<div class="co-alert co-alert-warning" ng-if="mapped.TLS_SETTING == 'external-tls'" style="margin-bottom: 20px">
Terminating TLS outside of Quay Enterprise can result in unusual behavior if the external load balancer is not
configured properly. <strong>This option is not recommended for simple setups</strong>. Please contact support
if you encounter problems while using this option.
</div>
<div class="co-alert co-alert-info" ng-if="mapped.TLS_SETTING == 'internal-tls'" style="margin-bottom: 20px">
Enabling TLS also enables <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security">HTTP Strict Transport Security</a>.<br/>
This prevents downgrade attacks and cookie theft, but browsers will reject all future insecure connections on this hostname.
</div>
<table class="config-table" ng-if="mapped.TLS_SETTING == 'internal-tls'">
<tr>
<td class="non-input">Certificate:</td>
<td>
<span class="config-file-field" filename="ssl.cert" has-file="hasfile.SSLCert"></span>
<div class="help-text">
The certificate must be in PEM format.
</div>
</td>
</tr>
<tr>
<td class="non-input">Private key:</td>
<td>
<span class="config-file-field" filename="ssl.key" has-file="hasfile.SSLKey"></span>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</div>
<!-- Consistency Settings -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa ci-shared-database"></i> Data Consistency Settings
</div>
<div class="co-panel-body">
<div class="description">
<p>Relax constraints on consistency guarantees for specific operations
to enable higher performance and availability.
</p>
</div>
<table class="config-table">
<tr>
<td>
<div class="config-bool-field" binding="config.ALLOW_PULLS_WITHOUT_STRICT_LOGGING">
Allow repository pulls even if audit logging fails.
<div class="help-text">
If enabled, failures to write to the audit log will fallback from
the database to the standard logger for registry pulls.
</div>
</div>
</td>
</tr>
</table>
</div>
</div> <!-- /Consistency Settings -->
<!-- Time Machine -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-history"></i> Time Machine
</div>
<div class="co-panel-body">
<div class="description">
<p>Time machine keeps older copies of tags within a repository for the configured period
of time, after which they are garbage collected. This allows users to
revert tags to older images in case they accidentally pushed a broken image. It is
highly recommended to have time machine enabled, but it does take a bit more space
in storage.
</p>
</div>
<table class="config-table">
<!-- This `ng-if` true is *required* to create a new scope so that the <form> inside
the config-list-field doesn't try to submit the *outer* form when the Add button
is clicked. This is highly stupid, but until we figure out a better way, such it
shall be.
-->
<tr ng-if="true">
<td>Allowed expiration periods:</td>
<td>
<span class="config-list-field" item-title="Tag Expiration Period" binding="config.TAG_EXPIRATION_OPTIONS" item-pattern="[0-9]+(m|w|h|d|s)"></span>
<div class="help-text">
The expiration periods allowed for configuration. The default tag expiration *must* be in this list.
</div>
</td>
</tr>
<tr>
<td>Default expiration period:</td>
<td>
<span class="config-string-field" binding="config.DEFAULT_TAG_EXPIRATION"
pattern="[0-9]+(m|w|h|d|s)"></span>
<div class="help-text">
The default tag expiration period for all namespaces (users and organizations). Must be expressed in a duration string form: <code>30m</code>, <code>1h</code>, <code>1d</code>, <code>2w</code>.
</div>
</td>
</tr>
<tr>
<td>Allow users to select expiration:</td>
<td>
<div class="config-bool-field" binding="config.FEATURE_CHANGE_TAG_EXPIRATION">
Enable Expiration Configuration
<div class="help-text">
If enabled, users will be able to select the tag expiration duration for the namespace(s) they
administrate, from the configured list of options.
</div>
</div>
</td>
</tr>
</table>
</div>
</div> <!-- /Time Machine -->
<!-- Redis -->
<div class="co-panel">
<div class="co-panel-heading">
<img src="/static/img/redis-small.png"> redis
</div>
<div class="co-panel-body">
<div class="description">
<p>A <a href="http://redis.io" ng-safenewtab>redis</a> key-value store is required for real-time events and build logs.</p>
</div>
<table class="config-table">
<tr>
<td>Redis Hostname:</td>
<td>
<span class="config-string-field" binding="mapped.redis.host"
placeholder="The redis server hostname"
pattern="{{ HOSTNAME_REGEX }}"
validator="validateHostname(value)"></span>
</td>
</tr>
<tr>
<td>Redis port:</td>
<td>
<span class="config-numeric-field" binding="mapped.redis.port" default-value="6379"></span>
<div class="help-text">
Access to this port and hostname must be allowed from all hosts running
the enterprise registry
</div>
</td>
</tr>
<tr>
<td>Redis password:</td>
<td>
<input class="form-control" type="password" ng-model="mapped.redis.password"
placeholder="Optional password for connecting to redis">
</td>
</tr>
</table>
</div>
</div> <!-- /Redis -->
<!-- Registry Storage -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-download"></i> Registry Storage
</div>
<div class="co-panel-body">
<div class="description">
<p>
Registry images can be stored either locally or in a remote storage system.
<strong>A remote storage system is required for high-availability systems.</strong>
</p>
<div class="config-bool-field feature-storage-replication" binding="config.FEATURE_STORAGE_REPLICATION">
Enable Storage Replication
<div class="help-text">
If enabled, replicates storage to other regions. See <a href="https://tectonic.com/quay-enterprise/docs/latest/geo-replication.html" ng-safenewtab>documentation</a> for more information.
</div>
</div>
<div class="storage-config" ng-class="$last ? 'last' : ''" ng-repeat="sc in storageConfig">
<table class="config-table">
<tr>
<td class="non-input">Location ID:</td>
<td>
<input class="form-control" ng-if="allowChangeLocationStorageConfig(sc.location)" ng-class="storageConfigError[$index].location ? 'ng-invalid' : ''" ng-model="sc.location" ng-pattern="/^[a-zA-Z0-9_-]+$/" required>
<div ng-if="!allowChangeLocationStorageConfig(sc.location)">
{{ sc.location }}
</div>
<div class="co-alert co-alert-danger" ng-show="storageConfigError[$index].location">
{{ storageConfigError[$index].location }}
</div>
<div class="input-util" ng-if="allowRemoveStorageConfig(sc.location)"><a class="remove-link" ng-click="removeStorageConfig(sc)">Remove</a></div>
</td>
</tr>
<tr ng-if="config.FEATURE_STORAGE_REPLICATION">
<td class="non-input">Set Default:</td>
<td>
<div class="config-bool-field" binding="sc.defaultLocation">
Replicate to storage engine by default
</div>
</td>
</tr>
<tr>
<td class="non-input">Storage Engine:</td>
<td>
<select class="form-control" ng-class="storageConfigError[$index].engine ? 'ng-invalid' : ''" ng-model="sc.data[0]">
<option value="LocalStorage">Locally mounted directory</option>
<option value="S3Storage">Amazon S3</option>
<option value="AzureStorage">Azure Blob Storage</option>
<option value="GoogleCloudStorage">Google Cloud Storage</option>
<option value="RadosGWStorage">Ceph Object Gateway (RADOS)</option>
<option value="SwiftStorage">OpenStack Storage (Swift)</option>
<option value="CloudFrontedS3Storage">CloudFront + Amazon S3</option>
</select>
<div class="co-alert co-alert-danger" ng-if="storageConfigError[$index].engine">
{{ storageConfigError[$index].engine }}
</div>
</td>
</tr>
<!-- Fields -->
<tr ng-repeat="field in STORAGE_CONFIG_FIELDS[sc.data[0]]">
<td>{{ field.title }}:</td>
<td>
<span class="config-map-field"
binding="sc.data[1][field.name]"
ng-if="field.kind == 'map'"
keys="field.keys"></span>
<span class="config-string-field"
binding="sc.data[1][field.name]"
placeholder="{{ field.placeholder }}"
pattern="{{ field.pattern }}"
ng-if="field.kind == 'text'"
is-optional="field.optional"></span>
<span class="config-bool-field"
binding="sc.data[1][field.name]"
ng-if="field.kind == 'bool'">
{{ field.placeholder }}
</span>
<span class="config-file-field"
filename="{{ sc.location + '-' + field.filesuffix }}"
binding="sc.data[1][field.name]"
has-file="hasfile['contact-' + sc.location + '-' + field.filesuffix]"
ng-if="field.kind == 'file'"></span>
<div ng-if="field.kind == 'option'">
<select class="form-control" ng-model="sc.data[1][field.name]">
<option ng-repeat="value in field.values" value="{{ value }}"
ng-selected="sc.data[1][field.name] == value">{{ value }}</option>
</select>
</div>
<div class="help-text" ng-if="field.help_text">
{{ field.help_text }}
</div>
<div class="help-text" ng-if="field.help_url">
See <a href="{{ field.help_url }}" ng-safenewtab>Documentation</a> for more information
</div>
</td>
</tr>
</table>
</div>
<div class="add-storage-link" ng-if="canAddStorageConfig()">
<a ng-click="addStorageConfig()">Add Additional Storage Engine</a>
</div>
</div>
</div>
</div>
<!-- Action log archiving -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-archive"></i> Action Log Rotation and Archiving
</div>
<div class="co-panel-body">
<div class="description">
<p>
All actions performed in <span class="registry-name"></span> are automatically logged. These logs are stored in a database table, which can become quite large.
Enabling log rotation and archiving will move all logs older than 30 days into storage.
</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_ACTION_LOG_ROTATION">
Enable Action Log Rotation
</div>
<table class="config-table" ng-if="config.FEATURE_ACTION_LOG_ROTATION">
<tr>
<td>Storage location:</td>
<td>
<select class="form-control" ng-model="config.ACTION_LOG_ARCHIVE_LOCATION">
<option ng-repeat="sc in storageConfig" value="{{ sc['location'] }}">{{ sc['location'] }}</option>
</select>
<div class="help-text">
The storage location in which to place archived action logs. Logs will only be archived to this single location.
</div>
</td>
</tr>
<tr>
<td>Storage path:</td>
<td>
<span class="config-string-field" binding="config.ACTION_LOG_ARCHIVE_PATH"
placeholder="Path under storage to place archived logs"></span>
<div class="help-text">
The path under the configured storage engine in which to place the archived logs in JSON form.
</div>
</td>
</tr>
</table>
</div>
<!-- Security Scanner -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-bug"></i> Security Scanner
</div>
<div class="co-panel-body">
<div class="description">
<p>If enabled, all images pushed to Quay will be scanned via the external security scanning service, with vulnerability information available in the UI and API, as well
as async notification support.
</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_SECURITY_SCANNER">
Enable Security Scanning
</div>
<div class="co-alert co-alert-info" ng-if="config.FEATURE_SECURITY_SCANNER" style="margin-top: 20px;">
A scanner compliant with the Quay Security Scanning API must be running to use this feature. Documentation on running <a href="https://github.com/coreos/clair" ng-safenewtab>Clair</a> can be found at <a href="https://tectonic.com/quay-enterprise/docs/latest/clair.html" ng-safenewtab>Running Clair Security Scanner</a>.
</div>
<table class="config-table" ng-if="config.FEATURE_SECURITY_SCANNER">
<tr>
<td>Authentication Key:</td>
<td>
<span class="config-service-key-field" service-name="{{ config.SECURITY_SCANNER_ISSUER_NAME || 'secscan' }}"></span>
<div class="help-text">
The security scanning service requires an authorized service key to speak to Quay. Once setup, the key
can be managed in the Service Keys panel under the Super User Admin Panel.
</div>
</td>
</tr>
<tr>
<td>Security Scanner Endpoint:</td>
<td>
<span class="config-string-field" binding="config.SECURITY_SCANNER_ENDPOINT"
placeholder="Security Scanner API endpoint (Example: http://myhost:6060)"
pattern="http(s)?://.+"></span>
<div class="help-text">
The HTTP URL at which the security scanner is running.
</div>
<div class="co-alert co-alert-info" ng-if="config.SECURITY_SCANNER_ENDPOINT.indexOf('https:') == 0" style="margin-top: 20px;">
Is the security scanner behind a domain signed with a <strong>self-signed TLS certificate</strong>? If so, please make sure to register your SSL CA in the <a href="#custom-ssl">custom certificates panel</a> above.
</div>
</td>
</tr>
</table>
</div>
</div>
<!-- App Registry -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa ci-appcube"></i> Application Registry
</div>
<div class="co-panel-body">
<div class="description">
<p>If enabled, an additional registry API will be available for managing applications (Kubernetes manifests, Helm charts) via the <a href="https://github.com/app-registry">App Registry specification</a>. A great place to get started is to install the <a href="https://github.com/app-registry/appr-helm-plugin">Helm Registry Plugin</a>.
</div>
<div class="config-bool-field" binding="config.FEATURE_APP_REGISTRY">
Enable App Registry
</div>
</div>
</div>
<!-- BitTorrent pull -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-cloud-download"></i> BitTorrent-based download
</div>
<div class="co-panel-body">
<div class="description">
<p>If enabled, all images in the registry can be downloaded using the <a href="http://github.com/coreos/quayctl" ng-safenewtab>quayctl</a> tool via the BitTorrent protocol. A JWT-compatible BitTorrent tracker such as <a href="https://tectonic.com/quay-enterprise/docs/latest/running-chihaya.html">Chihaya</a> must be run.
</div>
<div class="config-bool-field" binding="config.FEATURE_BITTORRENT">
Enable BitTorrent downloads
</div>
<table class="config-table" ng-if="config.FEATURE_BITTORRENT">
<tr>
<td>Announce URL:</td>
<td>
<span class="config-string-field" binding="config.BITTORRENT_ANNOUNCE_URL"
placeholder="Announce URL for the torrent tracker (Example: http://mytracker/announce)"
pattern="http(s)?://.+"></span>
<div class="help-text">
The HTTP URL at which the torrents should be announced. A JWT-compatible tracker such as <a href="https://github.com/chihaya/chihaya" ng-safenewtab>Chihaya</a> must be run to ensure proper security. Documentation on running Chihaya with
this support can be found at <a href="https://tectonic.com/quay-enterprise/docs/latest/running-chihaya.html">Running Chihaya for Quay Enterprise</a>.
</div>
</td>
</tr>
</table>
</div>
</div>
<!-- ACI Conversion -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa rocket-icon" style="width: 20px; height: 20px; background-size: cover; vertical-align: middle;"></i> <a href="http://github.com/coreos/rkt" ng-safenewtab>rkt</a> Conversion
</div>
<div class="co-panel-body">
<div class="description">
<p>If enabled, all images in the registry can be fetched via <code>rkt fetch</code> or any other <a href="https://github.com/appc/spec/blob/master/spec/discovery.md" ng-safenewtab>AppC discovery</a>-compliant implementation.</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_ACI_CONVERSION">
Enable ACI Conversion
</div>
<div class="co-alert co-alert-info" ng-if="config.FEATURE_ACI_CONVERSION" style="margin-top: 20px;">
Documentation on generating these keys can be found at <a href="https://tectonic.com/quay-enterprise/docs/latest/aci-signing-keys.html" ng-safenewtab>Generating ACI Signing Keys</a>.
</div>
<table class="config-table" ng-if="config.FEATURE_ACI_CONVERSION">
<tr>
<td class="non-input">GPG2 Public Key File:</td>
<td>
<span class="config-file-field" filename="signing-public.gpg" has-file="hasfile.gpgSigningPublic"></span>
<div class="help-text">
The certificate must be in PEM format.
</div>
</td>
</tr>
<tr>
<td class="non-input">GPG2 Private Key File:</td>
<td>
<span class="config-file-field" filename="signing-private.gpg" has-file="hasfile.gpgSigningPrivate"></span>
</td>
</tr>
<tr>
<td class="non-input">GPG2 Private Key Name:</td>
<td>
<span class="config-string-field" binding="config.GPG2_PRIVATE_KEY_NAME"
placeholder="Name of the private key in the private key file (Example: EAB32227)"></span>
</td>
</tr>
</table>
</div>
</div>
<!-- E-mail -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-envelope"></i> E-mail
</div>
<div class="co-panel-body">
<div class="description">
<p>Valid e-mail server configuration is required for notification e-mails and the ability of
users to reset their passwords.</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_MAILING">
Enable E-mails
</div>
<table class="config-table" ng-if="config.FEATURE_MAILING">
<tr>
<td>SMTP Server:</td>
<td>
<span class="config-string-field" binding="config.MAIL_SERVER"
placeholder="SMTP server for sending e-mail"
pattern="{{ HOSTNAME_REGEX }}"
validator="validateHostname(value)">></span>
</td>
</tr>
<tr>
<td>SMTP Server Port:</td>
<td>
<span class="config-numeric-field" binding="config.MAIL_PORT"
default-value="587"></span>
</td>
</tr>
<tr>
<td>TLS:</td>
<td>
<div class="config-bool-field" binding="config.MAIL_USE_TLS">
Require TLS
</div>
</td>
</tr>
<tr>
<td>Mail Sender:</td>
<td>
<input class="form-control" type="email" ng-model="config.MAIL_DEFAULT_SENDER"
placeholder="E-mail address"></span>
<div class="help-text">
E-mail address from which all e-mails are sent. If not specified,
<code>support@quay.io</code> will be used.
</div>
</td>
</tr>
<tr>
<td>Authentication:</td>
<td>
<div class="config-bool-field" binding="config.MAIL_USE_AUTH">
Requires Authentication
</div>
<table class="config-table" ng-if="config.MAIL_USE_AUTH">
<tr>
<td>Username:</td>
<td>
<span class="config-string-field" binding="config.MAIL_USERNAME"
placeholder="Username for authentication"></span>
</td>
</tr>
<tr>
<td>Password:</td>
<td>
<input class="form-control" type="password"
ng-model="config.MAIL_PASSWORD"
placeholder="Password for authentication"></span>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</div> <!-- /E-mail -->
<!-- Internal Authentication -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-users"></i> Internal Authentication
</div>
<div class="co-panel-body">
<div class="description">
<p>
Authentication for the registry can be handled by either the registry itself, LDAP, Keystone, or external JWT endpoint.
</p>
<p>
Additional <strong>external</strong> authentication providers (such as GitHub) can be used in addition for <strong>login into the UI</strong>.
</p>
</div>
<div ng-if="config.AUTHENTICATION_TYPE != 'AppToken'">
<div class="co-alert co-alert-warning" ng-if="config.AUTHENTICATION_TYPE != 'Database' && !config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
It is <strong>highly recommended</strong> to require encrypted client passwords. External passwords used in the Docker client will be stored in <strong>plaintext</strong>!
<a ng-click="config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH = true">Enable this requirement now</a>.
</div>
<div class="co-alert co-alert-success" ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
Note: The "Require Encrypted Client Passwords" feature is currently enabled which will
prevent passwords from being saved as plaintext by the Docker client.
</div>
</div>
<table class="config-table" style="margin-bottom: 20px;">
<tr>
<td class="non-input">Authentication:</td>
<td>
<select class="form-control" ng-model="config.AUTHENTICATION_TYPE">
<option value="Database">Local Database</option>
<option value="LDAP">LDAP</option>
<option value="Keystone">Keystone (OpenStack Identity)</option>
<option value="JWT">JWT Custom Authentication</option>
<option value="AppToken" ng-if="config.FEATURE_APP_SPECIFIC_TOKENS">External Application Token</option>
</select>
</td>
</tr>
<tr ng-if="config.AUTHENTICATION_TYPE == 'LDAP' || config.AUTHENTICATION_TYPE == 'Keystone'">
<td>Team synchronization:</td>
<td>
<div class="config-bool-field" binding="config.FEATURE_TEAM_SYNCING">
Enable Team Synchronization Support
</div>
<div class="help-text">
If enabled, organization administrators who are also superusers can set teams to have their membership synchronized with a backing group in {{ config.AUTHENTICATION_TYPE }}.
</div>
</td>
</tr>
<tr ng-if="(config.AUTHENTICATION_TYPE == 'LDAP' || config.AUTHENTICATION_TYPE == 'Keystone') && config.FEATURE_TEAM_SYNCING">
<td>Resynchronization duration:</td>
<td>
<span class="config-string-field" binding="config.TEAM_RESYNC_STALE_TIME"
pattern="[0-9]+(m|h|d|s)"></span>
<div class="help-text">
The duration before a team must be re-synchronized. Must be expressed in a duration string form: <code>30m</code>, <code>1h</code>, <code>1d</code>.
</div>
</td>
</tr>
<tr ng-if="(config.AUTHENTICATION_TYPE == 'LDAP' || config.AUTHENTICATION_TYPE == 'Keystone') && config.FEATURE_TEAM_SYNCING">
<td>Self-service team syncing setup:</td>
<td>
<div class="co-alert co-alert-warning" style="margin-bottom: 20px;">If enabled, this feature will allow *any organization administrator* to read the membership of any {{ config.AUTHENTICATION_TYPE }} group.</div>
<div class="config-bool-field" binding="config.FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP">
Allow non-superusers to enable and manage team syncing
</div>
<div class="help-text">
If enabled, non-superusers will be able to enable and manage team sycning on teams under organizations in which they are administrators.
</div>
</td>
</tr>
</table>
<!-- Keystone Authentication -->
<table class="config-table" ng-if="config.AUTHENTICATION_TYPE == 'Keystone'">
<tr>
<td>Keystone API Version:</td>
<td>
<select ng-model="config.KEYSTONE_AUTH_VERSION">
<option value="2">2.0</option>
<option value="3">V3</option>
</select>
</td>
</tr>
<tr>
<td>Keystone Authentication URL:</td>
<td>
<span class="config-string-field" binding="config.KEYSTONE_AUTH_URL"
pattern="http(s)?://.+"></span>
<div class="help-text">
The URL (starting with http or https) of the Keystone Server endpoint for auth.
</div>
</td>
</tr>
<tr>
<td>Keystone Administrator Username:</td>
<td>
<span class="config-string-field" binding="config.KEYSTONE_ADMIN_USERNAME"></span>
<div class="help-text">
The username for the Keystone admin.
</div>
</td>
</tr>
<tr>
<td>Keystone Administrator Password:</td>
<td>
<input type="password" ng-model="config.KEYSTONE_ADMIN_PASSWORD"
class="form-control" required></span>
<div class="help-text">
The password for the Keystone admin.
</div>
</td>
</tr>
<tr>
<td>Keystone Administrator Tenant:</td>
<td>
<span class="config-string-field" binding="config.KEYSTONE_ADMIN_TENANT"></span>
<div class="help-text">
The tenant (project/group) that contains the administrator user.
</div>
</td>
</tr>
</table>
<!-- JWT Custom Authentication -->
<div class="co-alert co-alert-info" ng-if="config.AUTHENTICATION_TYPE == 'JWT'">
JSON Web Token authentication allows your organization to provide an HTTP endpoint that
verifies user credentials on behalf of <span class="registry-name"></span>.
<br>
Documentation
on the API required can be found here: <a href="https://github.com/coreos/jwt-auth-example" ng-safenewtab>https://github.com/coreos/jwt-auth-example</a>.
</div>
<table class="config-table" ng-if="config.AUTHENTICATION_TYPE == 'JWT'">
<tr>
<td>Authentication Issuer:</td>
<td>
<span class="config-string-field" binding="config.JWT_AUTH_ISSUER"></span>
<div class="help-text">
The id of the issuer signing the JWT token. Must be unique to your organization.
</div>
</td>
</tr>
<tr>
<td>Public Key:</td>
<td>
<span class="config-file-field" filename="jwt-authn.cert" has-file="hasfile.JWTCert"></span>
<div class="help-text">
A certificate containing the public key portion of the key pair used to sign
the JSON Web Tokens. This file must be in PEM format.
</div>
</td>
</tr>
<tr>
<td>User Verification Endpoint:</td>
<td>
<span class="config-string-field" binding="config.JWT_VERIFY_ENDPOINT"
pattern="http(s)?://.+"></span>
<div class="help-text">
The URL (starting with http or https) on the JWT authentication server for verifying username and password credentials.
</div>
<div class="help-text" style="margin-top: 6px;">
Credentials will be sent in the <code>Authorization</code> header as Basic Auth, and this endpoint should return <code>200 OK</code> on success (or a <code>4**</code> otherwise).
</div>
</td>
</tr>
<tr>
<td>User Query Endpoint:</td>
<td>
<span class="config-string-field" binding="config.JWT_QUERY_ENDPOINT"
pattern="http(s)?://.+" is-optional="true"></span>
<div class="help-text">
The URL (starting with http or https) on the JWT authentication server for looking up
users based on a prefix query. This is optional.
</div>
<div class="help-text" style="margin-top: 6px;">
The prefix query will be sent as a query parameter with name <code>query</code>.
</div>
</td>
</tr>
<tr>
<td>User Lookup Endpoint:</td>
<td>
<span class="config-string-field" binding="config.JWT_GETUSER_ENDPOINT"
pattern="http(s)?://.+" is-optional="true"></span>
<div class="help-text">
The URL (starting with http or https) on the JWT authentication server for looking up
a user by username or email address.
</div>
<div class="help-text" style="margin-top: 6px;">
The username or email address will be sent as a query parameter with name <code>username</code>.
</div>
</td>
</tr>
</table>
<!-- LDAP Authentication -->
<table class="config-table" ng-if="config.AUTHENTICATION_TYPE == 'LDAP'">
<tr>
<td>LDAP URI:</td>
<td>
<span class="config-string-field" binding="config.LDAP_URI"
pattern="ldap(s)?://.+"></span>
<div class="help-text">
The full LDAP URI, including the ldap:// or ldaps:// prefix.
</div>
</td>
</tr>
<tr>
<td>Base DN:</td>
<td>
<span class="config-string-list-field" item-title="DN piece" item-delimiter="," binding="config.LDAP_BASE_DN"></span>
<div class="help-text">
A Distinguished Name path which forms the base path for looking up all LDAP records.
</div>
<div class="help-text">
Example: dc=my,dc=domain,dc=com
</div>
</td>
</tr>
<tr>
<td>User Relative DN:</td>
<td>
<span class="config-string-list-field" item-title="RDN piece" item-delimiter="," binding="config.LDAP_USER_RDN"></span>
<div class="help-text">
A Distinguished Name path which forms the base path for looking up all user LDAP records,
relative to the Base DN defined above.
</div>
<div class="help-text">
Example: ou=employees
</div>
</td>
</tr>
<tr>
<td>Secondary User Relative DNs:</td>
<td>
<span class="config-list-field" item-title="RDN" binding="config.LDAP_SECONDARY_USER_RDNS"></span>
<div class="help-text">
A list of Distinguished Name path(s) which forms the secondary base path(s) for
looking up all user LDAP records, relative to the Base DN defined above. These path(s)
will be tried if the user is not found via the primary relative DN.
</div>
<div class="help-text">
Example: [ou=employees]
</div>
</td>
</tr>
<tr>
<td>Administrator DN:</td>
<td><span class="config-string-field" binding="config.LDAP_ADMIN_DN"></span>
<div class="help-text">
The Distinguished Name for the Administrator account. This account must be able to login and view the records for all user accounts.
</div>
<div class="help-text">
Example: uid=admin,ou=employees,dc=my,dc=domain,dc=com
</div>
</td>
</tr>
<tr>
<td>Administrator DN Password:</td>
<td>
<div class="co-alert co-alert-warning" style="margin-bottom: 10px;">
Note: This will be stored in
<strong>plaintext</strong> inside the config.yaml, so setting up a dedicated account or using
<a href="http://tools.ietf.org/id/draft-stroeder-hashed-userpassword-values-01.html" ng-safenewtab>a password hash</a> is <strong>highly</strong> recommended.
</div>
<input class="form-control" type="password" ng-model="config.LDAP_ADMIN_PASSWD">
<div class="help-text">
The password for the Administrator DN.
</div>
</td>
</tr>
<tr>
<td>UID Attribute:</td>
<td>
<span class="config-string-field" binding="config.LDAP_UID_ATTR" default-value="uid"></span>
<div class="help-text">
The name of the property field in your LDAP user records that stores your
users' username. Typically "uid".
</div>
</td>
</tr>
<tr>
<td>Mail Attribute:</td>
<td>
<span class="config-string-field" binding="config.LDAP_EMAIL_ATTR" default-value="mail"></span>
<div class="help-text">
The name of the property field in your LDAP user records that stores your
users' e-mail address(es). Typically "mail".
</div>
</td>
</tr>
<tr ng-if="config.LDAP_URI.indexOf('ldaps://') == 0">
<td class="non-input">Custom TLS Certificate:</td>
<td>
<span class="config-file-field" filename="ldap.crt" has-file="hasfile.LDAPTLSCert"></span>
<div class="help-text">
If specified, the certificate (in PEM format) for the LDAP TLS connection.
</div
</td>
</tr>
<tr ng-if="config.LDAP_URI.indexOf('ldaps://') == 0">
<td class="non-input">Allow insecure:</td>
<td>
<div class="config-bool-field" binding="config.LDAP_ALLOW_INSECURE_FALLBACK">
Allow fallback to non-TLS connections
</div>
<div class="help-text">
If enabled, LDAP will fallback to <strong>insecure non-TLS connections</strong> if TLS does not succeed.
</div>
</td>
</tr>
</table>
</div>
</div> <!-- / Internal Authentication -->
<div class="co-panel"> <!-- External Authentication -->
<div class="co-panel-heading">
<i class="fa fa-id-card"></i> External Authorization (OAuth)
</div>
<div class="co-panel-body">
<!-- GitHub Authentication -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-github"></i> GitHub (Enterprise) Authentication
</div>
<div class="co-panel-body">
<div class="description">
<p>
If enabled, users can use GitHub or GitHub Enterprise to authenticate to the registry.
</p>
<p>
<strong>Note:</strong> A registered GitHub (Enterprise) OAuth application is required.
View instructions on how to
<a href="https://coreos.com/docs/enterprise-registry/github-app/" ng-safenewtab>
Create an OAuth Application in GitHub
</a>
</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_GITHUB_LOGIN">
Enable GitHub Authentication
</div>
<div class="co-alert co-alert-warning" ng-if="config.FEATURE_GITHUB_LOGIN && config.AUTHENTICATION_TYPE && config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken' && !config.GITHUB_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
Warning: This provider is not bound to your <strong>{{ config.AUTHENTICATION_TYPE }}</strong> authentication. Logging in via this provider will create a <strong><span class="registry-name"></span>-only user</strong>, which is not the recommended approach. It is <strong>highly</strong> recommended to choose a "Binding Field" below.
</div>
<table class="config-table" ng-if="config.FEATURE_GITHUB_LOGIN">
<tr>
<td>GitHub:</td>
<td>
<select class="form-control" ng-model="mapped.GITHUB_LOGIN_KIND">
<option value="hosted">GitHub.com</option>
<option value="enterprise">GitHub Enterprise</option>
</select>
</td>
</tr>
<tr ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">
<td>GitHub Endpoint:</td>
<td>
<span class="config-string-field"
binding="config.GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT"
placeholder="https://my.githubserver"
pattern="{{ GITHOST_REGEX }}">
</span>
<div class="help-text">
The GitHub Enterprise endpoint. Must start with http:// or https://.
</div>
</td>
</tr>
<tr>
<td>OAuth Client ID:</td>
<td>
<span class="config-string-field" binding="config.GITHUB_LOGIN_CONFIG.CLIENT_ID">
</span>
</td>
</tr>
<tr>
<td>OAuth Client Secret:</td>
<td>
<span class="config-string-field" binding="config.GITHUB_LOGIN_CONFIG.CLIENT_SECRET">
</span>
</td>
</tr>
<tr>
<td>Organization Filtering:</td>
<td>
<div class="config-bool-field" binding="config.GITHUB_LOGIN_CONFIG.ORG_RESTRICT">
Restrict By Organization Membership
</div>
<div class="help-text" style="margin-bottom: 20px;">
If enabled, only members of specified GitHub
<span ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">Enterprise</span> organizations will be allowed to login via GitHub
<span ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">Enterprise</span>.
</div>
<span class="config-list-field"
item-title="Organization ID"
binding="config.GITHUB_LOGIN_CONFIG.ALLOWED_ORGANIZATIONS"
ng-if="config.GITHUB_LOGIN_CONFIG.ORG_RESTRICT">
</span>
</td>
</tr>
<tr ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken'">
<td>Binding Field:</td>
<td>
<select class="form-control" ng-model="config.GITHUB_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
<option value="">(None)</option>
<option value="sub">Subject (User ID)</option>
<option value="username">Username</option>
<option value="email">E-mail address</option>
</select>
<div class="help-text">
If selected, when a user logs in via this provider, they will be automatically bound to their user in <strong>{{ config.AUTHENTICATION_TYPE }}</strong> by matching the selected field from the provider to the associated user in {{ config.AUTHENTICATION_TYPE }}.
</div>
<div class="help-text">
For example, selecting <code>Subject</code> here with a backing authentication system of LDAP means that a user logging in via this provider will also be bound to their user in LDAP by username.
</div>
<div class="help-text">
If none selected, a <strong>user unique to <span class="registry-name"></span></strong> will be created on initial login with this provider. <strong>This is not the recommended setup.</strong>
</div>
</td>
</tr>
</table>
</div>
</div> <!-- /GitHub Authentication -->
<!-- Google Authentication -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-google"></i> Google Authentication
</div>
<div class="co-panel-body">
<div class="description">
<p>
If enabled, users can use Google to authenticate to the registry.
</p>
<p>
<strong>Note:</strong> A registered Google OAuth application is required.
Visit the
<a href="https://console.developers.google.com" ng-safenewtab>
Google Developer Console
</a>
to register an application.
</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_GOOGLE_LOGIN">
Enable Google Authentication
</div>
<div class="co-alert co-alert-warning" ng-if="config.FEATURE_GOOGLE_LOGIN && config.AUTHENTICATION_TYPE && config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken' && !config.GOOGLE_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
Warning: This provider is not bound to your <strong>{{ config.AUTHENTICATION_TYPE }}</strong> authentication. Logging in via this provider will create a <strong><span class="registry-name"></span>-only user</strong>, which is not the recommended approach. It is <strong>highly</strong> recommended to choose a "Binding Field" below.
</div>
<table class="config-table" ng-if="config.FEATURE_GOOGLE_LOGIN">
<tr>
<td>OAuth Client ID:</td>
<td>
<span class="config-string-field" binding="config.GOOGLE_LOGIN_CONFIG.CLIENT_ID">
</span>
</td>
</tr>
<tr>
<td>OAuth Client Secret:</td>
<td>
<span class="config-string-field" binding="config.GOOGLE_LOGIN_CONFIG.CLIENT_SECRET">
</span>
</td>
</tr>
<tr ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken'">
<td>Binding Field:</td>
<td>
<select class="form-control" ng-model="config.GOOGLE_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
<option value="">(None)</option>
<option value="sub">Subject (User ID)</option>
<option value="username">Username</option>
<option value="email">E-mail address</option>
</select>
<div class="help-text">
If selected, when a user logs in via this provider, they will be automatically bound to their user in <strong>{{ config.AUTHENTICATION_TYPE }}</strong> by matching the selected field from the provider to the associated user in {{ config.AUTHENTICATION_TYPE }}.
</div>
<div class="help-text">
For example, selecting <code>Subject</code> here with a backing authentication system of LDAP means that a user logging in via this provider will also be bound to their user in LDAP by username.
</div>
<div class="help-text">
If none selected, a <strong>user unique to <span class="registry-name"></span></strong> will be created on initial login with this provider. <strong>This is not the recommended setup.</strong>
</div>
</td>
</tr>
</table>
</div>
</div> <!-- /Google Authentication -->
<!-- Custom OIDC providers -->
<div class="co-panel" ng-repeat="provider in getOIDCProviders(config)">
<div class="co-panel-heading">
<span class="icon-image-view" value="{{ config[provider]['SERVICE_ICON'] || 'fa-user-circle' }}" style="margin-right: 6px;"></span>
{{ config[provider]['SERVICE_NAME'] || (getOIDCProviderId(provider) + ' Authentication') }}
<span style="display: inline-block; margin-left: 10px">(<a href="javascript:void(0)" ng-click="removeOIDCProvider(provider)">Delete</a>)</span>
</div>
<div class="co-panel-body">
<div class="co-alert co-alert-warning" ng-if="config.AUTHENTICATION_TYPE && config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken' && !(config[provider].LOGIN_BINDING_FIELD)">
Warning: This OIDC provider is not bound to your <strong>{{ config.AUTHENTICATION_TYPE }}</strong> authentication. Logging in via this provider will create a <strong><span class="registry-name"></span>-only user</strong>, which is not the recommended approach. It is <strong>highly</strong> recommended to choose a "Binding Field" below.
</div>
<table class="config-table">
<tr>
<td class="non-input">Service ID:</td>
<td>
<code>{{ getOIDCProviderId(provider) }}</code>
</td>
</tr>
<tr>
<td>OIDC Server:</td>
<td>
<span class="config-string-field"
binding="config[provider].OIDC_SERVER"
placeholder="https://path/to/oidc/compliant/server"
pattern="https://.+">
</span>
<div class="help-text">
The URL of an OIDC-compliant server.
</div>
</td>
</tr>
<tr>
<td>Client ID:</td>
<td>
<span class="config-string-field" binding="config[provider].CLIENT_ID"></span>
</td>
</tr>
<tr>
<td>Client Secret:</td>
<td>
<span class="config-string-field" binding="config[provider].CLIENT_SECRET"></span>
</td>
</tr>
<tr>
<td>Service Name:</td>
<td>
<span class="config-string-field"
binding="config[provider].SERVICE_NAME"
placeholder="My Authentication Service">
</span>
<div class="help-text">
The user friendly name to display for the service on the login page.
</div>
</td>
</tr>
<tr>
<td>Service Icon (optional):</td>
<td>
<span class="config-string-field"
binding="config[provider].SERVICE_ICON"
placeholder="URL of the icon to use for this service OR a font awesome CSS name"
is-optional="true">
</span>
<div class="help-text">
If specified, the icon to display for this login service on the login page. Can be either a URL to an icon or a CSS class name from <a href="http://fontawesome.io" ng-safenewtab>Font Awesome</a>
</div>
</td>
</tr>
<tr ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken'">
<td>Binding Field:</td>
<td>
<select class="form-control" ng-model="config[provider].LOGIN_BINDING_FIELD">
<option value="">(None)</option>
<option value="sub">Subject (User ID)</option>
<option value="username">Username</option>
<option value="email">E-mail address</option>
</select>
<div class="help-text">
If selected, when a user logs in via this OIDC provider, they will be automatically bound to their user in <strong>{{ config.AUTHENTICATION_TYPE }}</strong> by matching the selected field from the OIDC provider to the associated user in {{ config.AUTHENTICATION_TYPE }}.
</div>
<div class="help-text">
For example, selecting <code>Subject</code> here with a backing authentication system of LDAP means that a user logging in via this OIDC provider will also be bound to their user in LDAP by username.
</div>
<div class="help-text">
If none selected, a <strong>user unique to <span class="registry-name"></span></strong> will be created on initial login with this OIDC provider. <strong>This is not the recommended setup.</strong>
</div>
</td>
</tr>
<tr>
<td>Login Scopes:</td>
<td>
<span class="config-list-field" item-title="Login Scope" binding="config[provider].LOGIN_SCOPES"></span>
<div class="help-text">
If specified, the scopes to send to the OIDC provider when performing the login flow. Note that, <strong>if specified</strong>, these scopes will
<strong>override</strong> those set by default, so this list <strong>must</strong> include a scope for OpenID Connect
(typically the <code>openid</code> scope) or this provider will fail.
</div>
</td>
</tr>
</table>
<div>
<h4>Callback URLs for this service:</h4>
<ul>
<li><code>{{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback</code></li>
<li><code>{{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback/attach</code></li>
<li><code>{{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback/cli</code></li>
</ul>
</div>
</div>
</div>
<!-- Add Provider -->
<a class="btn btn-default" ng-click="addOIDCProvider()" style="margin-right: 6px;">Add OIDC Provider</a>
<a href="http://openid.net/connect/" ng-safenewtab>What is OIDC?</a>
</div>
</div> <!-- /External Authentication -->
<!-- Access settings -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-user-circle"></i> Access Settings
</div>
<div class="co-panel-body">
<div class="description">
<p>Various settings around access and authentication to the registry.</p>
</div>
<table class="config-table">
<tr>
<td class="non-input">Basic Credentials Login:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_DIRECT_LOGIN" ng-if="getOIDCProviders(config).length || config.FEATURE_GITHUB_LOGIN || config.FEATURE_GOOGLE_LOGIN">
Login to User Interface via credentials
</div>
<div ng-if="!getOIDCProviders(config).length && !config.FEATURE_GITHUB_LOGIN && !config.FEATURE_GOOGLE_LOGIN">
<div ng-if="!config.FEATURE_DIRECT_LOGIN" class="co-alert co-alert-danger">
Login to User Interface via credentials must be enabled. <a ng-click="enableFeature(config, 'FEATURE_DIRECT_LOGIN')">Click here to enable</a>.
</div>
<div ng-if="config.FEATURE_DIRECT_LOGIN">
Login to User Interface via credentials is <strong>enabled</strong> (requires at least one OIDC provider to disable)
</div>
</div>
<div class="help-text">
If enabled, users will be able to login to the <strong>user interface</strong> via their username and password credentials.
</div>
<div class="help-text">
If <strong>disabled</strong>, users will only be able to login to the <strong>user interface</strong> via one of the configured External Authentication providers.
</div>
</td>
</tr>
<tr>
<td class="non-input">External Application tokens</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_APP_SPECIFIC_TOKENS">
Allow external application tokens
</div>
<div class="help-text">
If enabled, users will be able to generate external application tokens for use on the Docker and rkt CLI. Note
that these tokens will <strong>not be required</strong> unless "App Token" is chosen as the Internal Authentication method above.
</div>
</td>
</tr>
<tr ng-if="config.FEATURE_APP_SPECIFIC_TOKENS">
<td>External application token expiration</td>
<td colspan="2">
<span class="config-string-field" binding="config.APP_SPECIFIC_TOKEN_EXPIRATION"
pattern="[0-9]+(m|w|h|d|s)" is-optional="true"></span>
<div class="help-text">
The expiration time for user generated external application tokens. If none, tokens will never expire.
</div>
</td>
</tr>
<tr>
<td class="non-input">Anonymous Access:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_ANONYMOUS_ACCESS">
Enable Anonymous Access
</div>
<div class="help-text">
If enabled, public repositories and search can be accessed by anyone that can
reach the registry, even if they are not authenticated. Disable to only allow
authenticated users to view and pull "public" resources.
</div>
</td>
</tr>
<tr>
<td class="non-input">User Creation:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_USER_CREATION">
Enable Open User Creation
</div>
<div class="help-text">
If enabled, user accounts can be created by anyone (unless restricted below to invited users).
Users can always be created in the users panel in this superuser tool, even if this feature is disabled.
</div>
</td>
</tr>
<tr ng-show="config.FEATURE_USER_CREATION && config.FEATURE_MAILING">
<td class="non-input">Invite-only User Creation:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_INVITE_ONLY_USER_CREATION">
Enable Invite-only User Creation
</div>
<div class="help-text">
If enabled, user accounts can only be created when a user has been invited, by e-mail address, to join a team.
Users can always be created in the users panel in this superuser tool, even if this feature is enabled.
</div>
</td>
</tr>
<tr>
<td class="non-input">Encrypted Client Password:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
Require Encrypted Client Passwords
</div>
<div class="help-text">
If enabled, users will not be able to login from the Docker command
line with a non-encrypted password and must generate an encrypted
password to use.
</div>
<div class="help-text" ng-if="config.AUTHENTICATION_TYPE != 'Database'">
This feature is <strong>highly recommended</strong> for setups with external authentication, as Docker currently stores passwords in <strong>plaintext</strong> on user's machines.
</div>
</td>
</tr>
<tr>
<td class="non-input">Prefix username autocompletion:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_PARTIAL_USER_AUTOCOMPLETE">
Allow prefix username autocompletion
</div>
<div class="help-text">
If disabled, autocompletion for users will only match on exact usernames.
</div>
</td>
</tr>
<tr>
<td class="non-input">Allow username confirmation:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_USERNAME_CONFIRMATION">
Allow username confirmation
</div>
<div class="help-text">
If disabled, users logging in will be locked into the username granted by
the registry.
</div>
</td>
</tr>
<tr ng-show="config.FEATURE_MAILING">
<td class="non-input">Team Invitations:</td>
<td colspan="2">
<div class="config-bool-field" binding="config.FEATURE_REQUIRE_TEAM_INVITE">
Require Team Invitations
</div>
<div class="help-text">
If enabled, when adding a new user to a team, they will receive an invitation to join the team, with the option to decline.
Otherwise, users will be immediately part of a team when added by a team administrator.
</div>
</td>
</tr>
</table>
</div>
</div> <!-- /Access settings -->
<!-- Build Support -->
<div class="co-panel">
<div class="co-panel-heading">
<i class="fa fa-tasks"></i> Dockerfile Build Support
</div>
<div class="co-panel-body">
<div class="description">
If enabled, users can submit Dockerfiles to be built and pushed by <span class="registry-name"></span>.
</div>
<div class="config-bool-field" binding="config.FEATURE_BUILD_SUPPORT">
Enable Dockerfile Build
</div>
<div ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 10px">
<strong>Note: Build workers are required for this feature.</strong>
See <a href="https://coreos.com/docs/enterprise-registry/build-support/" ng-safenewtab>Adding Build Workers</a> for instructions on how to setup build workers.
</div>
</div>
</div> <!-- /Build Support -->
<!-- GitHub Trigger -->
<div class="co-panel" ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 20px;">
<div class="co-panel-heading">
<i class="fa fa-github"></i> GitHub (Enterprise) Build Triggers
</div>
<div class="co-panel-body">
<div class="description">
<p>
If enabled, users can setup GitHub or GitHub Enterprise triggers to invoke Registry builds.
</p>
<p>
<strong>Note:</strong> A registered GitHub (Enterprise) OAuth application (<strong>separate from GitHub Authentication</strong>) is required.
View instructions on how to
<a href="https://coreos.com/docs/enterprise-registry/github-app/" ng-safenewtab>
Create an OAuth Application in GitHub
</a>
</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_GITHUB_BUILD">
Enable GitHub Triggers
</div>
<table class="config-table" ng-if="config.FEATURE_GITHUB_BUILD">
<tr>
<td>GitHub:</td>
<td>
<select class="form-control" ng-model="mapped.GITHUB_TRIGGER_KIND">
<option value="hosted">GitHub.com</option>
<option value="enterprise">GitHub Enterprise</option>
</select>
</td>
</tr>
<tr ng-if="mapped.GITHUB_TRIGGER_KIND == 'enterprise'">
<td>GitHub Endpoint:</td>
<td>
<span class="config-string-field"
binding="config.GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT"
placeholder="https://my.githubserver"
pattern="{{ GITHOST_REGEX }}">
</span>
<div class="help-text">
The GitHub Enterprise endpoint. Must start with http:// or https://.
</div>
</td>
</tr>
<tr>
<td>OAuth Client ID:</td>
<td>
<span class="config-string-field" binding="config.GITHUB_TRIGGER_CONFIG.CLIENT_ID">
</span>
</td>
</tr>
<tr>
<td>OAuth Client Secret:</td>
<td>
<span class="config-string-field" binding="config.GITHUB_TRIGGER_CONFIG.CLIENT_SECRET">
</span>
</td>
</tr>
</table>
</div>
</div> <!-- /GitHub Trigger -->
<!-- BitBucket Trigger -->
<div class="co-panel" ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 20px;">
<div class="co-panel-heading">
<i class="fa fa-bitbucket"></i> BitBucket Build Triggers
</div>
<div class="co-panel-body">
<div class="description">
<p>
If enabled, users can setup BitBucket triggers to invoke Registry builds.
</p>
<p>
<strong>Note:</strong> A registered BitBucket OAuth application is required.
View instructions on how to
<a href="https://coreos.com/docs/enterprise-registry/bitbucket-app/" ng-safenewtab>
Create an OAuth Application in BitBucket
</a>
</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_BITBUCKET_BUILD">
Enable BitBucket Triggers
</div>
<table class="config-table" ng-if="config.FEATURE_BITBUCKET_BUILD">
<tr>
<td>OAuth Consumer Key:</td>
<td>
<span class="config-string-field" binding="config.BITBUCKET_TRIGGER_CONFIG.CONSUMER_KEY">
</span>
</td>
</tr>
<tr>
<td>OAuth Consumer Secret:</td>
<td>
<span class="config-string-field" binding="config.BITBUCKET_TRIGGER_CONFIG.CONSUMER_SECRET">
</span>
</td>
</tr>
</table>
</div>
</div> <!-- /BitBucket Trigger -->
<!-- GitLab Trigger -->
<div class="co-panel" ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 20px;">
<div class="co-panel-heading">
<i class="fa fa-gitlab"></i> GitLab Build Triggers
</div>
<div class="co-panel-body">
<div class="description">
<p>
If enabled, users can setup GitLab triggers to invoke Registry builds.
</p>
<p>
<strong>Note:</strong> A registered GitLab OAuth application is required.
Visit the
<a href="{{ config.GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT || 'https://gitlab.com' }}/profile/applications" ng-safenewtab>
GitLab applications admin panel
</a>
to create a new application.
</p>
<p>The callback URL to use is:&nbsp;&nbsp;
<code>{{ config.PREFERRED_URL_SCHEME || 'http' }}://{{ config.SERVER_HOSTNAME || 'localhost' }}/oauth2/gitlab/callback/trigger</code>
</p>
</div>
<div class="config-bool-field" binding="config.FEATURE_GITLAB_BUILD">
Enable GitLab Triggers
</div>
<table class="config-table" ng-if="config.FEATURE_GITLAB_BUILD">
<tr>
<td>GitLab:</td>
<td>
<select class="form-control" ng-model="mapped.GITLAB_TRIGGER_KIND">
<option value="hosted">GitLab.com</option>
<option value="enterprise">GitLab CE/EE</option>
</select>
</td>
</tr>
<tr ng-if="mapped.GITLAB_TRIGGER_KIND == 'enterprise'">
<td>GitLab Endpoint:</td>
<td>
<span class="config-string-field"
binding="config.GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT"
placeholder="https://my.gitlabserver"
pattern="{{ GITHOST_REGEX }}">
</span>
<div class="help-text">
The GitLab Enterprise endpoint. Must start with http:// or https://.
</div>
</td>
</tr>
<tr>
<td>Application Id:</td>
<td>
<span class="config-string-field" binding="config.GITLAB_TRIGGER_CONFIG.CLIENT_ID">
</span>
</td>
</tr>
<tr>
<td>Secret:</td>
<td>
<span class="config-string-field" binding="config.GITLAB_TRIGGER_CONFIG.CLIENT_SECRET">
</span>
</td>
</tr>
</table>
</div>
</div> <!-- /GitLab Trigger -->
</form>
<!-- Save Bar -->
<div class="cor-floating-bottom-bar">
<button class="btn" ng-class="mapped.$hasChanges ? 'btn-primary' : 'btn-success'"
ng-click="checkValidateAndSave()" ng-show="configform.$valid">
<i class="fa fa-lg" ng-class="mapped.$hasChanges ? 'fa-dot-circle-o' : 'fa-check-circle'"></i>
<span ng-if="mapped.$hasChanges">Save Configuration Changes</span>
<span ng-if="!mapped.$hasChanges">Configuration Saved</span>
</button>
<button class="btn btn-warning" ng-click="checkValidateAndSave()" ng-show="!configform.$valid"
ng-click="checkValidateAndSave()">
<i class="fa fa-lg fa-sort"></i>
<span ng-if="configform.$error['required'].length">
{{ configform.$error['required'].length }} configuration field<span ng-show="configform.$error['required'].length != 1">s</span> remaining
</span>
<span ng-if="!configform.$error['required'].length">
Invalid configuration field
</span>
</button>
</div>
<!-- Modal message dialog -->
<div class="modal co-dialog fade initial-setup-modal" id="validateAndSaveModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
Checking your settings
</h4>
</div>
<div class="modal-body">
<div class="service-verification">
<div class="service-verification-row" ng-repeat="serviceInfo in validating">
<span class="quay-spinner" ng-show="serviceInfo.status == 'validating'"></span>
<i class="fa fa-lg fa-check-circle" ng-show="serviceInfo.status == 'success'"></i>
<i class="fa fa-lg fa-warning" ng-show="serviceInfo.status == 'error'"></i>
<span class="service-title">{{ serviceInfo.service.title }}</span>
<div class="service-verification-error" ng-show="serviceInfo.status == 'error'">{{ serviceInfo.errorMessage }}</div>
</div>
</div>
</div>
<!-- Footer: Saving configuration -->
<div class="modal-footer working" ng-show="savingConfiguration">
<span class="cor-loader-inline"></span> Saving Configuration...
</div>
<!-- Footer: Validating -->
<div class="modal-footer working"
ng-show="!savingConfiguration && validationStatus(validating) == 'validating'">
<span class="cor-loader-inline"></span> Validating settings...
<button class="btn btn-default" ng-click="cancelValidation()">
Stop Validating
</button>
</div>
<!-- Footer: Valid Config -->
<div class="modal-footer"
ng-show="!savingConfiguration && validationStatus(validating) == 'success'">
<span class="left-align">
<i class="fa fa-check"></i>
Configuration Validated
</span>
<button class="btn btn-primary"
ng-click="saveConfiguration()"
ng-disabled="savingConfiguration">
<i class="fa fa-upload" style="margin-right: 10px;"></i>Save Configuration
</button>
</div>
<!-- Footer: Invalid Config -->
<div class="modal-footer"
ng-show="!savingConfiguration && validationStatus(validating) == 'failed'">
<span class="left-align">
<i class="fa fa-warning"></i>
Problem Detected
</span>
<button class="btn btn-default" data-dismiss="modal">
Continue Editing
</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
</div>