Add Timezone setting/pref & Update tests accordingly

This commit is contained in:
Bubka 2024-04-25 16:56:35 +02:00
parent 99bf9d7d80
commit 8734e9c807
12 changed files with 670 additions and 44 deletions

View File

@ -9,6 +9,14 @@ APP_NAME=2FAuth
APP_ENV=local
# The timezone for your application, which is used to record dates and times to database. This global setting can be
# overridden by users via in-app settings for a personalised dates and times display.
# If this setting is changed while the application is already running, existing records in the database won't be updated.
APP_TIMEZONE=UTC
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false

4
Dockerfile vendored
View File

@ -114,6 +114,10 @@ ENV \
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=local \
# The timezone for your application, which is used to record dates and times to database. This global setting can be
# overridden by users via in-app settings for a personalised dates and times display.
# If this setting is changed while the application is already running, existing records in the database won't be updated.
APP_TIMEZONE=UTC \
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false \
# This should be your email address

View File

@ -51,6 +51,8 @@ public function __construct($resource)
*/
public function toArray($request)
{
$tz = $request->user()->preferences['timezone'];
return [
'id' => $this->id,
'ip_address' => $this->ip_address,
@ -59,10 +61,10 @@ public function toArray($request)
'platform' => $this->agent->platform(),
'device' => $this->agent->deviceType(),
'login_at' => $this->login_at
? Carbon::parse($this->login_at)->toDayDateTimeString()
? Carbon::parse($this->login_at,)->tz($tz)->toDayDateTimeString()
: null,
'logout_at' => $this->logout_at
? Carbon::parse($this->logout_at)->toDayDateTimeString()
? Carbon::parse($this->logout_at)->tz($tz)->toDayDateTimeString()
: null,
'login_successful' => $this->login_successful,
'duration' => $this->logout_at

View File

@ -85,12 +85,14 @@ protected function tokenExpired($createdAt)
*/
public function toArray($request)
{
$tz = $request->user()?->preferences['timezone'];
return array_merge(
parent::toArray($request),
[
'twofaccounts_count' => is_null($this->twofaccounts_count) ? 0 : $this->twofaccounts_count,
'last_seen_at' => Carbon::parse($this->last_seen_at)->locale(App::getLocale())->diffForHumans(),
'created_at' => Carbon::parse($this->created_at)->locale(App::getLocale())->diffForHumans(),
'last_seen_at' => Carbon::parse($this->last_seen_at)->tz($tz)->locale(App::getLocale())->diffForHumans(),
'created_at' => Carbon::parse($this->created_at)->tz($tz)->locale(App::getLocale())->diffForHumans(),
]
);
}

View File

@ -124,6 +124,7 @@
'getOtpOnRequest' => true,
'notifyOnNewAuthDevice' => true,
'notifyOnFailedLogin' => true,
'timezone' => env('APP_TIMEZONE', 'UTC'),
],
];

View File

