This commit is contained in:
2024-04-21 14:42:52 +02:00
parent 4b69674ede
commit 8a25f53c99
10700 changed files with 55767 additions and 14201 deletions

View File

@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests;
class AssetCheckinRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
];
}
public function response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Http\Requests;
class AssetCheckoutRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
'status_id' => 'exists:status_labels,id,deployable,1',
'checkout_to_type' => 'required|in:asset,location,user',
];
return $rules;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace App\Http\Requests;
class CustomAssetReportRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'purchase_start' => 'date|date_format:Y-m-d|nullable',
'purchase_end' => 'date|date_format:Y-m-d|nullable',
'created_start' => 'date|date_format:Y-m-d|nullable',
'created_end' => 'date|date_format:Y-m-d|nullable',
'checkout_date_start' => 'date|date_format:Y-m-d|nullable',
'checkout_date_end' => 'date|date_format:Y-m-d|nullable',
'expected_checkin_start' => 'date|date_format:Y-m-d|nullable',
'expected_checkin_end' => 'date|date_format:Y-m-d|nullable',
'checkin_date_start' => 'date|date_format:Y-m-d|nullable',
'checkin_date_end' => 'date|date_format:Y-m-d|nullable',
'last_audit_start' => 'date|date_format:Y-m-d|nullable',
'last_audit_end' => 'date|date_format:Y-m-d|nullable',
'next_audit_start' => 'date|date_format:Y-m-d|nullable',
'next_audit_end' => 'date|date_format:Y-m-d|nullable',
];
}
public function response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
class CustomFieldRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(Request $request)
{
$rules = [];
$rules['associate_fieldsets.*'] = 'nullable|integer|exists:custom_fieldsets,id';
switch ($this->method()) {
// Brand new
case 'POST':
{
$rules['name'] = 'required|unique:custom_fields';
break;
}
// Save all fields
case 'PUT':
$rules['name'] = 'required';
break;
// Save only what's passed
case 'PATCH':
{
$rules['name'] = 'required';
break;
}
default:break;
}
$rules['custom_format'] = 'valid_regex';
return $rules;
}
public function messages()
{
return [
'associate_fieldsets.*.exists' => trans('admin/custom_fields/message/does_not_exist'),
];
}
}

View File

