Skip to content

Commit

Permalink
Merge pull request #892 from bcgov/gurj/refresh-pw
Browse files Browse the repository at this point in the history
feat: refresh password
  • Loading branch information
gurjmatharu authored Oct 25, 2023
2 parents 222a8ad + 1506953 commit 92afa21
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .config import InnkeeperWalletConfig
from .utils import (
approve_reservation,
refresh_registration_token,
create_api_key,
EndorserLedgerConfigSchema,
ReservationException,
Expand Down Expand Up @@ -91,15 +92,18 @@ async def wrapper(request):

return wrapper


# extending the CreateWalletTokenRequestSchema to allow for an API key
class CustomCreateWalletTokenRequestSchema(CreateWalletTokenRequestSchema):
"""Request schema for creating a wallet token."""

api_key = fields.Str(
description="API key for this wallet",
required=False,
example="3bd14a1e8fb645ddadf9913c0922ff3b",
)


class DefaultConfigValuesSchema(OpenAPISchema):
"""Response schema for default config values."""

Expand Down Expand Up @@ -136,6 +140,7 @@ class ReservationRequestSchema(OpenAPISchema):
example={"contact_phone": "555-555-5555"},
)


class ReservationResponseSchema(OpenAPISchema):
"""Response schema for tenant reservation."""

Expand Down Expand Up @@ -190,6 +195,10 @@ class ReservationApproveSchema(OpenAPISchema):
"""Request schema for tenant reservation approval."""


class ReservationRefreshSchema(OpenAPISchema):
"""Request schema for tenant reservation approval."""


class ReservationApproveResponseSchema(OpenAPISchema):
"""Response schema for tenant reservation approval."""

Expand Down Expand Up @@ -639,6 +648,25 @@ async def innkeeper_reservations_approve(request: web.BaseRequest):
return web.json_response({"reservation_pwd": _pwd})


@docs(
tags=[SWAGGER_CATEGORY],
)
@match_info_schema(ReservationIdMatchInfoSchema())
@request_schema(ReservationRefreshSchema())
@response_schema(ReservationApproveResponseSchema(), 200, description="")
@innkeeper_only
@error_handler
async def innkeeper_reservations_refresh_password(request: web.BaseRequest):
context: AdminRequestContext = request["context"]
reservation_id = request.match_info["reservation_id"]
mgr = context.inject(TenantManager)
try:
_pwd = await refresh_registration_token(reservation_id, mgr)
except ReservationException as err:
raise web.HTTPConflict(reason=str(err))
return web.json_response({"reservation_pwd": _pwd})


