3
0
mirror of https://github.com/snipe/snipe-it.git synced 2025-10-29 11:21:21 +00:00

Merge remote-tracking branch 'origin/develop'

This commit is contained in:
snipe 2025-07-16 16:54:30 +01:00
commit fe15dacb1f
15 changed files with 49 additions and 99 deletions

View File

@ -168,6 +168,7 @@ AWS_DEFAULT_REGION=null
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC

View File

@ -174,6 +174,7 @@ LOGIN_AUTOCOMPLETE=false
RESET_PASSWORD_LINK_EXPIRES=15
PASSWORD_CONFIRM_TIMEOUT=10800
PASSWORD_RESET_MAX_ATTEMPTS_PER_MIN=50
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC

View File

@ -13,7 +13,6 @@ use App\Models\Company;
use App\Models\Group;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
@ -142,18 +141,6 @@ class UsersController extends Controller
$user->groups()->sync([]);
}
if (($request->input('email_user') == 1) && ($request->filled('email'))) {
// Send the credentials through email
$data = [];
$data['email'] = e($request->input('email'));
$data['username'] = e($request->input('username'));
$data['first_name'] = e($request->input('first_name'));
$data['last_name'] = e($request->input('last_name'));
$data['password'] = e($request->input('password'));
$user->notify(new WelcomeNotification($data));
}
return Helper::getRedirectOption($request, $user->id, 'Users')
->with('success', trans('admin/users/message.success.create'));
}

View File

@ -133,7 +133,7 @@ abstract class Importer
} else {
$this->csv = Reader::createFromString($file);
}
$this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40);
$this->tempPassword = '*** NO PASSWORD - IMPORTED VIA CSV ***';
}
// Cached Values for import lookups

View File

@ -8,6 +8,7 @@ use App\Models\Setting;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Password;
/**
* This is ONLY used for the User Import. When we are importing users
@ -110,7 +111,7 @@ class UserImporter extends ItemImporter
// This needs to be applied after the update logic, otherwise we'll overwrite user passwords
// Issue #5408
$this->item['password'] = bcrypt($this->tempPassword);
$this->item['password'] = $this->tempPassword;
$this->log('No matching user, creating one');
$user = new User();
@ -121,17 +122,17 @@ class UserImporter extends ItemImporter
$this->log('User '.$this->item['name'].' was created');
if (($user->email) && ($user->activated == '1')) {
$data = [
'email' => $user->email,
'username' => $user->username,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'password' => $this->tempPassword,
];
if ($this->send_welcome) {
$user->notify(new WelcomeNotification($data));
try {
$user->notify(new WelcomeNotification($user));
} catch (\Exception $e) {
Log::warning('Could not send welcome notification for user: ' . $e->getMessage());
}
}
}
$user = null;
$this->item = null;
@ -140,7 +141,6 @@ class UserImporter extends ItemImporter
}
$this->logError($user, 'User');
return;
}
/**

View File

@ -5,26 +5,23 @@ namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Password;
use App\Models\User;
class WelcomeNotification extends Notification
{
use Queueable;
private $_data = [];
public $expire_date;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(array $content)
public function __construct(public User $user)
{
$this->_data['email'] = htmlspecialchars_decode($content['email']);
$this->_data['first_name'] = htmlspecialchars_decode($content['first_name']);
$this->_data['last_name'] = htmlspecialchars_decode($content['last_name']);
$this->_data['username'] = htmlspecialchars_decode($content['username']);
$this->_data['password'] = htmlspecialchars_decode($content['password']);
$this->_data['url'] = config('app.url');
$this->user->token = Password::broker('invites')->createToken($user);
$this->user->expire_date = now()->addMinutes((int) config('auth.passwords.invites.expire', 2880))->format('F j, Y, g:i a');
}
/**
@ -44,8 +41,9 @@ class WelcomeNotification extends Notification
*/
public function toMail()
{
return (new MailMessage())
->subject(trans('mail.welcome', ['name' => $this->_data['first_name'].' '.$this->_data['last_name']]))
->markdown('notifications.Welcome', $this->_data);
->subject(trans('mail.welcome', ['name' => $this->user->first_name.' '.$this->user->last_name]))
->markdown('notifications.Welcome', $this->user->toArray());
}
}