@ -0,0 +1,171 @@
<?php
namespace App\Http\Requests;
use App\Models\SnipeModel;
use enshrined\svgSanitize\Sanitizer;
use Intervention\Image\Facades\Image;
use App\Http\Traits\ConvertsBase64ToFiles;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Exception\NotReadableException;
class ImageUploadRequest extends Request
{
use ConvertsBase64ToFiles;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp,avif',
'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp,avif',
];
}
public function response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
/**
* Fields that should be traited from base64 to files
*/
protected function base64FileKeys(): array
{
/**
* image_source is here just legacy reasons. Api\AssetController
* had it once to allow encoded image uploads.
*/
return [
'image' => 'auto',
'image_source' => 'auto'
];
}
/**
* Handle and store any images attached to request
* @param SnipeModel $item Item the image is associated with
* @param string $path location for uploaded images, defaults to uploads/plural of item type.
* @return SnipeModel Target asset is being checked out to.
*/
public function handleImages($item, $w = 600, $form_fieldname = 'image', $path = null, $db_fieldname = 'image')
{
$type = strtolower(class_basename(get_class($item)));
if (is_null($path)) {
$path = str_plural($type);
if ($type == 'assetmodel') {
$path = 'models';
}
if ($type == 'user') {
$path = 'avatars';
}
}
if ($this->offsetGet($form_fieldname) instanceof UploadedFile) {
$image = $this->offsetGet($form_fieldname);
\Log::debug('Image is an instance of UploadedFile');
} elseif ($this->hasFile($form_fieldname)) {
$image = $this->file($form_fieldname);
\Log::debug('Just use regular upload for '.$form_fieldname);
} else {
\Log::debug('No image found for form fieldname: '.$form_fieldname);
}
if (isset($image)) {
if (!config('app.lock_passwords')) {
$ext = $image->guessExtension();
$file_name = $type.'-'.$form_fieldname.'-'.$item->id.'-'.str_random(10).'.'.$ext;
\Log::info('File name will be: '.$file_name);
\Log::debug('File extension is: '.$ext);
if (($image->getMimeType() == 'image/avif') || ($image->getMimeType() == 'image/webp')) {
// If the file is a webp or avif, we need to just move it since webp support
// needs to be compiled into gd for resizing to be available
Storage::disk('public')->put($path.'/'.$file_name, file_get_contents($image));
} elseif($image->getMimeType() == 'image/svg+xml') {
// If the file is an SVG, we need to clean it and NOT encode it
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($image->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
Storage::disk('public')->put($path . '/' . $file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug($e);
}
} else {
try {
$upload = Image::make($image->getRealPath())->setFileInfoFromPath($image->getRealPath())->resize(null, $w, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->orientate();
} catch(NotReadableException $e) {
\Log::debug($e);
$validator = \Validator::make([], []);
$validator->errors()->add($form_fieldname, trans('general.unaccepted_image_type', ['mimetype' => $image->getClientMimeType()]));
throw new \Illuminate\Validation\ValidationException($validator);
}
// This requires a string instead of an object, so we use ($string)
Storage::disk('public')->put($path.'/'.$file_name, (string) $upload->encode());
}
// Remove Current image if exists
if (($item->{$form_fieldname}!='') && (Storage::disk('public')->exists($path.'/'.$item->{$db_fieldname}))) {
try {
Storage::disk('public')->delete($path.'/'.$item->{$form_fieldname});
} catch (\Exception $e) {
\Log::debug('Could not delete old file. '.$path.'/'.$file_name.' does not exist?');
}
}
$item->{$db_fieldname} = $file_name;
}
// If the user isn't uploading anything new but wants to delete their old image, do so
} elseif ($this->input('image_delete') == '1') {
\Log::debug('Deleting image');
try {
Storage::disk('public')->delete($path.'/'.$item->{$db_fieldname});
$item->{$db_fieldname} = null;
} catch (\Exception $e) {
\Log::debug($e);
}
}
return $item;
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace App\Http\Requests;
use App\Models\Import;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
class ItemImportRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'import-type' => 'required',
];
}
public function import(Import $import)
{
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
$filename = config('app.private_uploads').'/imports/'.$import->file_path;
$import->import_type = $this->input('import-type');
$class = title_case($import->import_type);
$classString = "App\\Importer\\{$class}Importer";
$importer = new $classString($filename);
$import->field_map = request('column-mappings');
$import->save();
$fieldMappings = [];
if ($import->field_map) {
foreach ($import->field_map as $field => $fieldValue) {
$errorMessage = null;
if (is_null($fieldValue)) {
$errorMessage = trans('validation.import_field_empty', ['fieldname' => $field]);
$this->errorCallback($import, $field, $errorMessage);
return $this->errors;
}
}
// We submit as csv field: column, but the importer is happier if we flip it here.
$fieldMappings = array_change_key_case(array_flip($import->field_map), CASE_LOWER);
}
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
->setUserId(Auth::id())
->setUpdating($this->get('import-update'))
->setShouldNotify($this->get('send-welcome'))
->setUsernameFormat('firstname.lastname')
->setFieldMappings($fieldMappings);
$importer->import();
return $this->errors;
}
public function log($string)
{
\Log::Info($string);
}
public function progress($count)
{
// Open for future
}
public function errorCallback($item, $field, $errorString)
{
$this->errors[$item->name][$field] = $errorString;
}
private $errors;
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LicenseCheckoutRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'note' => 'string|nullable',
'asset_id' => 'required_without:assigned_to',
];
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests;
use App\Helpers\Helper;
use Illuminate\Foundation\Http\FormRequest;
abstract class Request extends FormRequest
{
protected $rules = [];
public function json($key = null, $default = null)
{
if ($this->ajax() || $this->wantsJson()) {
json_decode($this->getContent(), false, 512, JSON_THROW_ON_ERROR); // ignore output, just throw
}
return parent::json($key, $default);
}
public function rules()
{
return $this->rules;
}
public function response(array $errors)
{
if ($this->ajax() || $this->wantsJson()) {
return Helper::formatStandardApiResponse('error', null, $errors);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace App\Http\Requests;
use App\Models\Setting;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
class SaveUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
public function response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'department_id' => 'nullable|exists:departments,id',
'manager_id' => 'nullable|exists:users,id',
];
switch ($this->method()) {
// Brand new user
case 'POST':
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
if ($this->request->get('ldap_import') == false) {
$rules['password'] = Setting::passwordComplexityRulesSaving('store').'|confirmed';
}
break;
// Save all fields
case 'PUT':
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
$rules['password'] = Setting::passwordComplexityRulesSaving('update').'|confirmed';
break;
// Save only what's passed
case 'PATCH':
$rules['password'] = Setting::passwordComplexityRulesSaving('update');
break;
default:
break;
}
return $rules;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Http\Requests;
use Session;
class SettingsLdapRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'ldap_server' => 'sometimes|required_if:ldap_enabled,1|url|nullable',
'ldap_uname' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ldap_basedn' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ldap_filter' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ldap_username_field' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ldap_fname_field' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ldap_lname_field' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ldap_auth_filter_query' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ldap_version' => 'sometimes|required_if:ldap_enabled,1|nullable',
'ad_domain' => 'sometimes|required_if:is_ad,1|nullable',
];
return $rules;
}
public function response(array $errors)
{
$this->session()->flash('errors', Session::get('errors', new \Illuminate\Support\ViewErrorBag)
->put('default', new \Illuminate\Support\MessageBag($errors)));
\Input::flash();
return parent::response($errors);
}
}