@docs(
tags=[SWAGGER_CATEGORY],
)
Expand Down Expand Up @@ -878,6 +906,10 @@ async def register(app: web.Application):
"/innkeeper/reservations/{reservation_id}/deny",
innkeeper_reservations_deny,
),
web.put(
"/innkeeper/reservations/{reservation_id}/refresh-password",
innkeeper_reservations_refresh_password,
),
web.get("/innkeeper/tenants/", innkeeper_tenants_list, allow_head=False),
web.get(
"/innkeeper/tenants/{tenant_id}", innkeeper_tenant_get, allow_head=False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,46 @@ async def approve_reservation(
return _pwd


async def refresh_registration_token(reservation_id: str, manager: TenantManager):
"""
Invalidate the old token, generate a new token, and update the reservation record.
:return: new_token: the new refreshed token
"""
async with manager.profile.session() as session:
try:
reservation = await ReservationRecord.retrieve_by_reservation_id(
session, reservation_id, for_update=True
)
except Exception as err:
LOGGER.error("Failed to retrieve reservation: %s", err)
raise ReservationException("Could not retrieve reservation record.") from err

if reservation.state != ReservationRecord.STATE_APPROVED:
raise ReservationException("Only approved reservations can refresh tokens.")

# Generate new token data
_pwd = str(uuid.uuid4().hex) # This generates a new token
_salt = bcrypt.gensalt()
_hash = bcrypt.hashpw(_pwd.encode("utf-8"), _salt)

minutes = manager._config.reservation.expiry_minutes
_expiry = datetime.utcnow() + timedelta(minutes=minutes)

# Update the reservation record with the new token and related info
reservation.reservation_token_salt = _salt.decode("utf-8")
reservation.reservation_token_hash = _hash.decode("utf-8")
reservation.reservation_token_expiry = _expiry

try:
await reservation.save(session)
except Exception as err:
LOGGER.error("Failed to update reservation record: %s", err)
raise ReservationException("Could not update reservation record.") from err

LOGGER.info("Refreshed token for reservation %s", reservation_id)

return _pwd

def generate_api_key_data():
_key = str(uuid.uuid4().hex)
LOGGER.info(f"_key = {_key}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const emit = defineEmits(['success']);
// Approve reservation
const confirmApprove = (event: any) => {
console.log('here');
confirm.require({
target: event.currentTarget,
message: `Approve reservation for ${props.email}?`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<template>
<Button
:label="$t('reservations.refreshPassword')"
class="p-button-rounded p-button-text"
icon="pi pi-refresh"
@click="refresh"
/>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import Button from 'primevue/button';
import { useToast } from 'vue-toastification';
import { useInnkeeperTenantsStore } from '@/store';
import { useConfigStore } from '@/store';
import { storeToRefs } from 'pinia';
const { config } = storeToRefs(useConfigStore());
const innkeeperTenantsStore = useInnkeeperTenantsStore();
const toast = useToast();
const { t } = useI18n();
const props = defineProps({
id: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
});
const emit = defineEmits(['success']);
const displayModal = ref(false);
const reason = ref('');
const refresh = async () => {
try {
const res = await innkeeperTenantsStore.refreshCheckInPassword(
props.id,
props.email,
{
state_notes: reason.value,
}
);
if (config.value.frontend.showInnkeeperReservationPassword) {
// Have to handle the dialog up a level or it deletes when the rows re-draw after reload
emit('success', res.reservation_pwd, props.email);
toast.success(t('reservations.approved.toast', { email: props.email }));
} else {
toast.success(t('reservations.approved.text', { email: props.email }));
}
} catch (error) {
toast.error(`Failure: ${error}`);
} finally {
innkeeperTenantsStore.listReservations();
}
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,15 @@
:show-filter-match-modes="false"
>
<template #body="{ data }">
<StatusChip :status="data.state" />
<div class="state-container">
<StatusChip :status="data.state" />
<RefreshPassword
v-if="data.state === 'approved'"
:id="data.reservation_id"
:email="data.contact_email"
@success="showModal"
/>
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText
Expand Down Expand Up @@ -131,36 +139,67 @@
</template>
</DataTable>
</MainCardContent>

<Dialog
v-model:visible="displayModal"
:header="$t('reservations.refreshed.title')"
:modal="true"
>
<p>
{{ $t('reservations.approved.text', { email: approvedEmail }) }}
</p>
<p>
{{ $t('reservations.otp') }} <br />
<strong>{{ approvedPassword }}</strong>
</p>
</Dialog>
</template>

<script setup lang="ts">
// Vue
import { onMounted, ref, computed } from 'vue';
// PrimeVue
import Accordion from 'primevue/accordion';
import AccordionTab from 'primevue/accordiontab';
import Button from 'primevue/button';
import Column from 'primevue/column';
import DataTable, { DataTableFilterMetaData } from 'primevue/datatable';
import Dialog from 'primevue/dialog';
import InputText from 'primevue/inputtext';
import { FilterMatchMode } from 'primevue/api';
import { useToast } from 'vue-toastification';
import Accordion from 'primevue/accordion';
import AccordionTab from 'primevue/accordiontab';
// external
import VueJsonPretty from 'vue-json-pretty';
// State
import { useToast } from 'vue-toastification';
// store
import { useInnkeeperTenantsStore } from '@/store';
import { storeToRefs } from 'pinia';
// Other components
// Importing internal and helpers
import MainCardContent from '@/components/layout/mainCard/MainCardContent.vue';
import RefreshPassword from './RefreshPassword.vue';
import StatusChip from '@/components/common/StatusChip.vue';
import { TABLE_OPT } from '@/helpers/constants';
import { formatDateLong } from '@/helpers';
import { TABLE_OPT } from '@/helpers/constants';
const toast = useToast();
const innkeeperTenantsStore = useInnkeeperTenantsStore();
// modal
const displayModal = ref(false);
const approvedPassword = ref('');
const approvedEmail = ref('');
const innkeeperTenantsStore = useInnkeeperTenantsStore();
const { loading, reservationHistory } = storeToRefs(useInnkeeperTenantsStore());
const showModal = (password: string, email: string) => {
approvedPassword.value = password;
approvedEmail.value = email;
displayModal.value = true;
};
// Loading table contents
const loadTable = async () => {
innkeeperTenantsStore.listReservations().catch((err: string) => {
Expand Down Expand Up @@ -214,3 +253,8 @@ const filter = ref({
// necessary for expanding rows, we don't do anything with this
const expandedRows = ref([]);
</script>
<style scoped>
.state-container {
display: flex;
}
</style>
2 changes: 2 additions & 0 deletions services/tenant-ui/frontend/src/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export const API_PATH = {
INNKEEPER_RESERVATIONS: '/innkeeper/reservations/',
INNKEEPER_RESERVATIONS_APPROVE: (id: string) =>
`/innkeeper/reservations/${id}/approve`,
INNKEEPER_RESERVATIONS_REFRESH_PASSWORD: (id: string) =>
`/innkeeper/reservations/${id}/refresh-password`,
INNKEEPER_RESERVATIONS_CONFIG: (id: string) =>
`/innkeeper/reservations/${id}/config`,
INNKEEPER_RESERVATIONS_DENY: (id: string) =>
Expand Down
6 changes: 6 additions & 0 deletions services/tenant-ui/frontend/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@
"title": "Reservation Approved",
"toast": "Reservation Approved for {email}"
},
"refreshed": {
"text": "The Check-In password has been re-sent to {email}",
"title": "Password Refreshed",
"toast": "Password Refreshed for {email}"
},
"approveRequest": "Approve Request",
"checkJunkFolder": "(Please check your junk/spam folder before contacting us, as it is very common to have the email delivery problems because of automated filters.)",
"contactEmail": "Contact Email",
Expand All @@ -364,6 +369,7 @@
"toast": "Reservation Denied for {email}"
},
"denyRequest": "Deny Request",
"refreshPassword": "Refresh Password",
"emailSentTo": "An email has also been sent to {0} with the reservation number below.",
"enterPassword": "Please enter the reservation password below to validate your account.",
"history": "History",
Expand Down
6 changes: 6 additions & 0 deletions services/tenant-ui/frontend/src/plugins/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@
"title": "Reservation Approved <FR>",
"toast": "Reservation Approved for {email} <FR>"
},
"refreshed": {
"text": "The Check-In password has been re-sent to {email} <FR>",
"title": "Password Refreshed <FR>",
"toast": "Password Refreshed for {email} <FR>"
},
"approveRequest": "Approve Request <FR>",
"checkJunkFolder": "(Please check your junk/spam folder before contacting us, as it is very common to have the email delivery problems because of automated filters.) <FR>",
"contactEmail": "Contact Email <FR>",
Expand All @@ -362,6 +367,7 @@
"toast": "Reservation Denied for {email} <FR>"
},
"denyRequest": "Deny Request <FR>",
"refreshPassword": "Refresh Password <FR>",
"emailAddress": "Email Address <FR>",
"emailSentTo": "An email has also been sent to {0} with the reservation number below. <FR>",
"enterPassword": "Please enter the reservation password below to validate your account. <FR>",
Expand Down
7 changes: 7 additions & 0 deletions services/tenant-ui/frontend/src/plugins/i18n/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,12 @@
"title": "Reservation Approved <JA>",
"toast": "Reservation Approved for {email} <JA>"
},

"refreshed": {
"text": "The Check-In password has been re-sent to {email} <JA>",
"title": "Password Refreshed <JA>",
"toast": "Password Refreshed for {email} <JA>"
},
"approveRequest": "Approve Request <JA>",
"checkJunkFolder": "(Please check your junk/spam folder before contacting us, as it is very common to have the email delivery problems because of automated filters.) <JA>",
"contactEmail": "Contact Email <JA>",
Expand All @@ -362,6 +368,7 @@
"toast": "Reservation Denied for {email} <JA>"
},
"denyRequest": "Deny Request <JA>",
"refreshPassword": "Refresh Password <JA>",
"emailAddress": "Email Address <JA>",
"emailSentTo": "An email has also been sent to {0} with the reservation number below. <JA>",
"enterPassword": "Please enter the reservation password below to validate your account. <JA>",
Expand Down
Loading

0 comments on commit 92afa21

Please sign in to comment.