diff --git a/src/JobsJobsJobs/Common/Domain.fs b/src/JobsJobsJobs/Common/Domain.fs index 409aa72..601019c 100644 --- a/src/JobsJobsJobs/Common/Domain.fs +++ b/src/JobsJobsJobs/Common/Domain.fs @@ -159,6 +159,8 @@ type OtherContact = /// Visibility options for an employment profile type ProfileVisibility = + /// Profile is only visible to the citizen to whom it belongs + | Hidden /// Profile is only visible to authenticated users | Private /// Anonymous information is visible to public users @@ -172,6 +174,7 @@ module ProfileVisibility = /// Parse a string into a profile visibility let parse viz = match viz with + | "Hidden" -> Hidden | "Private" -> Private | "Anonymous" -> Anonymous | "Public" -> Public @@ -180,6 +183,7 @@ module ProfileVisibility = /// Convert a profile visibility to its string representation let toString = function + | Hidden -> "Hidden" | Private -> "Private" | Anonymous -> "Anonymous" | Public -> "Public" diff --git a/src/JobsJobsJobs/Profiles/Handlers.fs b/src/JobsJobsJobs/Profiles/Handlers.fs index 6bce6c8..d60f14c 100644 --- a/src/JobsJobsJobs/Profiles/Handlers.fs +++ b/src/JobsJobsJobs/Profiles/Handlers.fs @@ -209,31 +209,38 @@ let deleteHistory idx : HttpHandler = requireUser >=> validateCsrf >=> fun next | None -> return! notFound ctx } -// GET: /profile/[id]/view -let view citizenId : HttpHandler = fun next ctx -> task { +/// Get a profile for view, and enforce visibility restrictions against the current user +let private getProfileForView citizenId ctx = task { let citId = CitizenId citizenId match! Data.findByIdForView citId with | Some profile -> let currentCitizen = tryUser ctx |> Option.map CitizenId.ofString - if not (profile.Profile.Visibility = Public) && Option.isNone currentCitizen then - return! Error.notAuthorized next ctx - else - let title = $"Employment Profile for {Citizen.name profile.Citizen}" - return! Views.view profile currentCitizen |> render title next ctx + let canView = + match profile.Profile.Visibility, currentCitizen with + | Private, Some _ + | Anonymous, Some _ + | Public, _ -> true + | Hidden, Some citizenId when profile.Citizen.Id = citizenId -> true + | _ -> false + return if canView then Some (profile, currentCitizen) else None + | None -> return None +} + +// GET: /profile/[id]/view +let view citizenId : HttpHandler = fun next ctx -> task { + match! getProfileForView citizenId ctx with + | Some (profile, currentCitizen) -> + let title = $"Employment Profile for {Citizen.name profile.Citizen}" + return! Views.view profile currentCitizen |> render title next ctx | None -> return! notFound ctx } // GET: /profile/[id]/print let print citizenId : HttpHandler = fun next ctx -> task { - let citId = CitizenId citizenId - match! Data.findByIdForView citId with - | Some profile -> - let currentCitizen = tryUser ctx |> Option.map CitizenId.ofString - if not (profile.Profile.Visibility = Public) && Option.isNone currentCitizen then - return! Error.notAuthorized next ctx - else - let pageTitle = $"Employment Profile for {Citizen.name profile.Citizen}" - return! Views.print profile (Option.isNone currentCitizen) |> renderPrint pageTitle next ctx + match! getProfileForView citizenId ctx with + | Some (profile, currentCitizen) -> + let pageTitle = $"Employment Profile for {Citizen.name profile.Citizen}" + return! Views.print profile (Option.isNone currentCitizen) |> renderPrint pageTitle next ctx | None -> return! notFound ctx } diff --git a/src/JobsJobsJobs/Profiles/Views.fs b/src/JobsJobsJobs/Profiles/Views.fs index 6f00013..03ea4c8 100644 --- a/src/JobsJobsJobs/Profiles/Views.fs +++ b/src/JobsJobsJobs/Profiles/Views.fs @@ -102,6 +102,15 @@ let editGeneralInfo (m : EditProfileForm) continents isHtmx csrf = div [ _class "col-12" ] [ hr [] h4 [] [ txt "Visibility" ] + div [ _class "form-check" ] [ + let hid = ProfileVisibility.toString Hidden + input [ _type "radio"; _id $"{nameof m.Visibility}Hidden"; _name (nameof m.Visibility) + _class "form-check-input"; _value hid; if m.Visibility = hid then _checked ] + label [ _class "form-check-label"; _for $"{nameof m.Visibility}Hidden" ] [ + strong [] [ txt "Hidden" ] + txt " – do not show my employment profile to anyone else" + ] + ] div [ _class "form-check" ] [ let pvt = ProfileVisibility.toString Private input [ _type "radio"; _id $"{nameof m.Visibility}Private"; _name (nameof m.Visibility)