View File

@ -104,6 +104,16 @@ return [
]
],
'invites' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => env('INVITE_PASSWORD_LINK_EXPIRES', 2880),
'throttle' => [
'max_attempts' => env('LOGIN_MAX_ATTEMPTS', 5),
'lockout_duration' => env('LOGIN_LOCKOUT_DURATION', 60),
]
],
],
/*

View File

@ -51,8 +51,6 @@ class AssetModelSeeder extends Seeder
$del_files = Storage::files($dst);
foreach ($del_files as $del_file) { // iterate files
$file_to_delete = str_replace($src, '', $del_file);
Log::debug('Deleting: '.$file_to_delete);
try {
Storage::disk('public')->delete($dst.$del_file);
} catch (\Exception $e) {
@ -63,7 +61,6 @@ class AssetModelSeeder extends Seeder
$add_files = glob($src.'/*.*');
foreach ($add_files as $add_file) {
$file_to_copy = str_replace($src, '', $add_file);
Log::debug('Copying: '.$file_to_copy);
try {
Storage::disk('public')->put($dst.$file_to_copy, file_get_contents($src.$file_to_copy));
} catch (\Exception $e) {

View File

@ -10,9 +10,11 @@ return [
'forgot_password' => 'I forgot my password',
'ldap_reset_password' => 'Please click here to reset your LDAP password',
'remember_me' => 'Remember Me',
'username_help_top' => 'Enter your <strong>username</strong> to be emailed a password reset link.',
'username_help_top' => 'Enter your <strong>username</strong> to be emailed a password reset link.',
'username_help_bottom' => 'Your username and email address <em>may</em> be the same, but may not be, depending on your configuration. If you cannot remember your username, contact your administrator. <br><br><strong>Usernames without an associated email address will not be emailed a password reset link.</strong> ',
'google_login' => 'Login with Google Workspace',
'google_login_failed' => 'Google Login failed, please try again.',
'invite_password_expires' => 'This password reset link will expire on :expire_date. You can use the manual password reset link to receive a new reset token by clicking here',
];

View File

@ -596,6 +596,7 @@ return [
'version' => 'Version',
'build' => 'build',
'footer_credit' => '<a target="_blank" href="https://snipeitapp.com" rel="noopener">Snipe-IT</a> is open source software, made with <i class="fa fa-heart" aria-hidden="true" style="color: #a94442; font-size: 10px" /></i><span class="sr-only">love</span> by <a href="https://bsky.app/profile/snipeitapp.com" rel="noopener">@snipeitapp.com</a>.',
'set_password' => 'Set a Password',
// Add form placeholders here
'placeholders' => [

View File

@ -33,7 +33,7 @@ return [
'send_pdf_copy' => 'Send a copy of this acceptance to my email address',
'accessory_name' => 'Accessory Name',
'additional_notes' => 'Additional Notes',
'admin_has_created' => 'An administrator has created an account for you on the :web website.',
'admin_has_created' => 'An administrator has created an account for you on the :web website. Please find your username below, and click on the link to set a password.',
'asset' => 'Asset',
'asset_name' => 'Asset Name',
'asset_requested' => 'Asset requested',

View File

@ -33,7 +33,7 @@
<table class="table table-striped table-bordered" id="errors-table">
<thead>
<th>{{ trans('general.item') }}</th>
<th>Field</th>
<th>{{ trans('admin/custom_fields/general.field') }}</th>
<th>{{ trans('general.error') }}</th>
</thead>
<tbody>
@ -191,7 +191,7 @@
@if (($typeOfImport != 'location' && $typeOfImport!= 'assetModel' && $typeOfImport!= 'component' && $typeOfImport!= 'supplier') && $typeOfImport!= 'manufacturer' && $typeOfImport!= 'category' && ($typeOfImport!=''))
@if ($typeOfImport === 'user')
<label class="form-control">
<input type="checkbox" name="send_welcome" data-livewire-component="{{ $this->getId() }}" wire:model.live="send_welcome">
{{ trans('general.send_welcome_email_to_users') }}

View File

@ -3,13 +3,16 @@
{{ trans('mail.admin_has_created', ['web' => $snipeSettings->site_name]) }}
{{ trans('mail.login') }}: {{ $username }} <br>
{{ trans('mail.password') }}: {{ $password }}
<strong>{{ trans('mail.username') }}: </strong> {{ $username }}<br>
@component('mail::button', ['url' => $url])
Go To {{$snipeSettings->site_name}}
@component('mail::button',
['url' => url(route('password.reset', ['token' => $token, 'email' => $email]))])
{{ trans('general.set_password') }}
@endcomponent
<p>{{ trans('auth/general.invite_password_expires', ['expire_date' => $expire_date]) }}: <a href="{{ url(route('password.request')) }}">{{ url(route('password.request')) }}</a>
</p>
{{ trans('mail.best_regards') }} <br>
@if ($snipeSettings->show_url_in_emails=='1')
<p><a href="{{ config('app.url') }}">{{ $snipeSettings->site_name }}</a></p>

View File

@ -250,26 +250,7 @@
{!! $errors->first('email', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
<!-- Email user -->
@if (!$user->id)
<div class="form-group" id="email_user_row">
<div class="col-md-8 col-md-offset-3">
<label class="form-control form-control--disabled">
<input type="checkbox" name="email_user" value="1" id="email_user_checkbox" @checked(old('email_user')) aria-label="email_user">
{{ trans('admin/users/general.email_user_creds_on_create') }}
</label>
<p class="help-block"> {{ trans('admin/users/general.send_email_help') }}</p>
</div>
</div> <!--/form-group-->
@endif
@include ('partials.forms.edit.image-upload', ['fieldname' => 'avatar', 'image_path' => app('users_upload_path')])
@ -617,39 +598,8 @@
$(document).ready(function() {
// If the "user can login" check box is checked, show them the ability to email the user credentials
$("#activated").change(function() {
if (this.checked) {
$("#email_user_row").show();
} else {
$("#email_user_row").hide();
}
});
// Set some defaults
$('#email_user_checkbox').prop("disabled", true);
$('#email_user_checkbox').prop("checked", false);
$("#email_user_checkbox").removeAttr('checked');
// If the email address is longer than 5 characters, enable the "send email" checkbox
$('#email').on('keyup',function(){
//event.preventDefault();
@if (!config('app.lock_passwords'))
if (this.value.length > 5){
$('#email_user_checkbox').prop("disabled", false);
$("#email_user_checkbox").parent().removeClass("form-control--disabled");
} else {
$('#email_user_checkbox').prop("disabled", true);
$('#email_user_checkbox').prop("checked", false);
$("#email_user_checkbox").parent().addClass("form-control--disabled");
}
@endif
});
// Check/Uncheck all radio buttons in the group
$('tr.header-row input:radio').change(function() {

View File

@ -82,7 +82,7 @@ class ImportUsersTest extends ImportDataTestCase implements TestsPermissionsRequ
$this->assertEquals($row['location'], $newUser->location->name);
$this->assertEquals($row['phoneNumber'], $newUser->phone);
$this->assertEquals($row['position'], $newUser->jobtitle);
$this->assertTrue(Hash::isHashed($newUser->password));
$this->assertFalse(Hash::isHashed($newUser->password));
$this->assertEquals('', $newUser->website);
$this->assertEquals('', $newUser->country);
$this->assertEquals('', $newUser->address);
@ -298,7 +298,7 @@ class ImportUsersTest extends ImportDataTestCase implements TestsPermissionsRequ
$this->assertEquals($row['username'], $newUser->company->name);
$this->assertEquals($row['firstName'], $newUser->location->name);
$this->assertEquals($row['employeeNumber'], $newUser->phone);
$this->assertTrue(Hash::isHashed($newUser->password));
$this->assertFalse(Hash::isHashed($newUser->password));
$this->assertEquals('', $newUser->website);
$this->assertEquals('', $newUser->country);
$this->assertEquals('', $newUser->address);