View File

@ -0,0 +1,159 @@
<?php
namespace App\Http\Requests;
use App\Models\Setting;
use Illuminate\Foundation\Http\FormRequest;
use OneLogin\Saml2\IdPMetadataParser as OneLogin_Saml2_IdPMetadataParser;
use OneLogin\Saml2\Utils as OneLogin_Saml2_Utils;
/**
* This handles validating and cleaning SAML settings provided by the user.
*
* @author Johnson Yi <jyi.dev@outlook.com>
* @author Michael Pietsch <skywalker-11@mi-pietsch.de>
*
* @since 5.0.0
*/
class SettingsSamlRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
];
}
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($this->input('saml_enabled') == '1') {
$idpMetadata = $this->input('saml_idp_metadata');
if (! empty($idpMetadata)) {
try {
if (filter_var($idpMetadata, FILTER_VALIDATE_URL)) {
$metadataInfo = OneLogin_Saml2_IdPMetadataParser::parseRemoteXML($idpMetadata);
} else {
$metadataInfo = OneLogin_Saml2_IdPMetadataParser::parseXML($idpMetadata);
}
} catch (\Exception $e) {
$validator->errors()->add('saml_idp_metadata', trans('validation.url', ['attribute' => 'Metadata']));
}
}
}
$was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
$custom_x509cert = '';
$custom_privateKey = '';
$custom_x509certNew = '';
if (! empty($this->input('saml_custom_settings'))) {
$req_custom_settings = preg_split('/\r\n|\r|\n/', $this->input('saml_custom_settings'));
$custom_settings = [];
foreach ($req_custom_settings as $custom_setting) {
$split = explode('=', $custom_setting, 2);
if (count($split) == 2) {
$split[0] = trim($split[0]);
$split[1] = trim($split[1]);
if (! empty($split[0])) {
$custom_settings[] = implode('=', $split);
}
if ($split[0] == 'sp_x509cert') {
$custom_x509cert = $split[1];
} elseif ($split[0] == 'sp_privateKey') {
$custom_privateKey = $split[1];
} elseif ($split[0] == 'sp_x509certNew') {
//to prepare for Key rollover
$custom_x509certNew = $split[1];
}
}
}
$this->merge(['saml_custom_settings' => implode(PHP_EOL, $custom_settings).PHP_EOL]);
}
$cert_updated = false;
if (! empty($custom_x509cert) && ! empty($custom_privateKey)) {
// custom certificate and private key are defined
$cert_updated = true;
$x509 = openssl_x509_read($custom_x509cert);
$pkey = openssl_pkey_get_private($custom_privateKey);
} elseif ($this->input('saml_sp_regenerate_keypair') == '1' || ! $this->has('saml_sp_x509cert') || $was_custom_x509cert) {
// key regeneration requested, no certificate defined yet or previous custom certicate was removed
error_log('regen');
$cert_updated = true;
$dn = [
'countryName' => 'US',
'stateOrProvinceName' => 'N/A',
'localityName' => 'N/A',
'organizationName' => 'Snipe-IT',
'commonName' => 'Snipe-IT',
];
$pkey = openssl_pkey_new([
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
$csr = openssl_csr_new($dn, $pkey, ['digest_alg' => 'sha256']);
if ($csr) {
$x509 = openssl_csr_sign($csr, null, $pkey, 3650, ['digest_alg' => 'sha256']);
openssl_x509_export($x509, $x509cert);
openssl_pkey_export($pkey, $privateKey);
$errors = [];
while (($error = openssl_error_string() !== false)) {
$errors[] = $error;
}
if (! (empty($x509cert) && empty($privateKey))) {
$this->merge([
'saml_sp_x509cert' => $x509cert,
'saml_sp_privatekey' => $privateKey,
]);
}
} else {
$validator->errors()->add('saml_integration', 'openssl.cnf is missing/invalid');
}
}
if ($custom_x509certNew) {
$x509New = openssl_x509_read($custom_x509certNew);
openssl_x509_export($x509New, $x509certNew);
while (($error = openssl_error_string() !== false)) {
$errors[] = $error;
}
if (! empty($x509certNew)) {
$this->merge([
'saml_sp_x509certNew' => $x509certNew,
]);
}
} else {
$this->merge([
'saml_sp_x509certNew' => '',
]);
}
});
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Requests;
class SetupUserRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'site_name' => 'required|string|min:1',
'first_name' => 'required|string|min:1',
'last_name' => 'required|string|min:1',
'username' => 'required|string|min:2|unique:users,username,NULL,deleted_at',
'email' => 'email|unique:users,email',
'password' => 'required|min:8|confirmed',
'email_domain' => 'required|min:4',
];
}
public function response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\Http\Requests;
use App\Models\Asset;
use App\Models\Company;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Support\Facades\Gate;
class StoreAssetRequest extends ImageUploadRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return Gate::allows('create', new Asset);
}
public function prepareForValidation(): void
{
// Guard against users passing in an array for company_id instead of an integer.
// If the company_id is not an integer then we simply use what was
// provided to be caught by model level validation later.
$idForCurrentUser = is_int($this->company_id)
? Company::getIdForCurrentUser($this->company_id)
: $this->company_id;
$this->parseLastAuditDate();
$this->merge([
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
'company_id' => $idForCurrentUser,
'assigned_to' => $assigned_to ?? null,
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = array_merge(
(new Asset)->getRules(),
parent::rules(),
);
return $rules;
}
private function parseLastAuditDate(): void
{
if ($this->input('last_audit_date')) {
try {
$lastAuditDate = Carbon::parse($this->input('last_audit_date'));
$this->merge([
'last_audit_date' => $lastAuditDate->startOfDay()->format('Y-m-d H:i:s'),
]);
} catch (InvalidFormatException $e) {
// we don't need to do anything here...
// we'll keep the provided date in an
// invalid format so validation picks it up later
}
}
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace App\Http\Requests;
use enshrined\svgSanitize\Sanitizer;
use Illuminate\Support\Facades\Storage;
class UploadFileRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
return [
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
];
}
/**
* Sanitizes (if needed) and Saves a file to the appropriate location
* Returns the 'short' (storage-relative) filename
*
* TODO - this has a lot of similarities to UploadImageRequest's handleImage; is there
* a way to merge them or extend one into the other?
*/
public function handleFile(string $dirname, string $name_prefix, $file): string
{
$extension = $file->getClientOriginalExtension();
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
\Log::debug("Your filetype IS: ".$file->getMimeType());
// Check for SVG and sanitize it
if ($file->getMimeType() === 'image/svg+xml') {
\Log::debug('This is an SVG');
\Log::debug($file_name);
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
Storage::put($dirname.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug('Upload no workie :( ');
\Log::debug($e);
}
} else {
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
\Log::debug("Here are the '$put_results' (should be 0 or 1 or true or false or something?)");
}
return $file_name;
}
}