github.com/greenpau/go-authcrunch@v1.1.4/assets/portal/templates/basic/settings.template (about)

     1  <!doctype html>
     2  <html lang="en">
     3    <head>
     4      <title>{{ .MetaTitle }} - {{ .PageTitle }}</title>
     5      <!-- Required meta tags -->
     6      <meta charset="utf-8">
     7      <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     8      <meta name="description" content="{{ .MetaDescription }}" />
     9      <meta name="author" content="{{ .MetaAuthor }}" />
    10      <link rel="shortcut icon" href="{{ pathjoin .ActionEndpoint "/assets/images/favicon.png" }}" type="image/png">
    11      <link rel="icon" href="{{ pathjoin .ActionEndpoint "/assets/images/favicon.png" }}" type="image/png">
    12  
    13      <!-- Matrialize CSS -->
    14      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/materialize-css/css/materialize.css" }}" />
    15      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/google-webfonts/roboto.css" }}" />
    16      {{ if or (eq .Data.view "mfa-add-app") (eq .Data.view "mfa-test-app") }}
    17      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/google-webfonts/montserrat.css" }}" />
    18      {{ end }}
    19      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/line-awesome/line-awesome.css" }}" />
    20      {{ if or (eq .Data.view "sshkeys-add") (eq .Data.view "gpgkeys-add") (eq .Data.view "sshkeys-view") (eq .Data.view "gpgkeys-view") }}
    21      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/highlight.js/css/atom-one-dark.min.css" }}" />
    22      {{ end }}
    23      {{ if or (eq .Data.view "apikeys-add") (eq .Data.view "apikeys-add-status") }}
    24      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/highlight.js/css/atom-one-dark.min.css" }}" />
    25      {{ end }}
    26      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/css/styles.css" }}" />
    27      {{ if eq .Data.ui_options.custom_css_required "yes" }}
    28      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/css/custom.css" }}" />
    29      {{ end }}
    30      {{ if or (eq .Data.view "mfa-add-app") (eq .Data.view "mfa-test-app") }}
    31      <link rel="stylesheet" href="{{ pathjoin .ActionEndpoint "/assets/css/mfa_app.css" }}" />
    32      {{ end }}
    33    </head>
    34    <body class="app-body">
    35      <div class="container app-container">
    36        <div class="row">
    37          <nav>
    38            <div class="nav-wrapper">
    39              {{ if .LogoURL }}
    40              <img src="{{ .LogoURL }}" alt="{{ .LogoDescription }}" />
    41              {{ end }}
    42              <a href="#" class="brand-logo">{{ .PageTitle }}</a>
    43              <ul id="nav-mobile" class="right hide-on-med-and-down">
    44                <li>
    45                  <a href="{{ pathjoin .ActionEndpoint "/portal" }}">
    46                    <button type="button" class="btn waves-effect waves-light navbtn active">
    47                      <span class="app-btn-text">Portal</span>
    48                      <i class="las la-home left app-btn-icon app-navbar-btn-icon"></i>
    49                   </button>
    50                  </a>
    51                </li>
    52                <li>
    53                  <a href="{{ pathjoin .ActionEndpoint "/logout" }}" class="navbtn-last">
    54                    <button type="button" class="btn waves-effect waves-light navbtn active navbtn-last">
    55                      <span class="app-btn-text">Logout</span>
    56                      <i class="las la-sign-out-alt left app-btn-icon app-navbar-btn-icon"></i>
    57                    </button>
    58                  </a>
    59                </li>
    60              </ul>
    61            </div>
    62          </nav>
    63        </div>
    64        <div class="row">
    65          <div class="col s12 l3">
    66            <div class="collection">
    67              <a href="{{ pathjoin .ActionEndpoint "/settings/" }}" class="collection-item{{ if eq .Data.view "general" }} active{{ end }}">General</a>
    68              <a href="{{ pathjoin .ActionEndpoint "/settings/sshkeys" }}" class="collection-item{{ if eq .Data.view "sshkeys" }} active{{ end }}">SSH Keys</a>
    69              <a href="{{ pathjoin .ActionEndpoint "/settings/gpgkeys" }}" class="collection-item{{ if eq .Data.view "gpgkeys" }} active{{ end }}">GPG Keys</a>
    70              <a href="{{ pathjoin .ActionEndpoint "/settings/apikeys" }}" class="collection-item{{ if eq .Data.view "apikeys" }} active{{ end }}">API Keys</a>
    71              <a href="{{ pathjoin .ActionEndpoint "/settings/mfa" }}" class="collection-item{{ if eq .Data.view "mfa" }} active{{ end }}">MFA</a>
    72              <a href="{{ pathjoin .ActionEndpoint "/settings/password" }}" class="collection-item{{ if eq .Data.view "password" }} active{{ end }}">Password</a>
    73              <a href="{{ pathjoin .ActionEndpoint "/settings/connected" }}" class="collection-item{{ if eq .Data.view "connected" }} active{{ end }}">Connected Accounts</a>
    74              <a href="{{ pathjoin .ActionEndpoint "/portal" }}" class="hide-on-med-and-up collection-item">Portal</a>
    75              <a href="{{ pathjoin .ActionEndpoint "/logout" }}" class="hide-on-med-and-up collection-item">Logout</a>
    76            </div>
    77          </div>
    78          <div class="col s12 l9 app-content">
    79            {{ if eq .Data.view "general" }}
    80            <div class="row">
    81              <div class="col s12">
    82              {{ if eq .Data.status "SUCCESS" }}
    83              <p>
    84              <b>ID</b>: {{ .Data.metadata.ID }}<br/>
    85              {{ if .Data.metadata.Name }}<b>Name</b>: {{ .Data.metadata.Name }}<br/>{{ end }}
    86              {{ if .Data.metadata.Title }}<b>Title</b>: {{ .Data.metadata.Title }}<br/>{{ end }}
    87              <b>Username</b>: {{ .Data.metadata.Username }}<br/>
    88              <b>Email</b>: {{ .Data.metadata.Email }}<br/>
    89              <b>Created</b>: {{ .Data.metadata.Created }}<br/>
    90              <b>LastModified</b>: {{ .Data.metadata.LastModified }}<br/>
    91              <b>Revision</b>: {{ .Data.metadata.Revision }}
    92              </p>
    93              {{ else }}
    94              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
    95              {{ end }}
    96              </div>
    97            </div>
    98            {{ end }}
    99            {{ if eq .Data.view "sshkeys" }}
   100            <div class="row right">
   101              <div class="col s12 right">
   102                <a href="{{ pathjoin .ActionEndpoint "/settings/sshkeys/add" }}">
   103                  <button type="button" class="btn waves-effect waves-light navbtn active app-btn">
   104                    <i class="las la-key left app-btn-icon"></i>
   105                    <span class="app-btn-text">Add SSH Key</span>
   106                  </button>
   107                </a>
   108              </div>
   109            </div>
   110            <div class="row">
   111              <div class="col s12">
   112              {{ if .Data.sshkeys }}
   113                {{range .Data.sshkeys}}
   114                <div class="card">
   115                  <div class="card-content">
   116                    <span class="card-title">{{ .Comment }}</span>
   117                    <p>
   118                      <b>ID</b>: {{ .ID }}<br/>
   119                      <b>Type:</b> {{ .Type }}<br/>
   120                      <b>Fingerprint (SHA256)</b>: {{ .Fingerprint }}<br/>
   121                      <b>Fingerprint (MD5)</b>: {{ .FingerprintMD5 }}<br/>
   122                      <b>Created At</b>: {{ .CreatedAt }}
   123                    </p>
   124                  </div>
   125                  <div class="card-action">
   126                    <a href="{{ pathjoin $.ActionEndpoint "/settings/sshkeys/delete" .ID }}">Delete</a>
   127                    <a href="{{ pathjoin $.ActionEndpoint "/settings/sshkeys/view" .ID }}">View</a>
   128                  </div>
   129                </div>
   130                {{ end }}
   131              {{ else }}
   132                <p>No registered SSH Keys found</p>
   133              {{ end }}
   134              </div>
   135            </div>
   136            {{ end }}
   137            {{ if eq .Data.view "sshkeys-add" }}
   138              <form action="{{ pathjoin .ActionEndpoint "/settings/sshkeys/add" }}" method="POST">
   139                <div class="row">
   140                  <div class="col s12">
   141                    <h1>Add SSH Key</h1>
   142                    <p>Please paste your public SSH key here.</p>
   143                    <div class="input-field shell-textarea-wrapper">
   144                      <textarea id="key1" name="key1" class="hljs shell-textarea"></textarea>
   145                    </div>
   146                    <div class="input-field">
   147                      <input placeholder="Comment" name="comment1" id="comment1" type="text" autocorrect="off" autocapitalize="off" autocomplete="off" class="validate">
   148                    </div>
   149                    <div class="right">
   150                      <button type="submit" name="submit" class="btn waves-effect waves-light navbtn active navbtn-last app-btn">
   151                        <i class="las la-plus-circle left app-btn-icon"></i>
   152                        <span class="app-btn-text">Add SSH Key</span>
   153                      </button>
   154                    </div>
   155                  </div>
   156                </div>
   157              </form>
   158            {{ end }}
   159            {{ if eq .Data.view "sshkeys-add-status" }}
   160            <div class="row">
   161              <div class="col s12">
   162              {{ if eq .Data.status "SUCCESS" }}
   163                <h1>Public SSH Key</h1>
   164                <p>{{ .Data.status_reason }}</p>
   165                <a href="{{ pathjoin .ActionEndpoint "/settings/sshkeys" }}">
   166                  <button type="button" class="btn waves-effect waves-light navbtn active">
   167                    <i class="las la-undo-alt left app-btn-icon"></i>
   168                    <span class="app-btn-text">Go Back</span>
   169                  </button>
   170                </a>
   171              {{ else }}
   172                <h1>Public SSH Key</h1>
   173                <p>Reason: {{ .Data.status_reason }} </p>
   174                <a href="{{ pathjoin .ActionEndpoint "/settings/sshkeys/add" }}">
   175                  <button type="button" class="btn waves-effect waves-light navbtn active">
   176                    <i class="las la-undo-alt left app-btn-icon"></i>
   177                    <span class="app-btn-text">Try Again</span>
   178                  </button>
   179                </a>
   180              {{ end }}
   181              </div>
   182            </div>
   183            {{ end }}
   184            {{ if eq .Data.view "sshkeys-delete-status" }}
   185            <div class="row">
   186              <div class="col s12">
   187              <h1>Public SSH Key</h1>
   188              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   189              <a href="{{ pathjoin .ActionEndpoint "/settings/sshkeys" }}">
   190                <button type="button" class="btn waves-effect waves-light navbtn active">
   191                  <i class="las la-undo-alt left app-btn-icon"></i>
   192                  <span class="app-btn-text">Go Back</span>
   193                </button>
   194              </a>
   195              </div>
   196            </div>
   197            {{ end }}
   198            {{ if eq .Data.view "gpgkeys" }}
   199            <div class="row right">
   200              <div class="col s12 right">
   201                <a href="{{ pathjoin .ActionEndpoint "/settings/gpgkeys/add" }}">
   202                  <button type="button" class="btn waves-effect waves-light navbtn active app-btn">
   203                    <i class="las la-key left app-btn-icon"></i>
   204                    <span class="app-btn-text">Add GPG Key</span>
   205                  </button>
   206                </a>
   207              </div>
   208            </div>
   209            <div class="row">
   210              <div class="col s12">
   211              {{ if .Data.gpgkeys }}
   212                {{range .Data.gpgkeys}}
   213                <div class="card">
   214                  <div class="card-content">
   215                    <span class="card-title">{{ .Comment }}</span>
   216                    <p>
   217                      <b>ID</b>: {{ .ID }}<br/>
   218                      <b>Usage:</b> {{ .Usage }}<br/>
   219                      <b>Type:</b> {{ .Type }}<br/>
   220                      <b>Fingerprint</b>: {{ .Fingerprint }}<br/>
   221                      <b>Created At</b>: {{ .CreatedAt }}
   222                    </p>
   223                  </div>
   224                  <div class="card-action">
   225                    <a href="{{ pathjoin $.ActionEndpoint "/settings/gpgkeys/delete" .ID }}">Delete</a>
   226                    <a href="{{ pathjoin $.ActionEndpoint "/settings/gpgkeys/view" .ID }}">View</a>
   227                  </div>
   228                </div>
   229                {{ end }}
   230              {{ else }}
   231                <p>No registered GPG Keys found</p>
   232              {{ end }}
   233              </div>
   234            </div>
   235            {{ end }}
   236            {{ if eq .Data.view "gpgkeys-add" }}
   237              <form action="{{ pathjoin .ActionEndpoint "/settings/gpgkeys/add" }}" method="POST">
   238                <div class="row">
   239                  <div class="col s12">
   240                    <h1>Add GPG Key</h1>
   241                    <p>Please paste your public GPG key here.</p>
   242                    <div class="input-field shell-textarea-wrapper">
   243                        <textarea id="key1" name="key1" class="hljs shell-textarea"></textarea>
   244                    </div>
   245                    <div class="input-field">
   246                      <input placeholder="Comment" name="comment1" id="comment1" type="text" autocorrect="off" autocapitalize="off" autocomplete="off" class="validate">
   247                    </div>
   248                    <div class="right">
   249                      <button type="submit" name="submit" class="btn waves-effect waves-light navbtn active navbtn-last app-btn">
   250                        <i class="las la-plus-circle left app-btn-icon"></i>
   251                        <span class="app-btn-text">Add GPG Key</span>
   252                      </button>
   253                    </div>
   254                  </div>
   255                </div>
   256              </form>
   257            {{ end }}
   258            {{ if eq .Data.view "gpgkeys-add-status" }}
   259            <div class="row">
   260              <div class="col s12">
   261              {{ if eq .Data.status "SUCCESS" }}
   262                <h1>Public GPG Key</h1>
   263                <p>{{ .Data.status_reason }}</p>
   264                <a href="{{ pathjoin .ActionEndpoint "/settings/gpgkeys" }}">
   265                  <button type="button" class="btn waves-effect waves-light navbtn active">
   266                    <i class="las la-undo-alt left app-btn-icon"></i>
   267                    <span class="app-btn-text">Go Back</span>
   268                  </button>
   269                </a>
   270              {{ else }}
   271                <h1>Public GPG Key</h1>
   272                <p>Reason: {{ .Data.status_reason }} </p>
   273                <a href="{{ pathjoin .ActionEndpoint "/settings/gpgkeys/add" }}">
   274                  <button type="button" class="btn waves-effect waves-light navbtn active">
   275                    <i class="las la-undo-alt left app-btn-icon"></i>
   276                    <span class="app-btn-text">Try Again</span>
   277                  </button>
   278                </a>
   279              {{ end }}
   280              </div>
   281            </div>
   282            {{ end }}
   283            {{ if eq .Data.view "gpgkeys-delete-status" }}
   284            <div class="row">
   285              <div class="col s12">
   286              <h1>Public GPG Key</h1>
   287              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   288              <a href="{{ pathjoin .ActionEndpoint "/settings/gpgkeys" }}">
   289                <button type="button" class="btn waves-effect waves-light navbtn active">
   290                  <i class="las la-undo-alt left app-btn-icon"></i>
   291                  <span class="app-btn-text">Go Back</span>
   292                </button>
   293              </a>
   294              </div>
   295            </div>
   296            {{ end }}
   297            {{ if or (eq .Data.view "sshkeys-view") (eq .Data.view "gpgkeys-view") }}
   298            <div class="row">
   299              <div class="col s12">
   300                {{ if eq .Data.view "gpgkeys-view" }}
   301                <h1>GPG Key</h1>
   302                {{ else }}
   303                <h1>SSH Key</h1>
   304                {{ end }}
   305                <pre><code class="language-json hljs">{{ .Data.key }}</code></pre>
   306                {{ if .Data.pem_key }}
   307                <h5>PEM</h5>
   308                <pre><code class="language-text hljs">{{ .Data.pem_key }}</code></pre>
   309                {{ end }}
   310                {{ if .Data.openssh_key }}
   311                <h5>OpenSSH</h5>
   312                <pre><code class="language-text hljs">{{ .Data.openssh_key }}</code></pre>
   313                {{ end }}
   314                {{ if eq .Data.view "gpgkeys-view" }}
   315                <a href="{{ pathjoin .ActionEndpoint "/settings/gpgkeys" }}">
   316                {{ else }}
   317                <a href="{{ pathjoin .ActionEndpoint "/settings/sshkeys" }}">
   318                {{ end }}
   319                  <button type="button" class="btn waves-effect waves-light navbtn active">
   320                    <i class="las la-undo-alt left app-btn-icon"></i>
   321                    <span class="app-btn-text">Go Back</span>
   322                  </button>
   323                </a>
   324              </div>
   325            </div>
   326            {{ end }}
   327            {{ if eq .Data.view "apikeys" }}
   328            <div class="row right">
   329              <div class="col s12 right">
   330                <a href="{{ pathjoin .ActionEndpoint "/settings/apikeys/add" }}">
   331                  <button type="button" class="btn waves-effect waves-light navbtn active app-btn">
   332                    <i class="las la-key left app-btn-icon"></i>
   333                    <span class="app-btn-text">Add API Key</span>
   334                  </button>
   335                </a>
   336              </div>
   337            </div>
   338            <div class="row">
   339              <div class="col s12">
   340              {{ if .Data.apikeys }}
   341                {{range .Data.apikeys}}
   342                <div class="card">
   343                  <div class="card-content">
   344                    <span class="card-title">{{ .Comment }}</span>
   345                    <p>
   346                      <b>ID</b>: {{ .ID }}<br/>
   347                      <b>Created At</b>: {{ .CreatedAt }}
   348                    </p>
   349                  </div>
   350                  <div class="card-action">
   351                    <a href="{{ pathjoin $.ActionEndpoint "/settings/apikeys/delete" .ID }}">Delete</a>
   352                  </div>
   353                </div>
   354                {{ end }}
   355              {{ else }}
   356                <p>No registered API Keys found</p>
   357              {{ end }}
   358              </div>
   359            </div>
   360            {{ end }}
   361            {{ if eq .Data.view "apikeys-add" }}
   362              <form action="{{ pathjoin .ActionEndpoint "/settings/apikeys/add" }}" method="POST">
   363                <div class="row">
   364                  <div class="col s12">
   365                    <h1>Add API Key</h1>
   366                    <p>Please provide a nickname to identify your new API key.</p>
   367                    <div class="input-field">
   368                      <input placeholder="Comment" name="comment1" id="comment1" type="text" autocorrect="off" autocapitalize="off" autocomplete="off" class="validate">
   369                    </div>
   370                    <div class="right">
   371                      <button type="submit" name="submit" class="btn waves-effect waves-light navbtn active navbtn-last app-btn">
   372                        <i class="las la-plus-circle left app-btn-icon"></i>
   373                        <span class="app-btn-text">Add API Key</span>
   374                      </button>
   375                    </div>
   376                  </div>
   377                </div>
   378              </form>
   379            {{ end }}
   380            {{ if eq .Data.view "apikeys-add-status" }}
   381            <div class="row">
   382              <div class="col s12">
   383                <h1>API Key</h1>
   384                {{ if eq .Data.status "SUCCESS" }}
   385                <p>Keep this key secret!</p>
   386                <pre><code class="language-text hljs">{{ .Data.api_key }}</code></pre>
   387                {{ else }}
   388                <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   389                {{ end }}
   390                <a href="{{ pathjoin .ActionEndpoint "/settings/apikeys" }}">
   391                  <button type="button" class="btn waves-effect waves-light navbtn active">
   392                    <i class="las la-undo-alt left app-btn-icon"></i>
   393                    <span class="app-btn-text">Go Back</span>
   394                  </button>
   395                </a>
   396              </div>
   397            </div>
   398            {{ end }}
   399            {{ if eq .Data.view "apikeys-delete-status" }}
   400            <div class="row">
   401              <div class="col s12">
   402              <h1>API Key</h1>
   403              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   404              <a href="{{ pathjoin .ActionEndpoint "/settings/apikeys" }}">
   405                <button type="button" class="btn waves-effect waves-light navbtn active">
   406                  <i class="las la-undo-alt left app-btn-icon"></i>
   407                  <span class="app-btn-text">Go Back</span>
   408                </button>
   409              </a>
   410              </div>
   411            </div>
   412            {{ end }}
   413  
   414  
   415            {{ if eq .Data.view "mfa" }}
   416            <div class="row right">
   417              <div class="col s12 right">
   418                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa/add/app" }}">
   419                  <button type="button" class="btn waves-effect waves-light navbtn active app-btn">
   420                    <i class="las la-mobile-alt left app-btn-icon"></i>
   421                    <span class="app-btn-text">Add MFA App</span>
   422                  </button>
   423                </a>
   424                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa/add/u2f" }}" class="navbtn-last">
   425                  <button type="button" class="btn waves-effect waves-light navbtn active navbtn-last app-btn">
   426                    <i class="las la-key left app-btn-icon"></i>
   427                    <span class="app-btn-text">Add U2F Key</span>
   428                  </button>
   429                </a>
   430              </div>
   431            </div>
   432            <div class="row">
   433              <div class="col s12">
   434              {{ if .Data.mfa_tokens }}
   435                {{range .Data.mfa_tokens}}
   436                <div class="card">
   437                  <div class="card-content">
   438                    <span class="card-title">{{ .Comment }}</span>
   439                    <p>
   440                      <b>ID</b>: {{ .ID }}<br/>
   441                      {{ if eq .Type "u2f" }}
   442                      <b>Type</b>: Hardware/U2F Token<br/>
   443                      {{ else }}
   444                      <b>Type</b>: Authenticator App<br/>
   445                      <b>Algorithm</b>: {{ .Algorithm }}<br/>
   446                      <b>Period</b>: {{ .Period }} seconds<br/>
   447                      <b>Digits</b>: {{ .Digits }}<br/>
   448                      {{ end }}
   449                      <b>Created At</b>: {{ .CreatedAt }}
   450                    </p>
   451                  </div>
   452                  <div class="card-action">
   453                    <a href="{{ pathjoin $.ActionEndpoint "/settings/mfa/delete/" .ID }}">Delete</a>
   454                    {{ if eq .Type "totp" }}
   455                    <a href="{{ pathjoin $.ActionEndpoint "/settings/mfa/test/app/" (printf "%d" .Digits) .ID }}">Test</a>
   456                    {{ end }}
   457                    {{ if eq .Type "u2f" }}
   458                    <a href="{{ pathjoin $.ActionEndpoint "/settings/mfa/test/u2f/generic" .ID }}">Test</a>
   459                    {{ end }}
   460                  </div>
   461                </div>
   462                {{ end }}
   463              {{ else }}
   464                <p>No registered MFA devices found</p>
   465              {{ end }}
   466              </div>
   467            </div>
   468            {{ end }}
   469            {{ if eq .Data.view "mfa-add-app" }}
   470              <form id="mfa-add-app-form" action="{{ pathjoin .ActionEndpoint "/settings/mfa/add/app" }}" method="POST">
   471                <div class="row">
   472                  <h1>Add MFA Authenticator Application</h1>
   473                  <div class="col s12 m11 l11">
   474                    <div id="token-params">
   475                      <h6 id="token-params-mode" class="hide">Token Parameters</h6>
   476                      <p><b>Step 1</b>: Amend the label and comment associated with the authenticator.
   477                        The label is what you would see in your authenticator app.
   478                        The comment is what you would see in this portal.
   479                      </p>
   480                      <div class="input-field">
   481                        <input id="label" name="label" type="text" class="validate" pattern="[A-Za-z0-9 -]{4,25}"
   482                          title="Authentication code should contain 4-25 characters and consists of A-Z, a-z, 0-9, space, and dash characters."
   483                          maxlength="25"
   484                          autocorrect="off" autocapitalize="off" autocomplete="off"
   485                          value="{{ .Data.mfa_label }}"
   486                          required />
   487                        <label for="label">Label</label>
   488                      </div>
   489                      <div class="input-field">
   490                        <input id="comment" name="comment" type="text" class="validate" pattern="[A-Za-z0-9 -]{4,25}"
   491                          title="Authentication code should contain 4-25 characters and consists of A-Z, a-z, 0-9, space, and dash characters."
   492                          maxlength="25"
   493                          autocorrect="off" autocapitalize="off" autocomplete="off"
   494                          value="{{ .Data.mfa_comment }}"
   495                          required />
   496                        <label for="comment">Comment</label>
   497                      </div>
   498                      <p><b>Step 1a</b> (<i>optional</i>): If necessary, click
   499                        <a href="#advanced-setup-mode" onclick="toggleAdvancedSetupMode()">here</a> to customize default values.
   500                      </p>
   501                      <div id="advanced-setup-all" class="hide">
   502                        <h6 id="advanced-setup-mode" class="hide">Advanced Setup Mode</h6>
   503                        <div id="advanced-setup-secret" class="input-field">
   504                          <input id="secret" name="secret" type="text" class="validate" pattern="[A-Za-z0-9]{10,100}"
   505                            title="Token secret should contain 10-200 characters and consists of A-Z and 0-9 characters only."
   506                            autocorrect="off" autocapitalize="off" autocomplete="off"
   507                            maxlength="100"
   508                            value="{{ .Data.mfa_secret }}"
   509                            required />
   510                          <label for="secret">Token Secret</label>
   511                        </div>
   512                        <div id="advanced-setup-period" class="input-field">
   513                          <select id="period" name="period" class="browser-default">
   514                            <option value="15" {{ if eq .Data.mfa_period "15" }} selected{{ end }}>15 Seconds Lifetime</option>
   515                            <option value="30" {{ if eq .Data.mfa_period "30" }} selected{{ end }}>30 Seconds Lifetime</option>
   516                            <option value="60" {{ if eq .Data.mfa_period "60" }} selected{{ end }}>60 Seconds Lifetime</option>
   517                            <option value="90" {{ if eq .Data.mfa_period "90" }} selected{{ end }}>90 Seconds Lifetime</option>
   518                          </select>
   519                        </div>
   520                        <div id="advanced-setup-digits" class="input-field">
   521                          <select id="digits" name="digits" class="browser-default">
   522                            <option value="4" {{ if eq .Data.mfa_digits "4" }} selected{{ end }}>4 Digit Code</option>
   523                            <option value="6" {{ if eq .Data.mfa_digits "6" }} selected{{ end }}>6 Digit Code</option>
   524                            <option value="8" {{ if eq .Data.mfa_digits "8" }} selected{{ end }}>8 Digit Code</option>
   525                          </select>
   526                        </div>
   527                      </div>
   528                      <p><b>Step 2</b>: Open your MFA authenticator application, e.g. Microsoft/Google Authenticator, Authy, etc.,
   529                        add new entry and click the "Get QR" link.
   530                      </p>
   531                      <div id="mfa-get-qr-code" class="center-align">
   532                        <a href="#qr-code-mode" onclick="getQRCode()">Get QR Code</a>
   533                      </div>
   534                    </div>
   535                    <div id="mfa-qr-code" class="hide">
   536                      <h6 id="qr-code-mode" class="hide">QR Code Mode</h6>
   537                      <div class="center-align">
   538                        <p>&raquo; Scan the QR code image.</p>
   539                      </div>
   540                      <div id="mfa-qr-code-image" class="center-align">
   541                        <img src="{{ pathjoin .ActionEndpoint "/settings/mfa/barcode/" .Data.code_uri_encoded }}.png" alt="QR Code" />
   542                      </div>
   543                      <div class="center-align">
   544                        <p>&raquo; Can't scan? Click or copy the link below.</p>
   545                      </div>
   546                      <div id="mfa-no-camera-link" class="center-align">
   547                        <a href="{{ .Data.code_uri }}">No Camera Link</a>
   548                      </div>
   549                      <p><b>Step 3</b>: Enter the authentication code you see in the app and click "Add".</p>
   550                      <div class="input-field mfa-app-auth-ctrl mfa-app-auth-form">
   551                        <input class="mfa-app-auth-passcode" id="passcode" name="passcode" type="text" class="validate" pattern="[0-9]{4,8}"
   552                          title="Authentication code should contain 4-8 characters and consists of 0-9 characters."
   553                          autocorrect="off" autocapitalize="off" autocomplete="off"
   554                          placeholder="______"
   555                          required />
   556                      </div>
   557                      <input id="email" name="email" type="hidden" value="{{ .Data.mfa_email }}" />
   558                      <input id="type" name="type" type="hidden" value="{{ .Data.mfa_type }}" />
   559                      <input id="barcode_uri" name "barcode_uri" type="hidden" value="{{ pathjoin .ActionEndpoint "/settings/mfa/barcode/"}}" />
   560                      <div class="row right">
   561                        <button type="submit" name="submit" class="btn waves-effect waves-light navbtn active navbtn-last app-btn">
   562                          <i class="las la-plus-circle left app-btn-icon"></i>
   563                          <span class="app-btn-text">Add</span>
   564                        </button>
   565                      </div>
   566                    </div>
   567                  </div>
   568                </div>
   569              </form>
   570            {{ end }}
   571            {{ if eq .Data.view "mfa-add-app-status" }}
   572            <div class="row">
   573              <div class="col s12">
   574              <h1>MFA Token</h1>
   575              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   576              {{ if eq .Data.status "SUCCESS" }}
   577                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa" }}">
   578                  <button type="button" class="btn waves-effect waves-light navbtn active">
   579                    <i class="las la-undo-alt left app-btn-icon"></i>
   580                    <span class="app-btn-text">Go Back</span>
   581                  </button>
   582                </a>
   583              {{ else }}
   584                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa/add/app" }}">
   585                  <button type="button" class="btn waves-effect waves-light navbtn active">
   586                    <i class="las la-undo-alt left app-btn-icon"></i>
   587                    <span class="app-btn-text">Try Again</span>
   588                  </button>
   589                </a>
   590              {{ end }}
   591              </div>
   592            </div>
   593            {{ end }}
   594            {{ if eq .Data.view "mfa-test-app" }}
   595              <form id="mfa-test-app-form" action="{{ pathjoin .ActionEndpoint "/settings/mfa/test/app/" .Data.mfa_digits .Data.mfa_token_id }}" method="POST">
   596                <div class="row">
   597                  <h1>Test MFA Authenticator Application</h1>
   598                  <div class="row">
   599                    <div class="col s12 m12 l12">
   600                      <p>Please open your MFA authenticator application to view your authentication code and verify your identity</p>
   601                      <div class="input-field mfa-app-auth-ctrl mfa-app-auth-form">
   602                        <input class="mfa-app-auth-passcode" id="passcode" name="passcode" type="text" class="validate" pattern="[0-9]{4,8}"
   603                          title="Authentication code should contain 4-8 characters and consists of 0-9 characters."
   604                          maxlength="6"
   605                          autocorrect="off" autocapitalize="off" autocomplete="off"
   606                          placeholder="______"
   607                          required />
   608                      </div>
   609                      <input id="token_id" name="token_id" type="hidden" value="{{ .Data.mfa_token_id }}" />
   610                      <input id="digits" name="digits" type="hidden" value="{{ .Data.mfa_digits }}" />
   611                      <div class="center-align">
   612                        <button type="reset" name="reset" class="btn waves-effect waves-light navbtn active navbtn-last red lighten-1">
   613                          <i class="las la-redo-alt left app-btn-icon"></i>
   614                        </button>
   615                        <button type="submit" name="submit" class="btn waves-effect waves-light navbtn active navbtn-last">
   616                          <i class="las la-check-square left app-btn-icon"></i>
   617                          <span class="app-btn-text">Verify</span>
   618                        </button>
   619                    </div>
   620                  </div>
   621                </div>
   622              </form>
   623            {{ end }}
   624            {{ if eq .Data.view "mfa-test-app-status" }}
   625            <div class="row">
   626              <div class="col s12">
   627              <h1>Test MFA Authenticator Application</h1>
   628              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   629              {{ if eq .Data.status "SUCCESS" }}
   630                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa" }}">
   631                  <button type="button" class="btn waves-effect waves-light navbtn active">
   632                    <i class="las la-undo-alt left app-btn-icon"></i>
   633                    <span class="app-btn-text">Go Back</span>
   634                  </button>
   635                </a>
   636              {{ else }}
   637                {{ if ne .Data.mfa_token_id "" }}
   638                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa/test/app/" .Data.mfa_digits .Data.mfa_token_id }}">
   639                  <button type="button" class="btn waves-effect waves-light navbtn active">
   640                    <i class="las la-undo-alt left app-btn-icon"></i>
   641                    <span class="app-btn-text">Try Again</span>
   642                  </button>
   643                </a>
   644                {{ end }}
   645              {{ end }}
   646              </div>
   647            </div>
   648            {{ end }}
   649            {{ if eq .Data.view "mfa-delete-status" }}
   650            <div class="row">
   651              <div class="col s12">
   652              <h1>MFA Token</h1>
   653              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   654              <a href="{{ pathjoin .ActionEndpoint "/settings/mfa" }}">
   655                <button type="button" class="btn waves-effect waves-light navbtn active">
   656                  <i class="las la-undo-alt left app-btn-icon"></i>
   657                  <span class="app-btn-text">Go Back</span>
   658                </button>
   659              </a>
   660              </div>
   661            </div>
   662            {{ end }}
   663            {{ if eq .Data.view "mfa-add-u2f" }}
   664              <form id="mfa-add-u2f-form" action="{{ pathjoin .ActionEndpoint "/settings/mfa/add/u2f" }}" method="POST">
   665                <div class="row">
   666                  <div class="col s12">
   667                    <h1>Add U2F Security Key</h1>
   668                    <p>Please insert your U2F (USB, NFC, or Bluetooth) Security Key, e.g. Yubikey.</p>
   669                    <p>Then, please click "Register" button below.</p>
   670                    <div class="input-field">
   671                      <input id="comment" name="comment" type="text" class="validate" pattern="[A-Za-z0-9 -]{4,25}"
   672                        title="Authentication code should contain 4-25 characters and consists of A-Z, a-z, 0-9, space, and dash characters."
   673                        autocorrect="off" autocapitalize="off" autocomplete="off"
   674                        required />
   675                      <label for="comment">Comment</label>
   676                    </div>
   677                    <input class="hide" id="webauthn_register" name="webauthn_register" type="text" />
   678                    <input class="hide" id="webauthn_challenge" name="webauthn_challenge" type="text" value="{{ .Data.webauthn_challenge }}" />
   679                    <button id="mfa-add-u2f-button" type="button" name="action" onclick="u2f_token_register('mfa-add-u2f-form', 'mfa-add-u2f-button');" class="btn waves-effect waves-light navbtn active navbtn-last app-btn">
   680                      <i class="las la-plus-circle left app-btn-icon"></i>
   681                      <span class="app-btn-text">Register</span>
   682                    </button>
   683                  </div>
   684                </div>
   685              </form>
   686            {{ end }}
   687            {{ if eq .Data.view "mfa-add-u2f-status" }}
   688            <div class="row">
   689              <div class="col s12">
   690              <h1>U2F Security Key</h1>
   691              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   692              {{ if eq .Data.status "SUCCESS" }}
   693                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa" }}">
   694                  <button type="button" class="btn waves-effect waves-light navbtn active">
   695                    <i class="las la-undo-alt left app-btn-icon"></i>
   696                    <span class="app-btn-text">Go Back</span>
   697                  </button>
   698                </a>
   699              {{ else }}
   700                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa/add/u2f" }}">
   701                  <button type="button" class="btn waves-effect waves-light navbtn active">
   702                    <i class="las la-undo-alt left app-btn-icon"></i>
   703                    <span class="app-btn-text">Try Again</span>
   704                  </button>
   705                </a>
   706              {{ end }}
   707              </div>
   708            </div>
   709            {{ end }}
   710            {{ if eq .Data.view "mfa-test-u2f" }}
   711              <form id="mfa-test-u2f-form" action="{{ pathjoin .ActionEndpoint "/settings/mfa/test/u2f/generic" .Data.mfa_token_id }}" method="POST">
   712                <div class="row">
   713                  <div class="col s12 m12 l12">
   714                    <h1>Test Token</h1>
   715                    <p>
   716                      Insert your hardware token into a USB port.
   717                      Next, click "Authenticate" button below.
   718                      When prompted, touch, or otherwise trigger the hardware token.
   719                    </p>
   720                    <input id="webauthn_request" name="webauthn_request" type="hidden" />
   721                    <a id="mfa-test-u2f-button" onclick="u2f_token_authenticate('mfa-test-u2f-form', 'mfa-test-u2f-button');" class="btn waves-effect waves-light navbtn active navbtn-last">
   722                      <i class="las la-check-square left app-btn-icon"></i>
   723                      <span class="app-btn-text">Verify</span>
   724                    </a>
   725                  </div>
   726                  <input id="token_id" name="token_id" type="hidden" value="{{ .Data.mfa_token_id }}" />
   727                </div>
   728              </form>
   729            {{ end }}
   730            {{ if eq .Data.view "mfa-test-u2f-status" }}
   731            <div class="row">
   732              <div class="col s12">
   733              <h1>Test Token</h1>
   734              <p>{{.Data.status }}: {{ .Data.status_reason }}</p>
   735              {{ if eq .Data.status "SUCCESS" }}
   736                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa" }}">
   737                  <button type="button" class="btn waves-effect waves-light navbtn active">
   738                    <i class="las la-undo-alt left app-btn-icon"></i>
   739                    <span class="app-btn-text">Go Back</span>
   740                  </button>
   741                </a>
   742              {{ else }}
   743                {{ if ne .Data.mfa_token_id "" }}
   744                <a href="{{ pathjoin .ActionEndpoint "/settings/mfa/test/u2f/generic" .Data.mfa_token_id }}">
   745                  <button type="button" class="btn waves-effect waves-light navbtn active">
   746                    <i class="las la-undo-alt left app-btn-icon"></i>
   747                    <span class="app-btn-text">Try Again</span>
   748                  </button>
   749                </a>
   750                {{ end }}
   751              {{ end }}
   752              </div>
   753            </div>
   754            {{ end }}
   755            {{ if eq .Data.view "password" }}
   756              <form action="{{ pathjoin .ActionEndpoint "/settings/password/edit" }}" method="POST">
   757                <div class="row">
   758                  <h1>Password Management</h1>
   759                  <div class="row">
   760                    <div class="col s12 m6 l6">
   761                      <p>If you want to change your password, please provide your current password and 
   762                      </p>
   763                      <div class="input-field">
   764                        <input id="secret1" name="secret1" type="password" autocorrect="off" autocapitalize="off" autocomplete="off" required />
   765                        <label for="secret1">Current Password</label>
   766                      </div>
   767                      <div class="input-field">
   768                        <input id="secret2" name="secret2" type="password" autocorrect="off" autocapitalize="off" autocomplete="off" required />
   769                        <label for="secret2">New Password</label>
   770                      </div>
   771                      <div class="input-field">
   772                        <input id="secret3" name="secret3" type="password" autocorrect="off" autocapitalize="off" autocomplete="off" required />
   773                        <label for="secret3">Confirm New Password</label>
   774                      </div>
   775                    </div>
   776                  </div>
   777                </div>
   778                <div class="row right">
   779                  <button type="submit" name="submit" class="btn waves-effect waves-light navbtn active navbtn-last app-btn">
   780                    <i class="las la-paper-plane left app-btn-icon"></i>
   781                    <span class="app-btn-text">Change Password</span>
   782                  </button>
   783                </div>
   784              </form>
   785            {{ end }}
   786            {{ if eq .Data.view "password-edit" }}
   787            <div class="row">
   788              <div class="col s12">
   789              {{ if eq .Data.status "SUCCESS" }}
   790                <h1>Password Has Been Changed</h1>
   791                <p>Please log out and log back in.</p>
   792              {{ else }}
   793                <h1>Password Change Failed</h1>
   794                <p>Reason: {{ .Data.status_reason }} </p>
   795                <a href="{{ pathjoin .ActionEndpoint "/settings/password" }}">
   796                  <button type="button" class="btn waves-effect waves-light navbtn active">
   797                    <i class="las la-undo-alt left app-btn-icon"></i>
   798                    <span class="app-btn-text">Try Again</span>
   799                  </button>
   800                </a>
   801              {{ end }}
   802              </div>
   803            </div>
   804            {{ end }}
   805            {{ if eq .Data.view "connected" }}
   806            <div class="row">
   807              <div class="col s12">
   808              <p>No connected accounts found.</p>
   809              </div>
   810            </div>
   811            {{ end }}
   812          </div>
   813        </div>
   814      </div>
   815  
   816      <!-- Optional JavaScript -->
   817      <script src="{{ pathjoin .ActionEndpoint "/assets/materialize-css/js/materialize.js" }}"></script>
   818      {{ if or (eq .Data.view "sshkeys-add") (eq .Data.view "gpgkeys-add") (eq .Data.view "sshkeys-view") (eq .Data.view "gpgkeys-view") }}
   819      <script src="{{ pathjoin .ActionEndpoint "/assets/highlight.js/js/highlight.js" }}"></script>
   820      <script src="{{ pathjoin .ActionEndpoint "/assets/highlight.js/js/languages/json.min.js" }}"></script>
   821      <script src="{{ pathjoin .ActionEndpoint "/assets/highlight.js/js/languages/plaintext.min.js" }}"></script>
   822      {{ end }}
   823      {{ if or (eq .Data.view "apikeys-add") (eq .Data.view "apikeys-add-status") }}
   824      <script src="{{ pathjoin .ActionEndpoint "/assets/highlight.js/js/highlight.js" }}"></script>
   825      <script src="{{ pathjoin .ActionEndpoint "/assets/highlight.js/js/languages/json.min.js" }}"></script>
   826      <script src="{{ pathjoin .ActionEndpoint "/assets/highlight.js/js/languages/plaintext.min.js" }}"></script>
   827      {{ end }}
   828      {{ if or (eq .Data.view "mfa-add-u2f") (eq .Data.view "mfa-test-u2f") }}
   829      <script src="{{ pathjoin .ActionEndpoint "/assets/cbor/cbor.js" }}"></script>
   830      {{ end }}
   831      {{ if eq .Data.ui_options.custom_js_required "yes" }}
   832      <script src="{{ pathjoin .ActionEndpoint "/assets/js/custom.js" }}"></script>
   833      {{ end }}
   834      {{ if or (eq .Data.view "mfa-add-app") (eq .Data.view "mfa-test-app") }}
   835      <script src="{{ pathjoin .ActionEndpoint "/assets/js/mfa_add_app.js" }}"></script>
   836      {{ end }}
   837      {{ if eq .Data.view "mfa-add-u2f" }}
   838      <script src="{{ pathjoin .ActionEndpoint "/assets/js/mfa_add_u2f.js" }}"></script>
   839      {{ end }}
   840      {{ if eq .Data.view "mfa-test-u2f" }}
   841      <script src="{{ pathjoin .ActionEndpoint "/assets/js/mfa_add_u2f.js" }}"></script>
   842      <script src="{{ pathjoin .ActionEndpoint "/assets/js/mfa_test_u2f.js" }}"></script>
   843      {{ end }}
   844      {{ if or (eq .Data.view "sshkeys-add") (eq .Data.view "gpgkeys-add") (eq .Data.view "sshkeys-view") (eq .Data.view "gpgkeys-view") }}
   845      <script>
   846      hljs.initHighlightingOnLoad();
   847      </script>
   848      {{ end }}
   849      {{ if or (eq .Data.view "apikeys-add") (eq .Data.view "apikeys-add-status") }}
   850      <script>
   851      hljs.initHighlightingOnLoad();
   852      </script>
   853      {{ end }}
   854      {{ if .Message }}
   855      <script>
   856      var toastHTML = '<span class="app-error-text">{{ .Message }}</span><button class="btn-flat toast-action" onclick="M.Toast.dismissAll();">Close</button>';
   857      toastElement = M.toast({
   858        html: toastHTML,
   859        classes: 'toast-error'
   860      });
   861      const appContainer = document.querySelector('.app-container')
   862      appContainer.prepend(toastElement.el)
   863      </script>
   864      {{ end }}
   865      {{ if eq .Data.view "mfa-add-u2f" }}
   866      <script>
   867  function u2f_token_register(formID, btnID) {
   868    const params = {
   869      challenge: "{{ .Data.webauthn_challenge }}",
   870      rp_name: "{{ .Data.webauthn_rp_name }}",
   871      user_id: "{{ .Data.webauthn_user_id }}",
   872      user_name: "{{ .Data.webauthn_user_email }}",
   873      user_display_name: "{{ .Data.webauthn_user_display_name }}",
   874      user_verification: "{{ .Data.webauthn_user_verification }}",
   875      attestation: "{{ .Data.webauthn_attestation }}",
   876    };
   877    register_u2f_token(formID, btnID, params);
   878  }
   879      </script>
   880      {{ end }}
   881  
   882      {{ if eq .Data.view "mfa-test-u2f" }}
   883      <script>
   884  function u2f_token_authenticate(formID, btnID) {
   885    const params = {
   886      challenge: "{{ .Data.webauthn_challenge }}",
   887      timeout: {{ .Data.webauthn_timeout }},
   888      rp_name: "{{ .Data.webauthn_rp_name }}",
   889      user_verification: "{{ .Data.webauthn_user_verification }}",
   890      {{ if .Data.webauthn_credentials }}
   891      allowed_credentials: [
   892      {{ range .Data.webauthn_credentials }}
   893        {
   894          id: "{{ .id }}",
   895          type: "{{ .type }}",
   896          transports: [{{ .transports }}],
   897        },
   898      {{ end }}
   899      ],
   900      {{ else }}
   901      allowed_credentials: [],
   902      {{ end }}
   903      ext_uvm: {{ .Data.webauthn_ext_uvm }},
   904      ext_loc: {{ .Data.webauthn_ext_loc }},
   905      ext_tx_auth_simple: "{{ .Data.webauthn_tx_auth_simple }}",
   906    };
   907    authenticate_u2f_token(formID, btnID, params);
   908  }
   909      </script>
   910      {{ end }}
   911    </body>
   912  </html>