@ -72,7 +72,7 @@
|
*/
'timezone' => 'UTC',
'timezone' => env('APP_TIMEZONE', 'UTC'),
/*
|--------------------------------------------------------------------------

View File

@ -42,6 +42,20 @@ public function failedLogin()
});
}
/**
* Indicate that the model has a logout date too.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\AuthLog>
*/
public function withLogout()
{
return $this->state(function (array $attributes) {
return [
'logout_at' => now(),
];
});
}
/**
* Indicate that the model has a logout date only, without login date.
*

View File

@ -13,6 +13,10 @@ services:
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
- APP_ENV=local
# The timezone for your application, which is used to record dates and times to database. This global setting can be
# overridden by users via in-app settings for a personalised dates and times display.
# If this setting is changed while the application is already running, existing records in the database won't be updated
- APP_TIMEZONE=UTC
# Set to true if you want to see debug information in error screens.
- APP_DEBUG=false
# This should be your email address

View File

@ -4,6 +4,7 @@
import { useUserStore } from '@/stores/user'
import { useGroups } from '@/stores/groups'
import { useNotifyStore } from '@/stores/notify'
import { timezones } from './timezones'
const $2fauth = inject('2fauth')
const user = useUserStore()
@ -123,6 +124,8 @@
<FontAwesomeIcon :icon="['fas', 'external-link-alt']" />
</a>
</div>
<!-- timezone -->
<FormSelect v-model="user.preferences.timezone" @update:model-value="val => savePreference('timezone', val)" :options="timezones" fieldName="timezone" label="settings.forms.timezone.label" help="settings.forms.timezone.help" />
<!-- display mode -->
<FormToggle v-model="user.preferences.displayMode" @update:model-value="val => savePreference('displayMode', val)" :choices="layouts" fieldName="displayMode" label="settings.forms.display_mode.label" help="settings.forms.display_mode.help"/>
<!-- theme -->

537
resources/js/views/settings/timezones.js vendored Normal file
View File

@ -0,0 +1,537 @@
export let timezones = [
{ text: 'Africa/Abidjan', value: 'Africa/Abidjan' },
{ text: 'Africa/Accra', value: 'Africa/Accra' },
{ text: 'Africa/Addis_Ababa', value: 'Africa/Addis_Ababa' },
{ text: 'Africa/Algiers', value: 'Africa/Algiers' },
{ text: 'Africa/Asmara', value: 'Africa/Asmara' },
{ text: 'Africa/Asmera', value: 'Africa/Asmera' },
{ text: 'Africa/Bamako', value: 'Africa/Bamako' },
{ text: 'Africa/Bangui', value: 'Africa/Bangui' },
{ text: 'Africa/Banjul', value: 'Africa/Banjul' },
{ text: 'Africa/Bissau', value: 'Africa/Bissau' },
{ text: 'Africa/Blantyre', value: 'Africa/Blantyre' },
{ text: 'Africa/Brazzaville', value: 'Africa/Brazzaville' },
{ text: 'Africa/Bujumbura', value: 'Africa/Bujumbura' },
{ text: 'Africa/Cairo', value: 'Africa/Cairo' },
{ text: 'Africa/Casablanca', value: 'Africa/Casablanca' },
{ text: 'Africa/Ceuta', value: 'Africa/Ceuta' },
{ text: 'Africa/Conakry', value: 'Africa/Conakry' },
{ text: 'Africa/Dakar', value: 'Africa/Dakar' },
{ text: 'Africa/Dar_es_Salaam', value: 'Africa/Dar_es_Salaam' },
{ text: 'Africa/Djibouti', value: 'Africa/Djibouti' },
{ text: 'Africa/Douala', value: 'Africa/Douala' },
{ text: 'Africa/El_Aaiun', value: 'Africa/El_Aaiun' },
{ text: 'Africa/Freetown', value: 'Africa/Freetown' },
{ text: 'Africa/Gaborone', value: 'Africa/Gaborone' },
{ text: 'Africa/Harare', value: 'Africa/Harare' },
{ text: 'Africa/Johannesburg', value: 'Africa/Johannesburg' },
{ text: 'Africa/Juba', value: 'Africa/Juba' },
{ text: 'Africa/Kampala', value: 'Africa/Kampala' },
{ text: 'Africa/Khartoum', value: 'Africa/Khartoum' },
{ text: 'Africa/Kigali', value: 'Africa/Kigali' },
{ text: 'Africa/Kinshasa', value: 'Africa/Kinshasa' },
{ text: 'Africa/Lagos', value: 'Africa/Lagos' },
{ text: 'Africa/Libreville', value: 'Africa/Libreville' },
{ text: 'Africa/Lome', value: 'Africa/Lome' },
{ text: 'Africa/Luanda', value: 'Africa/Luanda' },
{ text: 'Africa/Lubumbashi', value: 'Africa/Lubumbashi' },
{ text: 'Africa/Lusaka', value: 'Africa/Lusaka' },
{ text: 'Africa/Malabo', value: 'Africa/Malabo' },
{ text: 'Africa/Maputo', value: 'Africa/Maputo' },
{ text: 'Africa/Maseru', value: 'Africa/Maseru' },
{ text: 'Africa/Mbabane', value: 'Africa/Mbabane' },
{ text: 'Africa/Mogadishu', value: 'Africa/Mogadishu' },
{ text: 'Africa/Monrovia', value: 'Africa/Monrovia' },
{ text: 'Africa/Nairobi', value: 'Africa/Nairobi' },
{ text: 'Africa/Ndjamena', value: 'Africa/Ndjamena' },
{ text: 'Africa/Niamey', value: 'Africa/Niamey' },
{ text: 'Africa/Nouakchott', value: 'Africa/Nouakchott' },
{ text: 'Africa/Ouagadougou', value: 'Africa/Ouagadougou' },
{ text: 'Africa/Porto-Novo', value: 'Africa/Porto-Novo' },
{ text: 'Africa/Sao_Tome', value: 'Africa/Sao_Tome' },
{ text: 'Africa/Timbuktu', value: 'Africa/Timbuktu' },
{ text: 'Africa/Tripoli', value: 'Africa/Tripoli' },
{ text: 'Africa/Tunis', value: 'Africa/Tunis' },
{ text: 'Africa/Windhoek', value: 'Africa/Windhoek' },
{ text: 'America/Adak', value: 'America/Adak' },
{ text: 'America/Anchorage', value: 'America/Anchorage' },
{ text: 'America/Anguilla', value: 'America/Anguilla' },
{ text: 'America/Antigua', value: 'America/Antigua' },
{ text: 'America/Araguaina', value: 'America/Araguaina' },
{ text: 'America/Argentina/Buenos_Aires', value: 'America/Argentina/Buenos_Aires' },
{ text: 'America/Argentina/Catamarca', value: 'America/Argentina/Catamarca' },
{ text: 'America/Argentina/ComodRivadavia', value: 'America/Argentina/ComodRivadavia' },
{ text: 'America/Argentina/Cordoba', value: 'America/Argentina/Cordoba' },
{ text: 'America/Argentina/Jujuy', value: 'America/Argentina/Jujuy' },
{ text: 'America/Argentina/La_Rioja', value: 'America/Argentina/La_Rioja' },
{ text: 'America/Argentina/Mendoza', value: 'America/Argentina/Mendoza' },
{ text: 'America/Argentina/Rio_Gallegos', value: 'America/Argentina/Rio_Gallegos' },
{ text: 'America/Argentina/Salta', value: 'America/Argentina/Salta' },
{ text: 'America/Argentina/San_Juan', value: 'America/Argentina/San_Juan' },
{ text: 'America/Argentina/San_Luis', value: 'America/Argentina/San_Luis' },
{ text: 'America/Argentina/Tucuman', value: 'America/Argentina/Tucuman' },
{ text: 'America/Argentina/Ushuaia', value: 'America/Argentina/Ushuaia' },
{ text: 'America/Aruba', value: 'America/Aruba' },
{ text: 'America/Asuncion', value: 'America/Asuncion' },
{ text: 'America/Atikokan', value: 'America/Atikokan' },
{ text: 'America/Atka', value: 'America/Atka' },
{ text: 'America/Bahia', value: 'America/Bahia' },
{ text: 'America/Bahia_Banderas', value: 'America/Bahia_Banderas' },
{ text: 'America/Barbados', value: 'America/Barbados' },
{ text: 'America/Belem', value: 'America/Belem' },
{ text: 'America/Belize', value: 'America/Belize' },
{ text: 'America/Blanc-Sablon', value: 'America/Blanc-Sablon' },
{ text: 'America/Boa_Vista', value: 'America/Boa_Vista' },
{ text: 'America/Bogota', value: 'America/Bogota' },
{ text: 'America/Boise', value: 'America/Boise' },
{ text: 'America/Buenos_Aires', value: 'America/Buenos_Aires' },
{ text: 'America/Cambridge_Bay', value: 'America/Cambridge_Bay' },
{ text: 'America/Campo_Grande', value: 'America/Campo_Grande' },
{ text: 'America/Cancun', value: 'America/Cancun' },
{ text: 'America/Caracas', value: 'America/Caracas' },
{ text: 'America/Catamarca', value: 'America/Catamarca' },
{ text: 'America/Cayenne', value: 'America/Cayenne' },
{ text: 'America/Cayman', value: 'America/Cayman' },
{ text: 'America/Chicago', value: 'America/Chicago' },
{ text: 'America/Chihuahua', value: 'America/Chihuahua' },
{ text: 'America/Ciudad_Juarez', value: 'America/Ciudad_Juarez' },
{ text: 'America/Coral_Harbour', value: 'America/Coral_Harbour' },
{ text: 'America/Cordoba', value: 'America/Cordoba' },
{ text: 'America/Costa_Rica', value: 'America/Costa_Rica' },
{ text: 'America/Creston', value: 'America/Creston' },
{ text: 'America/Cuiaba', value: 'America/Cuiaba' },
{ text: 'America/Curacao', value: 'America/Curacao' },
{ text: 'America/Danmarkshavn', value: 'America/Danmarkshavn' },
{ text: 'America/Dawson', value: 'America/Dawson' },
{ text: 'America/Dawson_Creek', value: 'America/Dawson_Creek' },
{ text: 'America/Denver', value: 'America/Denver' },
{ text: 'America/Detroit', value: 'America/Detroit' },
{ text: 'America/Dominica', value: 'America/Dominica' },
{ text: 'America/Edmonton', value: 'America/Edmonton' },
{ text: 'America/Eirunepe', value: 'America/Eirunepe' },
{ text: 'America/El_Salvador', value: 'America/El_Salvador' },
{ text: 'America/Ensenada', value: 'America/Ensenada' },
{ text: 'America/Fort_Nelson', value: 'America/Fort_Nelson' },
{ text: 'America/Fort_Wayne', value: 'America/Fort_Wayne' },
{ text: 'America/Fortaleza', value: 'America/Fortaleza' },
{ text: 'America/Glace_Bay', value: 'America/Glace_Bay' },
{ text: 'America/Godthab', value: 'America/Godthab' },
{ text: 'America/Goose_Bay', value: 'America/Goose_Bay' },
{ text: 'America/Grand_Turk', value: 'America/Grand_Turk' },
{ text: 'America/Grenada', value: 'America/Grenada' },
{ text: 'America/Guadeloupe', value: 'America/Guadeloupe' },
{ text: 'America/Guatemala', value: 'America/Guatemala' },
{ text: 'America/Guayaquil', value: 'America/Guayaquil' },
{ text: 'America/Guyana', value: 'America/Guyana' },
{ text: 'America/Halifax', value: 'America/Halifax' },
{ text: 'America/Havana', value: 'America/Havana' },
{ text: 'America/Hermosillo', value: 'America/Hermosillo' },
{ text: 'America/Indiana/Indianapolis', value: 'America/Indiana/Indianapolis' },
{ text: 'America/Indiana/Knox', value: 'America/Indiana/Knox' },
{ text: 'America/Indiana/Marengo', value: 'America/Indiana/Marengo' },
{ text: 'America/Indiana/Petersburg', value: 'America/Indiana/Petersburg' },
{ text: 'America/Indiana/Tell_City', value: 'America/Indiana/Tell_City' },
{ text: 'America/Indiana/Vevay', value: 'America/Indiana/Vevay' },
{ text: 'America/Indiana/Vincennes', value: 'America/Indiana/Vincennes' },
{ text: 'America/Indiana/Winamac', value: 'America/Indiana/Winamac' },
{ text: 'America/Indianapolis', value: 'America/Indianapolis' },
{ text: 'America/Inuvik', value: 'America/Inuvik' },
{ text: 'America/Iqaluit', value: 'America/Iqaluit' },
{ text: 'America/Jamaica', value: 'America/Jamaica' },
{ text: 'America/Jujuy', value: 'America/Jujuy' },
{ text: 'America/Juneau', value: 'America/Juneau' },
{ text: 'America/Kentucky/Louisville', value: 'America/Kentucky/Louisville' },
{ text: 'America/Kentucky/Monticello', value: 'America/Kentucky/Monticello' },
{ text: 'America/Knox_IN', value: 'America/Knox_IN' },
{ text: 'America/Kralendijk', value: 'America/Kralendijk' },
{ text: 'America/La_Paz', value: 'America/La_Paz' },
{ text: 'America/Lima', value: 'America/Lima' },
{ text: 'America/Los_Angeles', value: 'America/Los_Angeles' },
{ text: 'America/Louisville', value: 'America/Louisville' },
{ text: 'America/Lower_Princes', value: 'America/Lower_Princes' },
{ text: 'America/Maceio', value: 'America/Maceio' },
{ text: 'America/Managua', value: 'America/Managua' },
{ text: 'America/Manaus', value: 'America/Manaus' },
{ text: 'America/Marigot', value: 'America/Marigot' },
{ text: 'America/Martinique', value: 'America/Martinique' },
{ text: 'America/Matamoros', value: 'America/Matamoros' },
{ text: 'America/Mazatlan', value: 'America/Mazatlan' },
{ text: 'America/Mendoza', value: 'America/Mendoza' },
{ text: 'America/Menominee', value: 'America/Menominee' },
{ text: 'America/Merida', value: 'America/Merida' },
{ text: 'America/Metlakatla', value: 'America/Metlakatla' },
{ text: 'America/Mexico_City', value: 'America/Mexico_City' },
{ text: 'America/Miquelon', value: 'America/Miquelon' },
{ text: 'America/Moncton', value: 'America/Moncton' },
{ text: 'America/Monterrey', value: 'America/Monterrey' },
{ text: 'America/Montevideo', value: 'America/Montevideo' },
{ text: 'America/Montreal', value: 'America/Montreal' },
{ text: 'America/Montserrat', value: 'America/Montserrat' },
{ text: 'America/Nassau', value: 'America/Nassau' },
{ text: 'America/New_York', value: 'America/New_York' },
{ text: 'America/Nipigon', value: 'America/Nipigon' },
{ text: 'America/Nome', value: 'America/Nome' },
{ text: 'America/Noronha', value: 'America/Noronha' },
{ text: 'America/North_Dakota/Beulah', value: 'America/North_Dakota/Beulah' },
{ text: 'America/North_Dakota/Center', value: 'America/North_Dakota/Center' },
{ text: 'America/North_Dakota/New_Salem', value: 'America/North_Dakota/New_Salem' },
{ text: 'America/Nuuk', value: 'America/Nuuk' },
{ text: 'America/Ojinaga', value: 'America/Ojinaga' },
{ text: 'America/Panama', value: 'America/Panama' },
{ text: 'America/Pangnirtung', value: 'America/Pangnirtung' },
{ text: 'America/Paramaribo', value: 'America/Paramaribo' },
{ text: 'America/Phoenix', value: 'America/Phoenix' },
{ text: 'America/Port-au-Prince', value: 'America/Port-au-Prince' },
{ text: 'America/Port_of_Spain', value: 'America/Port_of_Spain' },
{ text: 'America/Porto_Acre', value: 'America/Porto_Acre' },
{ text: 'America/Porto_Velho', value: 'America/Porto_Velho' },
{ text: 'America/Puerto_Rico', value: 'America/Puerto_Rico' },
{ text: 'America/Punta_Arenas', value: 'America/Punta_Arenas' },
{ text: 'America/Rainy_River', value: 'America/Rainy_River' },
{ text: 'America/Rankin_Inlet', value: 'America/Rankin_Inlet' },
{ text: 'America/Recife', value: 'America/Recife' },
{ text: 'America/Regina', value: 'America/Regina' },
{ text: 'America/Resolute', value: 'America/Resolute' },
{ text: 'America/Rio_Branco', value: 'America/Rio_Branco' },
{ text: 'America/Rosario', value: 'America/Rosario' },
{ text: 'America/Santa_Isabel', value: 'America/Santa_Isabel' },
{ text: 'America/Santarem', value: 'America/Santarem' },
{ text: 'America/Santiago', value: 'America/Santiago' },
{ text: 'America/Santo_Domingo', value: 'America/Santo_Domingo' },
{ text: 'America/Sao_Paulo', value: 'America/Sao_Paulo' },
{ text: 'America/Scoresbysund', value: 'America/Scoresbysund' },
{ text: 'America/Shiprock', value: 'America/Shiprock' },
{ text: 'America/Sitka', value: 'America/Sitka' },
{ text: 'America/St_Barthelemy', value: 'America/St_Barthelemy' },
{ text: 'America/St_Johns', value: 'America/St_Johns' },
{ text: 'America/St_Kitts', value: 'America/St_Kitts' },
{ text: 'America/St_Lucia', value: 'America/St_Lucia' },
{ text: 'America/St_Thomas', value: 'America/St_Thomas' },
{ text: 'America/St_Vincent', value: 'America/St_Vincent' },
{ text: 'America/Swift_Current', value: 'America/Swift_Current' },
{ text: 'America/Tegucigalpa', value: 'America/Tegucigalpa' },
{ text: 'America/Thule', value: 'America/Thule' },
{ text: 'America/Thunder_Bay', value: 'America/Thunder_Bay' },
{ text: 'America/Tijuana', value: 'America/Tijuana' },
{ text: 'America/Toronto', value: 'America/Toronto' },
{ text: 'America/Tortola', value: 'America/Tortola' },
{ text: 'America/Vancouver', value: 'America/Vancouver' },
{ text: 'America/Virgin', value: 'America/Virgin' },
{ text: 'America/Whitehorse', value: 'America/Whitehorse' },
{ text: 'America/Winnipeg', value: 'America/Winnipeg' },
{ text: 'America/Yakutat', value: 'America/Yakutat' },
{ text: 'America/Yellowknife', value: 'America/Yellowknife' },
{ text: 'Antarctica/Casey', value: 'Antarctica/Casey' },
{ text: 'Antarctica/Davis', value: 'Antarctica/Davis' },
{ text: 'Antarctica/DumontDUrville', value: 'Antarctica/DumontDUrville' },
{ text: 'Antarctica/Macquarie', value: 'Antarctica/Macquarie' },
{ text: 'Antarctica/Mawson', value: 'Antarctica/Mawson' },
{ text: 'Antarctica/McMurdo', value: 'Antarctica/McMurdo' },
{ text: 'Antarctica/Palmer', value: 'Antarctica/Palmer' },
{ text: 'Antarctica/Rothera', value: 'Antarctica/Rothera' },
{ text: 'Antarctica/South_Pole', value: 'Antarctica/South_Pole' },
{ text: 'Antarctica/Syowa', value: 'Antarctica/Syowa' },
{ text: 'Antarctica/Troll', value: 'Antarctica/Troll' },
{ text: 'Antarctica/Vostok', value: 'Antarctica/Vostok' },
{ text: 'Arctic/Longyearbyen', value: 'Arctic/Longyearbyen' },
{ text: 'Asia/Aden', value: 'Asia/Aden' },
{ text: 'Asia/Almaty', value: 'Asia/Almaty' },
{ text: 'Asia/Amman', value: 'Asia/Amman' },
{ text: 'Asia/Anadyr', value: 'Asia/Anadyr' },
{ text: 'Asia/Aqtau', value: 'Asia/Aqtau' },
{ text: 'Asia/Aqtobe', value: 'Asia/Aqtobe' },
{ text: 'Asia/Ashgabat', value: 'Asia/Ashgabat' },
{ text: 'Asia/Ashkhabad', value: 'Asia/Ashkhabad' },
{ text: 'Asia/Atyrau', value: 'Asia/Atyrau' },
{ text: 'Asia/Baghdad', value: 'Asia/Baghdad' },
{ text: 'Asia/Bahrain', value: 'Asia/Bahrain' },
{ text: 'Asia/Baku', value: 'Asia/Baku' },
{ text: 'Asia/Bangkok', value: 'Asia/Bangkok' },
{ text: 'Asia/Barnaul', value: 'Asia/Barnaul' },
{ text: 'Asia/Beirut', value: 'Asia/Beirut' },
{ text: 'Asia/Bishkek', value: 'Asia/Bishkek' },
{ text: 'Asia/Brunei', value: 'Asia/Brunei' },
{ text: 'Asia/Calcutta', value: 'Asia/Calcutta' },
{ text: 'Asia/Chita', value: 'Asia/Chita' },
{ text: 'Asia/Choibalsan', value: 'Asia/Choibalsan' },
{ text: 'Asia/Chongqing', value: 'Asia/Chongqing' },
{ text: 'Asia/Chungking', value: 'Asia/Chungking' },
{ text: 'Asia/Colombo', value: 'Asia/Colombo' },
{ text: 'Asia/Dacca', value: 'Asia/Dacca' },
{ text: 'Asia/Damascus', value: 'Asia/Damascus' },
{ text: 'Asia/Dhaka', value: 'Asia/Dhaka' },
{ text: 'Asia/Dili', value: 'Asia/Dili' },
{ text: 'Asia/Dubai', value: 'Asia/Dubai' },
{ text: 'Asia/Dushanbe', value: 'Asia/Dushanbe' },
{ text: 'Asia/Famagusta', value: 'Asia/Famagusta' },
{ text: 'Asia/Gaza', value: 'Asia/Gaza' },
{ text: 'Asia/Harbin', value: 'Asia/Harbin' },
{ text: 'Asia/Hebron', value: 'Asia/Hebron' },
{ text: 'Asia/Ho_Chi_Minh', value: 'Asia/Ho_Chi_Minh' },
{ text: 'Asia/Hong_Kong', value: 'Asia/Hong_Kong' },
{ text: 'Asia/Hovd', value: 'Asia/Hovd' },
{ text: 'Asia/Irkutsk', value: 'Asia/Irkutsk' },
{ text: 'Asia/Istanbul', value: 'Asia/Istanbul' },
{ text: 'Asia/Jakarta', value: 'Asia/Jakarta' },
{ text: 'Asia/Jayapura', value: 'Asia/Jayapura' },
{ text: 'Asia/Jerusalem', value: 'Asia/Jerusalem' },
{ text: 'Asia/Kabul', value: 'Asia/Kabul' },
{ text: 'Asia/Kamchatka', value: 'Asia/Kamchatka' },
{ text: 'Asia/Karachi', value: 'Asia/Karachi' },
{ text: 'Asia/Kashgar', value: 'Asia/Kashgar' },
{ text: 'Asia/Kathmandu', value: 'Asia/Kathmandu' },
{ text: 'Asia/Katmandu', value: 'Asia/Katmandu' },
{ text: 'Asia/Khandyga', value: 'Asia/Khandyga' },
{ text: 'Asia/Kolkata', value: 'Asia/Kolkata' },
{ text: 'Asia/Krasnoyarsk', value: 'Asia/Krasnoyarsk' },
{ text: 'Asia/Kuala_Lumpur', value: 'Asia/Kuala_Lumpur' },
{ text: 'Asia/Kuching', value: 'Asia/Kuching' },
{ text: 'Asia/Kuwait', value: 'Asia/Kuwait' },
{ text: 'Asia/Macao', value: 'Asia/Macao' },
{ text: 'Asia/Macau', value: 'Asia/Macau' },
{ text: 'Asia/Magadan', value: 'Asia/Magadan' },
{ text: 'Asia/Makassar', value: 'Asia/Makassar' },
{ text: 'Asia/Manila', value: 'Asia/Manila' },
{ text: 'Asia/Muscat', value: 'Asia/Muscat' },
{ text: 'Asia/Nicosia', value: 'Asia/Nicosia' },
{ text: 'Asia/Novokuznetsk', value: 'Asia/Novokuznetsk' },
{ text: 'Asia/Novosibirsk', value: 'Asia/Novosibirsk' },
{ text: 'Asia/Omsk', value: 'Asia/Omsk' },
{ text: 'Asia/Oral', value: 'Asia/Oral' },
{ text: 'Asia/Phnom_Penh', value: 'Asia/Phnom_Penh' },
{ text: 'Asia/Pontianak', value: 'Asia/Pontianak' },
{ text: 'Asia/Pyongyang', value: 'Asia/Pyongyang' },
{ text: 'Asia/Qatar', value: 'Asia/Qatar' },
{ text: 'Asia/Qostanay', value: 'Asia/Qostanay' },
{ text: 'Asia/Qyzylorda', value: 'Asia/Qyzylorda' },
{ text: 'Asia/Rangoon', value: 'Asia/Rangoon' },
{ text: 'Asia/Riyadh', value: 'Asia/Riyadh' },
{ text: 'Asia/Saigon', value: 'Asia/Saigon' },
{ text: 'Asia/Sakhalin', value: 'Asia/Sakhalin' },
{ text: 'Asia/Samarkand', value: 'Asia/Samarkand' },
{ text: 'Asia/Seoul', value: 'Asia/Seoul' },
{ text: 'Asia/Shanghai', value: 'Asia/Shanghai' },
{ text: 'Asia/Singapore', value: 'Asia/Singapore' },
{ text: 'Asia/Srednekolymsk', value: 'Asia/Srednekolymsk' },
{ text: 'Asia/Taipei', value: 'Asia/Taipei' },
{ text: 'Asia/Tashkent', value: 'Asia/Tashkent' },
{ text: 'Asia/Tbilisi', value: 'Asia/Tbilisi' },
{ text: 'Asia/Tehran', value: 'Asia/Tehran' },
{ text: 'Asia/Tel_Aviv', value: 'Asia/Tel_Aviv' },
{ text: 'Asia/Thimbu', value: 'Asia/Thimbu' },
{ text: 'Asia/Thimphu', value: 'Asia/Thimphu' },
{ text: 'Asia/Tokyo', value: 'Asia/Tokyo' },
{ text: 'Asia/Tomsk', value: 'Asia/Tomsk' },
{ text: 'Asia/Ujung_Pandang', value: 'Asia/Ujung_Pandang' },
{ text: 'Asia/Ulaanbaatar', value: 'Asia/Ulaanbaatar' },
{ text: 'Asia/Ulan_Bator', value: 'Asia/Ulan_Bator' },
{ text: 'Asia/Urumqi', value: 'Asia/Urumqi' },
{ text: 'Asia/Ust-Nera', value: 'Asia/Ust-Nera' },
{ text: 'Asia/Vientiane', value: 'Asia/Vientiane' },
{ text: 'Asia/Vladivostok', value: 'Asia/Vladivostok' },
{ text: 'Asia/Yakutsk', value: 'Asia/Yakutsk' },
{ text: 'Asia/Yangon', value: 'Asia/Yangon' },
{ text: 'Asia/Yekaterinburg', value: 'Asia/Yekaterinburg' },
{ text: 'Asia/Yerevan', value: 'Asia/Yerevan' },
{ text: 'Atlantic/Azores', value: 'Atlantic/Azores' },
{ text: 'Atlantic/Bermuda', value: 'Atlantic/Bermuda' },
{ text: 'Atlantic/Canary', value: 'Atlantic/Canary' },
{ text: 'Atlantic/Cape_Verde', value: 'Atlantic/Cape_Verde' },
{ text: 'Atlantic/Faeroe', value: 'Atlantic/Faeroe' },
{ text: 'Atlantic/Faroe', value: 'Atlantic/Faroe' },
{ text: 'Atlantic/Jan_Mayen', value: 'Atlantic/Jan_Mayen' },
{ text: 'Atlantic/Madeira', value: 'Atlantic/Madeira' },
{ text: 'Atlantic/Reykjavik', value: 'Atlantic/Reykjavik' },
{ text: 'Atlantic/South_Georgia', value: 'Atlantic/South_Georgia' },
{ text: 'Atlantic/St_Helena', value: 'Atlantic/St_Helena' },
{ text: 'Atlantic/Stanley', value: 'Atlantic/Stanley' },
{ text: 'Australia/ACT', value: 'Australia/ACT' },
{ text: 'Australia/Adelaide', value: 'Australia/Adelaide' },
{ text: 'Australia/Brisbane', value: 'Australia/Brisbane' },
{ text: 'Australia/Broken_Hill', value: 'Australia/Broken_Hill' },
{ text: 'Australia/Canberra', value: 'Australia/Canberra' },
{ text: 'Australia/Currie', value: 'Australia/Currie' },
{ text: 'Australia/Darwin', value: 'Australia/Darwin' },
{ text: 'Australia/Eucla', value: 'Australia/Eucla' },
{ text: 'Australia/Hobart', value: 'Australia/Hobart' },
{ text: 'Australia/LHI', value: 'Australia/LHI' },
{ text: 'Australia/Lindeman', value: 'Australia/Lindeman' },
{ text: 'Australia/Lord_Howe', value: 'Australia/Lord_Howe' },
{ text: 'Australia/Melbourne', value: 'Australia/Melbourne' },
{ text: 'Australia/NSW', value: 'Australia/NSW' },
{ text: 'Australia/North', value: 'Australia/North' },
{ text: 'Australia/Perth', value: 'Australia/Perth' },
{ text: 'Australia/Queensland', value: 'Australia/Queensland' },
{ text: 'Australia/South', value: 'Australia/South' },
{ text: 'Australia/Sydney', value: 'Australia/Sydney' },
{ text: 'Australia/Tasmania', value: 'Australia/Tasmania' },
{ text: 'Australia/Victoria', value: 'Australia/Victoria' },
{ text: 'Australia/West', value: 'Australia/West' },
{ text: 'Australia/Yancowinna', value: 'Australia/Yancowinna' },
{ text: 'Brazil/Acre', value: 'Brazil/Acre' },
{ text: 'Brazil/DeNoronha', value: 'Brazil/DeNoronha' },
{ text: 'Brazil/East', value: 'Brazil/East' },
{ text: 'Brazil/West', value: 'Brazil/West' },
{ text: 'Canada/Atlantic', value: 'Canada/Atlantic' },
{ text: 'Canada/Central', value: 'Canada/Central' },
{ text: 'Canada/Eastern', value: 'Canada/Eastern' },
{ text: 'Canada/Mountain', value: 'Canada/Mountain' },
{ text: 'Canada/Newfoundland', value: 'Canada/Newfoundland' },
{ text: 'Canada/Pacific', value: 'Canada/Pacific' },
{ text: 'Canada/Saskatchewan', value: 'Canada/Saskatchewan' },
{ text: 'Canada/Yukon', value: 'Canada/Yukon' },
{ text: 'Chile/Continental', value: 'Chile/Continental' },
{ text: 'Chile/EasterIsland', value: 'Chile/EasterIsland' },
{ text: 'Cuba', value: 'Cuba' },
{ text: 'Egypt', value: 'Egypt' },
{ text: 'Eire', value: 'Eire' },
{ text: 'Europe/Amsterdam', value: 'Europe/Amsterdam' },
{ text: 'Europe/Andorra', value: 'Europe/Andorra' },
{ text: 'Europe/Astrakhan', value: 'Europe/Astrakhan' },
{ text: 'Europe/Athens', value: 'Europe/Athens' },
{ text: 'Europe/Belfast', value: 'Europe/Belfast' },
{ text: 'Europe/Belgrade', value: 'Europe/Belgrade' },
{ text: 'Europe/Berlin', value: 'Europe/Berlin' },
{ text: 'Europe/Bratislava', value: 'Europe/Bratislava' },
{ text: 'Europe/Brussels', value: 'Europe/Brussels' },
{ text: 'Europe/Bucharest', value: 'Europe/Bucharest' },
{ text: 'Europe/Budapest', value: 'Europe/Budapest' },
{ text: 'Europe/Busingen', value: 'Europe/Busingen' },
{ text: 'Europe/Chisinau', value: 'Europe/Chisinau' },
{ text: 'Europe/Copenhagen', value: 'Europe/Copenhagen' },
{ text: 'Europe/Dublin', value: 'Europe/Dublin' },
{ text: 'Europe/Gibraltar', value: 'Europe/Gibraltar' },
{ text: 'Europe/Guernsey', value: 'Europe/Guernsey' },
{ text: 'Europe/Helsinki', value: 'Europe/Helsinki' },
{ text: 'Europe/Isle_of_Man', value: 'Europe/Isle_of_Man' },
{ text: 'Europe/Istanbul', value: 'Europe/Istanbul' },
{ text: 'Europe/Jersey', value: 'Europe/Jersey' },
{ text: 'Europe/Kaliningrad', value: 'Europe/Kaliningrad' },
{ text: 'Europe/Kiev', value: 'Europe/Kiev' },
{ text: 'Europe/Kirov', value: 'Europe/Kirov' },
{ text: 'Europe/Kyiv', value: 'Europe/Kyiv' },
{ text: 'Europe/Lisbon', value: 'Europe/Lisbon' },
{ text: 'Europe/Ljubljana', value: 'Europe/Ljubljana' },
{ text: 'Europe/London', value: 'Europe/London' },
{ text: 'Europe/Luxembourg', value: 'Europe/Luxembourg' },
{ text: 'Europe/Madrid', value: 'Europe/Madrid' },
{ text: 'Europe/Malta', value: 'Europe/Malta' },
{ text: 'Europe/Mariehamn', value: 'Europe/Mariehamn' },
{ text: 'Europe/Minsk', value: 'Europe/Minsk' },
{ text: 'Europe/Monaco', value: 'Europe/Monaco' },
{ text: 'Europe/Moscow', value: 'Europe/Moscow' },
{ text: 'Europe/Nicosia', value: 'Europe/Nicosia' },
{ text: 'Europe/Oslo', value: 'Europe/Oslo' },
{ text: 'Europe/Paris', value: 'Europe/Paris' },
{ text: 'Europe/Podgorica', value: 'Europe/Podgorica' },
{ text: 'Europe/Prague', value: 'Europe/Prague' },
{ text: 'Europe/Riga', value: 'Europe/Riga' },
{ text: 'Europe/Rome', value: 'Europe/Rome' },
{ text: 'Europe/Samara', value: 'Europe/Samara' },
{ text: 'Europe/San_Marino', value: 'Europe/San_Marino' },
{ text: 'Europe/Sarajevo', value: 'Europe/Sarajevo' },
{ text: 'Europe/Saratov', value: 'Europe/Saratov' },
{ text: 'Europe/Simferopol', value: 'Europe/Simferopol' },
{ text: 'Europe/Skopje', value: 'Europe/Skopje' },
{ text: 'Europe/Sofia', value: 'Europe/Sofia' },
{ text: 'Europe/Stockholm', value: 'Europe/Stockholm' },
{ text: 'Europe/Tallinn', value: 'Europe/Tallinn' },
{ text: 'Europe/Tirane', value: 'Europe/Tirane' },
{ text: 'Europe/Tiraspol', value: 'Europe/Tiraspol' },
{ text: 'Europe/Ulyanovsk', value: 'Europe/Ulyanovsk' },
{ text: 'Europe/Uzhgorod', value: 'Europe/Uzhgorod' },
{ text: 'Europe/Vaduz', value: 'Europe/Vaduz' },
{ text: 'Europe/Vatican', value: 'Europe/Vatican' },
{ text: 'Europe/Vienna', value: 'Europe/Vienna' },
{ text: 'Europe/Vilnius', value: 'Europe/Vilnius' },
{ text: 'Europe/Volgograd', value: 'Europe/Volgograd' },
{ text: 'Europe/Warsaw', value: 'Europe/Warsaw' },
{ text: 'Europe/Zagreb', value: 'Europe/Zagreb' },
{ text: 'Europe/Zaporozhye', value: 'Europe/Zaporozhye' },
{ text: 'Europe/Zurich', value: 'Europe/Zurich' },
{ text: 'Hongkong', value: 'Hongkong' },
{ text: 'Iceland', value: 'Iceland' },
{ text: 'Indian/Antananarivo', value: 'Indian/Antananarivo' },
{ text: 'Indian/Chagos', value: 'Indian/Chagos' },
{ text: 'Indian/Christmas', value: 'Indian/Christmas' },
{ text: 'Indian/Cocos', value: 'Indian/Cocos' },
{ text: 'Indian/Comoro', value: 'Indian/Comoro' },
{ text: 'Indian/Kerguelen', value: 'Indian/Kerguelen' },
{ text: 'Indian/Mahe', value: 'Indian/Mahe' },
{ text: 'Indian/Maldives', value: 'Indian/Maldives' },
{ text: 'Indian/Mauritius', value: 'Indian/Mauritius' },
{ text: 'Indian/Mayotte', value: 'Indian/Mayotte' },
{ text: 'Indian/Reunion', value: 'Indian/Reunion' },
{ text: 'Iran', value: 'Iran' },
{ text: 'Israel', value: 'Israel' },
{ text: 'Jamaica', value: 'Jamaica' },
{ text: 'Japan', value: 'Japan' },
{ text: 'Kwajalein', value: 'Kwajalein' },
{ text: 'Libya', value: 'Libya' },
{ text: 'Mexico/BajaNorte', value: 'Mexico/BajaNorte' },
{ text: 'Mexico/BajaSur', value: 'Mexico/BajaSur' },
{ text: 'Mexico/General', value: 'Mexico/General' },
{ text: 'Navajo', value: 'Navajo' },
{ text: 'Pacific/Apia', value: 'Pacific/Apia' },
{ text: 'Pacific/Auckland', value: 'Pacific/Auckland' },
{ text: 'Pacific/Bougainville', value: 'Pacific/Bougainville' },
{ text: 'Pacific/Chatham', value: 'Pacific/Chatham' },
{ text: 'Pacific/Chuuk', value: 'Pacific/Chuuk' },
{ text: 'Pacific/Easter', value: 'Pacific/Easter' },
{ text: 'Pacific/Efate', value: 'Pacific/Efate' },
{ text: 'Pacific/Enderbury', value: 'Pacific/Enderbury' },
{ text: 'Pacific/Fakaofo', value: 'Pacific/Fakaofo' },
{ text: 'Pacific/Fiji', value: 'Pacific/Fiji' },
{ text: 'Pacific/Funafuti', value: 'Pacific/Funafuti' },
{ text: 'Pacific/Galapagos', value: 'Pacific/Galapagos' },
{ text: 'Pacific/Gambier', value: 'Pacific/Gambier' },
{ text: 'Pacific/Guadalcanal', value: 'Pacific/Guadalcanal' },
{ text: 'Pacific/Guam', value: 'Pacific/Guam' },
{ text: 'Pacific/Honolulu', value: 'Pacific/Honolulu' },
{ text: 'Pacific/Johnston', value: 'Pacific/Johnston' },
{ text: 'Pacific/Kanton', value: 'Pacific/Kanton' },
{ text: 'Pacific/Kiritimati', value: 'Pacific/Kiritimati' },
{ text: 'Pacific/Kosrae', value: 'Pacific/Kosrae' },
{ text: 'Pacific/Kwajalein', value: 'Pacific/Kwajalein' },
{ text: 'Pacific/Majuro', value: 'Pacific/Majuro' },
{ text: 'Pacific/Marquesas', value: 'Pacific/Marquesas' },
{ text: 'Pacific/Midway', value: 'Pacific/Midway' },
{ text: 'Pacific/Nauru', value: 'Pacific/Nauru' },
{ text: 'Pacific/Niue', value: 'Pacific/Niue' },
{ text: 'Pacific/Norfolk', value: 'Pacific/Norfolk' },
{ text: 'Pacific/Noumea', value: 'Pacific/Noumea' },
{ text: 'Pacific/Pago_Pago', value: 'Pacific/Pago_Pago' },
{ text: 'Pacific/Palau', value: 'Pacific/Palau' },
{ text: 'Pacific/Pitcairn', value: 'Pacific/Pitcairn' },
{ text: 'Pacific/Pohnpei', value: 'Pacific/Pohnpei' },
{ text: 'Pacific/Ponape', value: 'Pacific/Ponape' },
{ text: 'Pacific/Port_Moresby', value: 'Pacific/Port_Moresby' },
{ text: 'Pacific/Rarotonga', value: 'Pacific/Rarotonga' },
{ text: 'Pacific/Saipan', value: 'Pacific/Saipan' },
{ text: 'Pacific/Samoa', value: 'Pacific/Samoa' },
{ text: 'Pacific/Tahiti', value: 'Pacific/Tahiti' },
{ text: 'Pacific/Tarawa', value: 'Pacific/Tarawa' },
{ text: 'Pacific/Tongatapu', value: 'Pacific/Tongatapu' },
{ text: 'Pacific/Truk', value: 'Pacific/Truk' },
{ text: 'Pacific/Wake', value: 'Pacific/Wake' },
{ text: 'Pacific/Wallis', value: 'Pacific/Wallis' },
{ text: 'Pacific/Yap', value: 'Pacific/Yap' },
{ text: 'Poland', value: 'Poland' },
{ text: 'Portugal', value: 'Portugal' },
{ text: 'Singapore', value: 'Singapore' },
{ text: 'Turkey', value: 'Turkey' },
{ text: 'US/Alaska', value: 'US/Alaska' },
{ text: 'US/Aleutian', value: 'US/Aleutian' },
{ text: 'US/Arizona', value: 'US/Arizona' },
{ text: 'US/Central', value: 'US/Central' },
{ text: 'US/East-Indiana', value: 'US/East-Indiana' },
{ text: 'US/Eastern', value: 'US/Eastern' },
{ text: 'US/Hawaii', value: 'US/Hawaii' },
{ text: 'US/Indiana-Starke', value: 'US/Indiana-Starke' },
{ text: 'US/Michigan', value: 'US/Michigan' },
{ text: 'US/Mountain', value: 'US/Mountain' },
{ text: 'US/Pacific', value: 'US/Pacific' },
{ text: 'US/Samoa', value: 'US/Samoa' },
{ text: 'UTC', value: 'UTC' },
{ text: 'Zulu', value: 'Zulu' }
]

View File

@ -52,6 +52,10 @@
'label' => 'Language',
'help' => 'Language used to translate the 2FAuth user interface. Named languages are complete, set the one of your choice to override your browser preference.'
],
'timezone' => [
'label' => 'Time zone',
'help' => 'The time zone applied to all dates and times displayed in the application'
],
'show_otp_as_dot' => [
'label' => 'Show generated <abbr title="One-Time Password">OTP</abbr> as dot',
'help' => 'Replace generated password caracters with *** to ensure confidentiality. Do not affect the copy/paste feature'

View File

@ -5,6 +5,7 @@
use App\Api\v1\Controllers\UserManagerController;
use App\Api\v1\Resources\UserManagerResource;
use App\Models\AuthLog;
use App\Models\TwoFAccount;
use App\Models\User;
use App\Policies\UserPolicy;
use Database\Factories\UserFactory;
@ -13,7 +14,6 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Password;
@ -40,6 +40,11 @@ class UserManagerControllerTest extends FeatureTestCase
protected $anotherUser;
/**
* @var array
*/
protected $defaultPreferences;
private const USERNAME = 'john doe';
private const EMAIL = 'johndoe@example.org';
@ -56,6 +61,11 @@ public function setUp() : void
$this->admin = User::factory()->administrator()->create();
$this->user = User::factory()->create();
$this->anotherUser = User::factory()->create();
TwoFAccount::factory()->for($this->anotherUser)->create();
foreach (config('2fauth.preferences') as $pref => $value) {
$this->defaultPreferences[$pref] = config('2fauth.preferences.' . $pref);
}
}
/**
@ -79,63 +89,71 @@ public function test_all_controller_routes_are_protected_by_admin_middleware()
/**
* @test
*/
public function test_index_returns_all_users()
public function test_index_returns_all_users_with_expected_UserManagerResources() : void
{
$this->actingAs($this->admin, 'api-guard')
$response = $this->actingAs($this->admin, 'api-guard')
->json('GET', '/api/v1/users')
->assertOk()
->assertJsonCount(3)
->assertJsonFragment([
'email' => $this->admin->email,
->assertJsonStructure([
'*' => [
"last_seen_at",
"created_at",
]
])
->assertJsonFragment([
'email' => $this->user->email,
"id" => $this->user->id,
"name" => $this->user->name,
"email" => $this->user->email,
"oauth_provider" => null,
"preferences" => $this->defaultPreferences,
"is_admin" => false,
"twofaccounts_count" => 0,
])
->assertJsonFragment([
'email' => $this->anotherUser->email,
"id" => $this->admin->id,
"name" => $this->admin->name,
"email" => $this->admin->email,
"oauth_provider" => null,
"preferences" => $this->defaultPreferences,
"is_admin" => true,
"twofaccounts_count" => 0,
])
->assertJsonFragment([
"id" => $this->anotherUser->id,
"name" => $this->anotherUser->name,
"email" => $this->anotherUser->email,
"oauth_provider" => null,
"preferences" => $this->defaultPreferences,
"is_admin" => false,
"twofaccounts_count" => 1,
]);
}
/**
* @test
*/
public function test_index_succeeds_and_returns_UserManagerResource() : void
{
$path = '/api/v1/users';
$resources = UserManagerResource::collection(User::all());
$request = Request::create($path, 'GET');
$this->actingAs($this->admin, 'api-guard')
->json('GET', $path)
->assertExactJson($resources->response($request)->getData(true));
}
/**
* @test
*/
public function test_show_returns_the_correct_user()
public function test_show_returns_the_expected_UserManagerResource() : void
{
$this->actingAs($this->admin, 'api-guard')
->json('GET', '/api/v1/users/' . $this->user->id)
->assertJsonFragment([
'email' => $this->user->email,
->assertJson([
"info" => [
"id" => $this->user->id,
"name" => $this->user->name,
"email" => $this->user->email,
"oauth_provider" => null,
"preferences" => $this->defaultPreferences,
"is_admin" => false,
"twofaccounts_count" => 0,
"last_seen_at" => "1 second ago",
"created_at" => "1 second ago"
],
"password_reset" => null,
"valid_personal_access_tokens" => 0,
"webauthn_credentials" => 0
]);
}
/**
* @test
*/
public function test_show_returns_UserManagerResource() : void
{
$path = '/api/v1/users/' . $this->user->id;
$resources = UserManagerResource::make($this->user);
$request = Request::create($path, 'GET');
$this->actingAs($this->admin, 'api-guard')
->json('GET', $path)
->assertExactJson($resources->response($request)->getData(true));
}
/**
* @test
*/
@ -584,6 +602,35 @@ public function test_authentications_returns_expected_resource() : void
]);
}
/**
* @test
*/
public function test_authentications_returns_resource_with_timezoned_dates() : void
{
$timezone = 'Europe/Paris';
$this->admin['preferences->timezone'] = $timezone;
$this->admin->save();
$now = now();
$timezonedNow = now($timezone);
AuthLog::factory()->for($this->user, 'authenticatable')->create([
'login_at' => $now,
'logout_at' => $now,
]);
$response = $this->actingAs($this->admin, 'api-guard')
->json('GET', '/api/v1/users/' . $this->user->id . '/authentications');
$login_at = Carbon::parse($response->getData()[0]->login_at);
$logout_at = Carbon::parse($response->getData()[0]->logout_at);
$this->assertTrue($login_at->isSameHour($timezonedNow));
$this->assertTrue($login_at->isSameMinute($timezonedNow));
$this->assertTrue($logout_at->isSameHour($timezonedNow));
$this->assertTrue($logout_at->isSameMinute($timezonedNow));
}
/**
* @test
*/