348 lines
24 KiB
HTML
348 lines
24 KiB
HTML
<!-- Modal change password -->
|
|
<div class="modal fade" id="passwordChangeModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Change your password</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form name="passwordChangeForm" role="form" novalidate ng-submit="passwordchange.submit()" autocomplete="off">
|
|
<input type="password" style="display: none;">
|
|
<div class="form-group" ng-class="{ 'has-error': (!passwordChangeForm.password.$dirty && passwordchange.error.password) || (passwordChangeForm.password.$dirty && passwordChangeForm.password.$invalid) }">
|
|
<label class="control-label" for="inputPasswordChangePassword">Current password</label>
|
|
<div class="control-label" ng-show="(!passwordChangeForm.password.$dirty && passwordchange.error.password) || (passwordChangeForm.password.$dirty && passwordChangeForm.password.$invalid)">
|
|
<small ng-show="!passwordChangeForm.password.$dirty && passwordchange.error.password">Wrong password</small>
|
|
<small ng-show="passwordChangeForm.password.$dirty && passwordChangeForm.password.$error.required">A password is required</small>
|
|
</div>
|
|
<input type="password" class="form-control" ng-model="passwordchange.password" id="inputPasswordChangePassword" name="password" required autofocus>
|
|
</div>
|
|
<div class="form-group" ng-class="{ 'has-error': (!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPassword.$dirty && passwordChangeForm.newPassword.$invalid) }">
|
|
<label class="control-label" for="inputPasswordChangeNewPassword">New password</label>
|
|
<div class="control-label" ng-show="(!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPassword.$dirty && passwordChangeForm.newPassword.$invalid)">
|
|
<small ng-show="!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword">{{ passwordchange.error.newPassword }}<br/><br/></small>
|
|
<small ng-show=" passwordChangeForm.newPassword.$dirty && passwordChangeForm.newPassword.$invalid">Password must be atleast 8 characters</small>
|
|
</div>
|
|
<input type="password" class="form-control" ng-model="passwordchange.newPassword" id="inputPasswordChangeNewPassword" name="newPassword" ng-pattern="/^.{8,30}$/" required autofocus>
|
|
</div>
|
|
<div class="form-group" ng-class="{ 'has-error': (!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordChangeForm.newPasswordRepeat.$error.required) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordchange.newPassword !== passwordchange.newPasswordRepeat) }">
|
|
<label class="control-label" for="inputPasswordChangeNewPasswordRepeat">Repeat new password</label>
|
|
<div class="control-label" ng-show="(!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordChangeForm.newPasswordRepeat.$error.required) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordchange.newPassword !== passwordchange.newPasswordRepeat)">
|
|
<small ng-show="passwordChangeForm.newPasswordRepeat.$dirty && passwordChangeForm.newPasswordRepeat.$error.required">A password is required</small>
|
|
<small ng-show="passwordChangeForm.newPasswordRepeat.$dirty && passwordchange.newPassword !== passwordchange.newPasswordRepeat && passwordchange.newPasswordRepeat">Passwords don't match</small>
|
|
</div>
|
|
<input type="password" class="form-control" ng-model="passwordchange.newPasswordRepeat" id="inputPasswordChangeNewPasswordRepeat" name="newPasswordRepeat" required autofocus>
|
|
</div>
|
|
<input class="ng-hide" type="submit" ng-disabled="passwordChangeForm.$invalid"/>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-danger" ng-click="passwordchange.submit()" ng-disabled="passwordChangeForm.$invalid || passwordchange.busy"><i class="fa fa-circle-o-notch fa-spin" ng-show="passwordchange.busy"></i> Change</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal change email -->
|
|
<div class="modal fade" id="emailChangeModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Change primary email address</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form name="emailChangeForm" role="form" novalidate ng-submit="emailchange.submit()" autocomplete="off">
|
|
<div class="form-group" ng-class="{ 'has-error': (emailChangeForm.email.$dirty && emailChangeForm.email.$invalid) || (!emailChangeForm.email.$dirty && emailchange.error.email)}">
|
|
<div class="control-label" ng-show="(!emailChangeForm.email.$dirty && emailchange.error.email) || (emailChangeForm.email.$dirty && emailChangeForm.email.$invalid)">
|
|
<small ng-show="emailChangeForm.email.$error.required">A valid email address is required</small>
|
|
<small ng-show="(emailChangeForm.email.$dirty && emailChangeForm.email.$invalid) && !emailChangeForm.email.$error.required">The Email address is not valid</small>
|
|
<small ng-show="!emailChangeForm.email.$dirty && emailchange.error.email">{{ emailchange.error.email }}</small>
|
|
</div>
|
|
<input type="email" class="form-control" ng-model="emailchange.email" id="inputEmailChangeEmail" name="email" required autofocus>
|
|
</div>
|
|
<input class="ng-hide" type="submit" ng-disabled="emailChangeForm.$invalid"/>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-success" ng-click="emailchange.submit()" ng-disabled="emailChangeForm.$invalid || emailchange.busy"><i class="fa fa-circle-o-notch fa-spin" ng-show="emailchange.busy"></i> Change</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal change fallback email -->
|
|
<div class="modal fade" id="fallbackEmailChangeModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Change password recovery email address</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form name="fallbackEmailChangeForm" role="form" novalidate ng-submit="fallbackEmailChange.submit()" autocomplete="off">
|
|
<div class="form-group" ng-class="{ 'has-error': (fallbackEmailChangeForm.email.$dirty && fallbackEmailChangeForm.email.$invalid) || (!fallbackEmailChangeForm.email.$dirty && fallbackEmailChange.error.email)}">
|
|
<div class="control-label" ng-show="(!fallbackEmailChangeForm.email.$dirty && fallbackEmailChange.error.email) || (fallbackEmailChangeForm.email.$dirty && fallbackEmailChangeForm.email.$invalid)">
|
|
<small ng-show="fallbackEmailChangeForm.email.$error.required">A valid email address is required</small>
|
|
<small ng-show="(fallbackEmailChangeForm.email.$dirty && fallbackEmailChangeForm.email.$invalid) && !fallbackEmailChangeForm.email.$error.required">The Email address is not valid</small>
|
|
<small ng-show="!fallbackEmailChangeForm.email.$dirty && fallbackEmailChange.error.email">{{ fallbackEmailChange.error.email }}</small>
|
|
</div>
|
|
<input type="email" class="form-control" ng-model="fallbackEmailChange.email" id="inputfallbackEmailChangeEmail" name="email" required autofocus>
|
|
</div>
|
|
<input class="ng-hide" type="submit" ng-disabled="fallbackEmailChangeForm.$invalid"/>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-success" ng-click="fallbackEmailChange.submit()" ng-disabled="fallbackEmailChangeForm.$invalid || fallbackEmailChange.busy"><i class="fa fa-circle-o-notch fa-spin" ng-show="fallbackEmailChange.busy"></i> Change</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal change displayName -->
|
|
<div class="modal fade" id="displayNameChangeModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Change your display name</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form name="displayNameChangeForm" role="form" novalidate ng-submit="displayNameChange.submit()" autocomplete="off">
|
|
<div class="form-group" ng-class="{ 'has-error': (displayNameChangeForm.displayName.$dirty && displayNameChangeForm.displayName.$invalid) || (!displayNameChangeForm.displayName.$dirty && displayNameChange.error.displayName)}">
|
|
<label class="control-label">Display name</label>
|
|
<div class="control-label" ng-show="(!displayNameChangeForm.displayName.$dirty && displayNameChange.error.displayName) || (displayNameChangeForm.displayName.$dirty && displayNameChangeForm.displayName.$invalid)">
|
|
<small ng-show="displayNameChangeForm.displayName.$error.required">A valid display name is required</small>
|
|
<small ng-show="(displayNameChangeForm.displayName.$dirty && displayNameChangeForm.displayName.$invalid) && !displayNameChangeForm.displayName.$error.required">This display name is not valid</small>
|
|
<small ng-show="!displayNameChangeForm.email.$dirty && displayNameChange.error.displayName">{{ displayNameChange.error.displayName }}</small>
|
|
</div>
|
|
<input type="text" class="form-control" ng-model="displayNameChange.displayName" id="inputDisplayNameChangeDisplayName" name="displayName" required autofocus>
|
|
</div>
|
|
<input class="ng-hide" type="submit" ng-disabled="displayNameChangeForm.$invalid"/>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-success" ng-click="displayNameChange.submit()" ng-disabled="displayNameChangeForm.$invalid || displayNameChange.busy"><i class="fa fa-circle-o-notch fa-spin" ng-show="displayNameChange.busy"></i> Change</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal enable twofactor authentication -->
|
|
<div class="modal fade" id="twoFactorAuthenticationEnableModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Enable Two-Factor Authentication</h4>
|
|
</div>
|
|
<div class="modal-body text-center" ng-hide="twoFactorAuthentication.secret">
|
|
<h2><i class="fa fa-circle-o-notch fa-spin"></i></h2>
|
|
</div>
|
|
<div class="modal-body" ng-show="twoFactorAuthentication.secret">
|
|
<p>
|
|
Use Google Authenticator (<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank">Android</a>, <a href="https://itunes.apple.com/us/app/google-authenticator/id388497605" target="_blank">iOS</a>), FreeOTP authenticator (<a href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp" target="_blank">Android</a>, <a href="https://itunes.apple.com/us/app/freeotp-authenticator/id872559395" target="_blank">iOS</a>) or a similar TOTP app to scan the secret.
|
|
</p>
|
|
<center>
|
|
<img ng-src="{{ twoFactorAuthentication.qrcode }}"/>
|
|
<p>{{ twoFactorAuthentication.secret }}</p>
|
|
</center>
|
|
<br/>
|
|
<form name="twoFactorAuthenticationEnableForm" role="form" novalidate ng-submit="twoFactorAuthentication.enable()" autocomplete="off">
|
|
<div class="form-group" ng-class="{ 'has-error': (!twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthenticationEnableForm.totpToken.$invalid) }">
|
|
<label class="control-label">Token</label>
|
|
<div class="control-label" ng-show="(!twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthenticationEnableForm.totpToken.$invalid)">
|
|
<small>{{ twoFactorAuthentication.error }}</small>
|
|
</div>
|
|
<input type="text" class="form-control" ng-model="twoFactorAuthentication.totpToken" id="twoFactorAuthenticationTotpTokenInput" name="totpToken" required autofocus>
|
|
</div>
|
|
<input class="ng-hide" type="submit" ng-disabled="twoFactorAuthenticationEnableForm.$invalid"/>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-success" ng-click="twoFactorAuthentication.enable()" ng-disabled="twoFactorAuthenticationEnableForm.$invalid || twoFactorAuthentication.busy"><i class="fa fa-circle-o-notch fa-spin" ng-show="twoFactorAuthentication.busy"></i> Enable</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal disable twofactor authentication -->
|
|
<div class="modal fade" id="twoFactorAuthenticationDisableModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Disable Two-Factor Authentication</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form name="twoFactorAuthenticationDisableForm" role="form" novalidate ng-submit="twoFactorAuthentication.disable()" autocomplete="off">
|
|
<div class="form-group" ng-class="{ 'has-error': (!twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthenticationDisableForm.password.$invalid) }">
|
|
<label class="control-label">Password</label>
|
|
<div class="control-label" ng-show="(!twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthenticationDisableForm.password.$invalid)">
|
|
<small>{{ twoFactorAuthentication.error }}</small>
|
|
</div>
|
|
<input type="password" class="form-control" ng-model="twoFactorAuthentication.password" id="twoFactorAuthenticationPasswordInput" name="password" required autofocus>
|
|
</div>
|
|
<input class="ng-hide" type="submit" ng-disabled="twoFactorAuthenticationDisableForm.$invalid"/>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-success" ng-click="twoFactorAuthentication.disable()" ng-disabled="twoFactorAuthenticationDisableForm.$invalid || twoFactorAuthentication.busy"><i class="fa fa-circle-o-notch fa-spin" ng-show="twoFactorAuthentication.busy"></i> Disable</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal add token -->
|
|
<div class="modal fade" id="tokenAddModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Create API token</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div ng-hide="tokenAdd.token.accessToken">
|
|
<form name="tokenAddForm" role="form" novalidate ng-submit="tokenAdd.submit(apiClient)" autocomplete="off">
|
|
<div class="form-group" ng-class="{ 'has-error': (tokenAddForm.tokenName.$dirty && tokenAddForm.tokenName.$invalid) || (!tokenAddForm.tokenName.$dirty && tokenAdd.error.tokenName)}">
|
|
<label class="control-label">Token name</label>
|
|
<div class="control-label" ng-show="(!tokenAddForm.tokenName.$dirty && tokenAdd.error.tokenName) || (tokenAddForm.tokenName.$dirty && tokenAddForm.tokenName.$invalid)">
|
|
<small ng-show="tokenAddForm.tokenName.$error.required">A token name is required</small>
|
|
<small ng-show="(tokenAddForm.tokenName.$dirty && tokenAddForm.tokenName.$invalid) && !tokenAddForm.tokenName.$error.required">This token name is not valid</small>
|
|
<small ng-show="!tokenAddForm.email.$dirty && tokenAdd.error.tokenName">{{ tokenAdd.error.tokenName }}</small>
|
|
</div>
|
|
<input type="text" class="form-control" ng-model="tokenAdd.tokenName" id="inputTokenAddName" name="tokenName" required autofocus>
|
|
</div>
|
|
<input class="ng-hide" type="submit" ng-disabled="tokenAddForm.$invalid"/>
|
|
</form>
|
|
</div>
|
|
|
|
<div ng-show="tokenAdd.token.accessToken">
|
|
Use the following token to authenticate against the Cloudron API:
|
|
<br/>
|
|
<b ng-click-select>{{ tokenAdd.token.accessToken }}</b>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
<button type="button" class="btn btn-success" ng-click="tokenAdd.submit(apiClient)" ng-hide="tokenAdd.token.accessToken" ng-disabled="tokenAddForm.$invalid || tokenAdd.busy">
|
|
<i class="fa fa-circle-o-notch fa-spin" ng-show="tokenAdd.busy"></i> Generate Token
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="content">
|
|
|
|
<div class="text-left">
|
|
<h1>Account</h1>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="grid-item-top">
|
|
<div class="row">
|
|
<div class="col-xs-4" style="min-width: 150px;">
|
|
<img width="128" height="128" ng-src="{{ user.gravatarHuge }}"/>
|
|
</div>
|
|
<div class="col-xs-8">
|
|
<table width="100%">
|
|
<tr>
|
|
<td class="text-muted" style="vertical-align: top;">Username</td>
|
|
<td class="text-right" style="vertical-align: top;">
|
|
{{ user.username }}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="text-muted" style="vertical-align: top;">Display name</td>
|
|
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
|
|
{{ user.displayName }} <a href="" ng-click="displayNameChange.show()"><i class="fa fa-pencil text-small"></i></a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="text-muted" style="vertical-align: top;">Primary email</td>
|
|
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
|
|
{{ user.email }} <a href="" ng-click="emailchange.show()"><i class="fa fa-pencil text-small"></i></a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="text-muted" style="vertical-align: top;">Password recovery email</td>
|
|
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
|
|
{{ user.fallbackEmail }} <a href="" ng-click="fallbackEmailChange.show()"><i class="fa fa-pencil text-small"></i></a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="text-right" colspan="2" style="vertical-align: top;">
|
|
<br/>
|
|
<button class="btn btn-outline btn-xs btn-danger" ng-click="twoFactorAuthentication.show()">{{ user.twoFactorAuthenticationEnabled ? 'Disable' : 'Enable' }} 2FA</button>
|
|
<button class="btn btn-outline btn-xs btn-danger" ng-click="passwordchange.show()">Change Password</button>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
<div class="text-left">
|
|
<h3>API Tokens <button class="btn btn-xs btn-primary btn-outline pull-right" ng-click="tokenAdd.show(apiClient)"><i class="fa fa-plus"></i> New Token</button> </h3>
|
|
</div>
|
|
|
|
<!-- we will always at least have the webadmin token here, so activeClients always will have one entry with at least one token -->
|
|
<div class="card">
|
|
<div class="grid-item-top">
|
|
<div class="row">
|
|
<div class="col-xs-12">
|
|
<p>These tokens can be used to access the <a ng-href="{{ config.webServerOrigin + '/developer/api/' }}" target="_blank">Cloudron API</a>.</p>
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:40%">Name</th>
|
|
<th style="width:55%" class="hidden-xs hidden-sm">Token</th>
|
|
<th style="width: 5%" class="text-right">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr ng-repeat="token in apiClient.activeTokens">
|
|
<td class="text-left elide-table-cell">
|
|
{{ token.name || '-' }}
|
|
</td>
|
|
<td class="text-left hand elide-table-cell hidden-xs hidden-sm" ng-click="useredit.show(user)">
|
|
<span ng-click-select>{{ token.accessToken }}</span>
|
|
</td>
|
|
<td class="text-right no-wrap" style="vertical-align: bottom">
|
|
<button class="btn btn-xs btn-danger pull-right" ng-click="removeToken(apiClient, token)" title="Revoke Token"><i class="far fa-trash-alt"></i></button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
<div class="text-left">
|
|
<h3>Sessions</h3>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="grid-item-top">
|
|
<div class="row">
|
|
<div class="col-xs-12">
|
|
<p>You are logged into {{ activeClients.length + 1 }} app(s), including this session.</p>
|
|
<span ng-show="activeTokenCount > 1">
|
|
<hr/>
|
|
<h4>Active Apps:</h4>
|
|
<p ng-repeat="client in activeClients"><b>{{ client.name }} - {{client.activeTokens.length}} time(s)</b></p>
|
|
<hr/>
|
|
</span>
|
|
<button class="btn btn-outline btn-xs btn-danger pull-right" ng-click="revokeTokens()">Logout From All</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|