Files
sftpgo/templates/user.html
T
Nicola Murino 0823928f98 allow to disable login filesystem checks
SFTPGo requires that the user's home directory, virtual folder root,
and intermediate paths to virtual folders exist to work properly.
If you already know that the required directories exist, disabling
these checks will speed up login.
2021-04-05 17:57:30 +02:00

469 lines
25 KiB
HTML

{{template "base" .}}
{{define "title"}}{{.Title}}{{end}}
{{define "extra_css"}}
<link href="/static/vendor/tempusdominus/css/tempusdominus-bootstrap-4.min.css" rel="stylesheet">
{{end}}
{{define "page_body"}}
<!-- Page Heading -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">{{.Title}}</h6>
</div>
<div class="card-body">
{{if .Error}}
<div class="card mb-4 border-left-warning">
<div class="card-body text-form-error">{{.Error}}</div>
</div>
{{end}}
{{if eq .Mode 3}}
<div class="card mb-4 border-left-info">
<div class="card-body">
Generate a data provider independent JSON file to create new users or update existing ones.
<br>
The following placeholders are supported:
<br><br>
<ul>
<li><span class="text-success">%username%</span> will be replaced with the specified username</li>
<li><span class="text-success">%password%</span> will be replaced with the specified password</li>
</ul>
The generated users file can be imported from the "Maintenance" section.
{{if .User.Username}}
<br>
Please note that no credentials were copied from user "{{.User.Username}}", you have to set them explicitly.
{{end}}
</div>
</div>
{{end}}
<form id="user_form" enctype="multipart/form-data" action="{{.CurrentURL}}" method="POST" autocomplete="off" {{if eq .Mode 3}}target="_blank"{{end}}>
{{if eq .Mode 3}}
<div class="form-group row">
<label for="idUsers" class="col-sm-2 col-form-label">Users</label>
<div class="col-sm-10">
<textarea class="form-control" id="idUsers" name="users" rows="5" required
aria-describedby="usersHelpBlock"></textarea>
<small id="usersHelpBlock" class="form-text text-muted">
Specify the username and at least one of the password and public key. Each line must be username::password::public-key
</small>
</div>
</div>
<input type="hidden" name="username" id="idUsername" value="{{.User.Username}}">
{{else}}
<div class="form-group row">
<label for="idUsername" class="col-sm-2 col-form-label">Username</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idUsername" name="username" placeholder=""
value="{{.User.Username}}" maxlength="255" autocomplete="nope" required {{if ge .Mode 2}}readonly{{end}}>
</div>
</div>
{{end}}
<div class="form-group row">
<label for="idDescription" class="col-sm-2 col-form-label">Description</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idDescription" name="description" placeholder=""
value="{{.User.Description}}" maxlength="255" aria-describedby="descriptionHelpBlock">
<small id="descriptionHelpBlock" class="form-text text-muted">
Optional description, for example the user full name
</small>
</div>
</div>
<div class="form-group row">
<label for="idStatus" class="col-sm-2 col-form-label">Status</label>
<div class="col-sm-10">
<select class="form-control" id="idStatus" name="status">
<option value="1" {{if eq .User.Status 1 }}selected{{end}}>Active</option>
<option value="0" {{if eq .User.Status 0 }}selected{{end}}>Inactive</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="idExpirationDate" class="col-sm-2 col-form-label">Expiration Date</label>
<div class="col-sm-10 input-group date" id="expirationDatePicker" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input" id="idExpirationDate"
data-target="#expirationDatePicker">
<div class="input-group-append" data-target="#expirationDatePicker" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fas fa-calendar"></i></div>
</div>
</div>
</div>
{{if ne .Mode 3}}
<div class="form-group row">
<label for="idPassword" class="col-sm-2 col-form-label">Password</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="idPassword" name="password" value="{{.User.Password}}" placeholder="">
</div>
</div>
<div class="form-group row">
<label for="idPublicKeys" class="col-sm-2 col-form-label">Public keys</label>
<div class="col-sm-10">
<textarea class="form-control" id="idPublicKeys" name="public_keys" rows="3"
aria-describedby="pkHelpBlock">{{range .User.PublicKeys}}{{.}}&#10;{{end}}</textarea>
<small id="pkHelpBlock" class="form-text text-muted">
One public key or SSH user certificate per line
</small>
</div>
</div>
{{end}}
<div class="form-group row">
<label for="idTLSUsername" class="col-sm-2 col-form-label">TLS username</label>
<div class="col-sm-10">
<select class="form-control" id="idTLSUsername" name="tls_username" aria-describedby="tlsUsernameHelpBlock">
<option value="None" {{if eq .User.Filters.TLSUsername "None" }}selected{{end}}>None</option>
<option value="CommonName" {{if eq .User.Filters.TLSUsername "CommonName" }}selected{{end}}>Common Name</option>
</select>
<small id="tlsUsernameHelpBlock" class="form-text text-muted">
Defines the TLS certificate field to use as username. Ignored if mutual TLS is disabled
</small>
</div>
</div>
<div class="form-group row">
<label for="idProtocols" class="col-sm-2 col-form-label">Denied protocols</label>
<div class="col-sm-10">
<select class="form-control" id="idProtocols" name="denied_protocols" multiple>
{{range $protocol := .ValidProtocols}}
<option value="{{$protocol}}" {{range $p :=$.User.Filters.DeniedProtocols }}{{if eq $p $protocol}}selected{{end}}{{end}}>{{$protocol}}
</option>
{{end}}
</select>
</div>
</div>
<div class="form-group row">
<label for="idLoginMethods" class="col-sm-2 col-form-label">Denied login methods</label>
<div class="col-sm-10">
<select class="form-control" id="idLoginMethods" name="ssh_login_methods" multiple>
{{range $method := .ValidLoginMethods}}
<option value="{{$method}}" {{range $m :=$.User.Filters.DeniedLoginMethods }}{{if eq $m $method}}selected{{end}}{{end}}>{{$method}}
</option>
{{end}}
</select>
</div>
</div>
<div class="form-group row">
<label for="idPermissions" class="col-sm-2 col-form-label">Permissions</label>
<div class="col-sm-10">
<select class="form-control" id="idPermissions" name="permissions" required multiple>
{{range $validPerm := .ValidPerms}}
<option value="{{$validPerm}}" {{range $perm :=$.RootDirPerms }}{{if eq $perm $validPerm}}selected{{end}}{{end}}>{{$validPerm}}
</option>
{{end}}
</select>
</div>
</div>
<div class="form-group row">
<label for="idSubDirsPermissions" class="col-sm-2 col-form-label">Sub dirs permissions</label>
<div class="col-sm-10">
<textarea class="form-control" id="idSubDirsPermissions" name="sub_dirs_permissions" rows="3"
aria-describedby="subDirsHelpBlock">{{range $dir, $perms := .User.Permissions -}}
{{if ne $dir "/" -}}
{{$dir}}::{{range $index, $p := $perms}}{{if $index}},{{end}}{{$p}}{{end}}&#10;
{{- end}}
{{- end}}</textarea>
<small id="subDirsHelpBlock" class="form-text text-muted">
One exposed virtual directory path per line as /dir::perms, for example /somedir::list,download
</small>
</div>
</div>
<div class="form-group row">
<label for="idHomeDir" class="col-sm-2 col-form-label">Home Dir</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idHomeDir" name="home_dir" placeholder=""
value="{{.User.HomeDir}}" maxlength="255">
</div>
</div>
<div class="form-group row">
<label for="idVirtualFolders" class="col-sm-2 col-form-label">Virtual folders</label>
<div class="col-sm-10">
<textarea class="form-control" id="idVirtualFolders" name="virtual_folders" rows="3"
aria-describedby="vfHelpBlock">{{range $index, $mapping := .User.VirtualFolders -}}
{{$mapping.VirtualPath}}::{{$mapping.Name}}::{{$mapping.QuotaFiles}}::{{$mapping.QuotaSize}}&#10;
{{- end}}</textarea>
<small id="vfHelpBlock" class="form-text text-muted">
One mapping per line as vpath::folder-name::[quota_files]::[quota_size(bytes)], for example
/vdir::afolder or /vdir::afolder::10::104857600. Quota -1 means included inside user quota.
Ignored for non local filesystems
</small>
</div>
</div>
<div class="form-group row">
<label for="idQuotaFiles" class="col-sm-2 col-form-label">Quota files</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idQuotaFiles" name="quota_files" placeholder=""
value="{{.User.QuotaFiles}}" min="0" aria-describedby="qfHelpBlock">
<small id="qfHelpBlock" class="form-text text-muted">
0 means no limit
</small>
</div>
<div class="col-sm-2"></div>
<label for="idQuotaSize" class="col-sm-2 col-form-label">Quota size (bytes)</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idQuotaSize" name="quota_size" placeholder=""
value="{{.User.QuotaSize}}" min="0" aria-describedby="qsHelpBlock">
<small id="qsHelpBlock" class="form-text text-muted">
0 means no limit
</small>
</div>
</div>
<div class="form-group row">
<label for="idMaxUploadSize" class="col-sm-2 col-form-label">Max file upload size (bytes)</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idMaxUploadSize" name="max_upload_file_size"
placeholder="" value="{{.User.Filters.MaxUploadFileSize}}" min="0"
aria-describedby="fqsHelpBlock">
<small id="fqsHelpBlock" class="form-text text-muted">
0 means no limit
</small>
</div>
<div class="col-sm-2"></div>
<label for="idMaxSessions" class="col-sm-2 col-form-label">Max sessions</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idMaxSessions" name="max_sessions" placeholder=""
value="{{.User.MaxSessions}}" min="0" aria-describedby="sessionsHelpBlock">
<small id="sessionsHelpBlock" class="form-text text-muted">
0 means no limit
</small>
</div>
</div>
<div class="form-group row">
<label for="idUploadBandwidth" class="col-sm-2 col-form-label">Bandwidth UL (KB/s)</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idUploadBandwidth" name="upload_bandwidth"
placeholder="" value="{{.User.UploadBandwidth}}" min="0" aria-describedby="ulHelpBlock">
<small id="ulHelpBlock" class="form-text text-muted">
0 means no limit
</small>
</div>
<div class="col-sm-2"></div>
<label for="idDownloadBandwidth" class="col-sm-2 col-form-label">Bandwidth DL (KB/s)</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idDownloadBandwidth" name="download_bandwidth"
placeholder="" value="{{.User.DownloadBandwidth}}" min="0" aria-describedby="dlHelpBlock">
<small id="dlHelpBlock" class="form-text text-muted">
0 means no limit
</small>
</div>
</div>
<div class="form-group row">
<label for="idUID" class="col-sm-2 col-form-label">UID</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idUID" name="uid" placeholder="" value="{{.User.UID}}"
min="0" max="2147483647">
</div>
<div class="col-sm-2"></div>
<label for="idGID" class="col-sm-2 col-form-label">GID</label>
<div class="col-sm-3">
<input type="number" class="form-control" id="idGID" name="gid" placeholder="" value="{{.User.GID}}"
min="0" max="2147483647">
</div>
</div>
<div class="form-group row">
<label for="idDeniedIP" class="col-sm-2 col-form-label">Denied IP/Mask</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idDeniedIP" name="denied_ip" placeholder=""
value="{{.User.GetDeniedIPAsString}}" maxlength="255" aria-describedby="deniedIPHelpBlock">
<small id="deniedIPHelpBlock" class="form-text text-muted">
Comma separated IP/Mask in CIDR format, for example "192.168.1.0/24,10.8.0.100/32"
</small>
</div>
</div>
<div class="form-group row">
<label for="idAllowedIP" class="col-sm-2 col-form-label">Allowed IP/Mask</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idAllowedIP" name="allowed_ip" placeholder=""
value="{{.User.GetAllowedIPAsString}}" maxlength="255" aria-describedby="allowedIPHelpBlock">
<small id="allowedIPHelpBlock" class="form-text text-muted">
Comma separated IP/Mask in CIDR format, for example "192.168.1.0/24,10.8.0.100/32"
</small>
</div>
</div>
<div class="form-group row">
<label for="idFilePatternsDenied" class="col-sm-2 col-form-label">Denied file patterns</label>
<div class="col-sm-10">
<textarea class="form-control" id="idFilePatternsDenied" name="denied_patterns" rows="3"
aria-describedby="deniedPatternsHelpBlock">{{range $index, $filter := .User.Filters.FilePatterns -}}
{{if $filter.DeniedPatterns -}}
{{$filter.Path}}::{{range $idx, $p := $filter.DeniedPatterns}}{{if $idx}},{{end}}{{$p}}{{end}}&#10;
{{- end}}
{{- end}}</textarea>
<small id="deniedPatternsHelpBlock" class="form-text text-muted">
One exposed virtual directory per line as /dir::pattern1,pattern2, for example
/subdir::*.zip,*.rar
</small>
</div>
</div>
<div class="form-group row">
<label for="idFilePatternsAllowed" class="col-sm-2 col-form-label">Allowed file patterns</label>
<div class="col-sm-10">
<textarea class="form-control" id="idFilePatternsAllowed" name="allowed_patterns" rows="3"
aria-describedby="allowedPatternsHelpBlock">{{range $index, $filter := .User.Filters.FilePatterns -}}
{{if $filter.AllowedPatterns -}}
{{$filter.Path}}::{{range $idx, $p := $filter.AllowedPatterns}}{{if $idx}},{{end}}{{$p}}{{end}}&#10;
{{- end}}
{{- end}}</textarea>
<small id="allowedPatternsHelpBlock" class="form-text text-muted">
One exposed virtual directory per line as /dir::pattern1,pattern2, for example
/somedir::*.jpg,*.png
</small>
</div>
</div>
<div class="form-group row">
<label for="idFilesExtensionsDenied" class="col-sm-2 col-form-label">Denied file extensions</label>
<div class="col-sm-10">
<textarea class="form-control" id="idFilesExtensionsDenied" name="denied_extensions" rows="3"
aria-describedby="deniedExtensionsHelpBlock">{{range $index, $filter := .User.Filters.FileExtensions -}}
{{if $filter.DeniedExtensions -}}
{{$filter.Path}}::{{range $idx, $p := $filter.DeniedExtensions}}{{if $idx}},{{end}}{{$p}}{{end}}&#10;
{{- end}}
{{- end}}</textarea>
<small id="deniedExtensionsHelpBlock" class="form-text text-muted">
One exposed virtual directory per line as /dir::extension1,extension2, for example
/subdir::.zip,.rar. Deprecated, use file patterns
</small>
</div>
</div>
<div class="form-group row">
<label for="idFilesExtensionsAllowed" class="col-sm-2 col-form-label">Allowed file extensions</label>
<div class="col-sm-10">
<textarea class="form-control" id="idFilesExtensionsAllowed" name="allowed_extensions" rows="3"
aria-describedby="allowedExtensionsHelpBlock">{{range $index, $filter := .User.Filters.FileExtensions -}}
{{if $filter.AllowedExtensions -}}
{{$filter.Path}}::{{range $idx, $p := $filter.AllowedExtensions}}{{if $idx}},{{end}}{{$p}}{{end}}&#10;
{{- end}}
{{- end}}</textarea>
<small id="allowedExtensionsHelpBlock" class="form-text text-muted">
One exposed virtual directory per line as /dir::extension1,extension2, for example
/somedir::.jpg,.png. Deprecated, use file patterns
</small>
</div>
</div>
<div class="form-group row">
<label for="idHooks" class="col-sm-2 col-form-label">Hooks</label>
<div class="col-sm-10">
<select class="form-control" id="idHooks" name="hooks" multiple>
<option value="external_auth_disabled" {{if .User.Filters.Hooks.ExternalAuthDisabled}}selected{{end}}>
External auth disabled
</option>
<option value="pre_login_disabled" {{if .User.Filters.Hooks.PreLoginDisabled}}selected{{end}}>
Pre-login disabled
</option>
<option value="check_password_disabled" {{if .User.Filters.Hooks.CheckPasswordDisabled}}selected{{end}}>
Check password disabled
</option>
</select>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="idDisableFsChecks" name="disable_fs_checks"
{{if .User.Filters.DisableFsChecks}}checked{{end}} aria-describedby="disableFsChecksHelpBlock">
<label for="idDisableFsChecks" class="form-check-label">Disable filesystem checks</label>
<small id="disableFsChecksHelpBlock" class="form-text text-muted">
Disable checks for existence and automatic creation of home directory and virtual folders
</small>
</div>
</div>
{{template "fshtml" .User.FsConfig}}
<div class="form-group row">
<label for="idAdditionalInfo" class="col-sm-2 col-form-label">Additional info</label>
<div class="col-sm-10">
<textarea class="form-control" id="idAdditionalInfo" name="additional_info" rows="3"
aria-describedby="additionalInfoHelpBlock">{{.User.AdditionalInfo}}</textarea>
<small id="additionalInfoHelpBlock" class="form-text text-muted">
Free form text field
</small>
</div>
</div>
{{if eq .Mode 2}}
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="idDisconnect" name="disconnect"
aria-describedby="disconnectHelpBlock">
<label for="idDisconnect" class="form-check-label">Disconnect the user after the update</label>
<small id="disconnectHelpBlock" class="form-text text-muted">
This way you force the user to login again, if connected, and so to use the new configuration
</small>
</div>
</div>
{{end}}
<input type="hidden" name="expiration_date" id="hidden_start_datetime" value="">
<input type="hidden" name="_form_token" value="{{.CSRFToken}}">
<button type="submit" class="btn btn-primary float-right mt-3 px-5 px-3">{{if eq .Mode 3}}Generate and export users{{else}}Submit{{end}}</button>
</form>
</div>
</div>
{{end}}
{{define "extra_js"}}
<script src="/static/vendor/moment/js/moment.min.js"></script>
<script src="/static/vendor/tempusdominus/js/tempusdominus-bootstrap-4.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#expirationDatePicker').datetimepicker({
format: 'YYYY-MM-DD',
buttons: {
showClear: false,
showClose: true,
showToday: false
}
});
{{ if gt .User.ExpirationDate 0 }}
var input_dt = moment({{.User.ExpirationDate }}).format('YYYY-MM-DD');
$('#idExpirationDate').val(input_dt);
$('#expirationDatePicker').datetimepicker('viewDate', input_dt);
{{ end }}
$("#user_form").submit(function (event) {
var dt = $('#idExpirationDate').val();
if (dt) {
var d = $('#expirationDatePicker').datetimepicker('viewDate');
if (d) {
var dateString = moment(d).format('YYYY-MM-DD HH:mm:ss');
$('#hidden_start_datetime').val(dateString);
} else {
$('#hidden_start_datetime').val("");
}
} else {
$('#hidden_start_datetime').val("");
}
return true;
});
onFilesystemChanged('{{.User.FsConfig.Provider}}');
});
{{template "fsjs"}}
</script>
{{end}}