Osy#betaRun it on Osyrin: osyrin.com

Reference / Security

The Guardianship kit — who acts for whom: children, carers, consents and emergency contacts

use Osysharp.Guardianship@0; · policy ManagesGuardianships => IsDesk; · ActsForDependant(person) · AddDependant(name, bornOn) · InviteGuardian(child, address) · AnswerConsent(child, form, ConsentAnswer.Given)

`Osysharp.Guardianship` links a person to the people who look after them. A parent adds a child (an account the child cannot sign in to yet), invites a second guardian by address, answers the app's consent forms for the child and keeps their emergency contacts and the people who may collect them; staff read all of it on site. Each link has a standing — acts for them, kept informed, ended — that changes by itself on the day the child comes of age. The app writes its own rules with one policy, `ActsForDependant`.

preview1 example compiled by CIsecuritykitfamilyguardianship

Source on GitHub: Osysharp.Guardianship — github.com/osysharp/guardianship ↗

Summary#

use Osysharp.Guardianship@0; adds who looks after whom. The app says who runs it and who reads it on site, and builds its own rules on ActsForDependant:

policy ManagesGuardianships => IsDesk;
policy SeesFamilies => IsDesk || IsInstructor;
policy MakesManagedAccounts => IsAuthenticated;      // Osysharp.UserAccounts: who may add a child
policy BooksFor(Principal person) => person == user || ActsForDependant(person);

Signature#

virtual policy ManagesGuardianships => false;
virtual policy SeesFamilies => ManagesGuardianships;
virtual policy MayLinkAsGuardian(Principal guardian, Principal dependant) => guardian != dependant;
public policy ActsForDependant(Principal dependant);      // a guardian who acts for them
public policy KeptInformedOf(Principal dependant);        // …or is kept informed
public policy ActsForSomeone;   public policy LooksAfterSomeone;   // the `when` beside them: is the caller a guardian at all
app.Guardianship = new GuardianshipSetup { AgeOfMajority = 18, AtMajority = GuardianStanding.Ended,
                                           InvitationValidFor = TimeSpan.FromDays(14), Mails = new GuardianshipMails() };

Guardianship AddDependant(string name, DateOnly? bornOn = null, GuardianRelationship relationship = Parent, string careNotes = "");
Guardianship LinkGuardian(Principal guardian, Principal dependant, GuardianRelationship r = Parent, GuardianStanding s = Acting);
Guardianship ChangeStanding(Guardianship link, GuardianStanding standing, string reason = "");
GuardianInvitation InviteGuardian(Principal dependant, string address, GuardianRelationship r = Parent, GuardianStanding s = Acting);
Guardianship AcceptGuardianInvitation(GuardianInvitation i);   void DeclineGuardianInvitation(…);   void WithdrawGuardianInvitation(…);
void SetBornOn(DependantProfile p, DateOnly bornOn);   void DeclareOfAge(DependantProfile p);   void HandOverAccount(Principal d, string address);
Consent AnswerConsent(Principal person, ConsentForm form, ConsentAnswer answer);   bool HasConsent(Principal person, string key);
ConsentForm ReviseConsentForm(ConsentForm form, string wording);   List<ConsentForm> ConsentsOwed(Principal person);
List<EmergencyContact> EmergencyContactsOf(Principal person);   List<string> MayCollectNames(Principal person);
int AgeAt(DateOnly bornOn, DateOnly on);   bool NeedsGuardian(DateOnly bornOn, DateOnly? on = null);   bool IsMinor(Principal person);
// events: GuardianLinked · GuardianStandingChanged · GuardianInvited · ConsentAnswered · DependantCameOfAge
// pages: /family (FamilyScreen) · /family/lookup (FamilyLookup, SeesFamilies)

Description#

A link is made in one of three ways: a guardian adds a child (AddDependant — the child gets an account through Osysharp.UserAccounts' MakeManagedAccount, and the caller acts for them); somebody accepts an invitation sent to their own proven address by a guardian who acts, or by an adult for themself; or the desk links a guardian it has checked (LinkGuardian). The app's own screening, MayLinkAsGuardian, is asked at the first two. A pair is one row, kept when it ends, so the history of who looked after whom stays.

Standings, and coming of age#

A link acts for the person (ActsForDependant: book, answer, change), keeps the guardian informed (KeptInformedOf: read only), or has ended. A guardian may step back and never forward; the person changes it any way once they decide for themself, and never while they are a minor; the desk may do anything. Each profile carries a durable clock that waits for the person's birthday at the app's age of majority and then gives every acting guardian the standing AtMajority names — ended by default, or kept informed. A date set or corrected re-arms it at once; the desk can declare somebody of age early.

Consents, against the words#

A ConsentForm is a question the app asks — photos, first aid, travel. Each answer is a new row holding the version and words it answered, who answered and when; the latest counts. A minor's guardian answers for them; an adult answers for themself. Changing a form's words makes a new version, and HasConsent is false until it is answered again.

Emergency contacts, collection and care notes#

The person or a guardian who acts for them keeps the emergency contacts, in calling order, each marked if they may collect the person. Staff (SeesFamilies) read them with the guardians and the care notes. MayCollectNames lists the acting guardians first, then the contacts allowed to collect.

Handing the account over#

HandOverAccount(child, address) gives a child's account an address of its own (Osysharp.UserAccounts' GiveManagedAccountAnAddress); once the link mailed there is opened the child signs in themself. Their guardians go on acting for them until they come of age.

Personal data#

Dates of birth, care notes (special-category data), emergency contacts and invitation addresses are classified, so their values never reach the audit trail. With Osysharp.Privacy, Osysharp.Privacy.Guardianship arrives by itself and answers requests for all of it.

Examples#

A music school where parents read their child's lesson notes#

app MusicSchool {
  use Osysharp.Ui;
  use Osysharp.Workflow;
  use Osysharp.Oauth;
  use Osysharp.Identity@0;
  use Osysharp.Mail@0;
  use Osysharp.Sms@0;
  use Osysharp.EmailTemplates@0;
  use Osysharp.UserAccounts@1;
  use Osysharp.Guardianship@0;
  model "model/**/*.osy";
}

using Osysharp.Identity;
using Osysharp.UserAccounts;
using Osysharp.Guardianship;

[Role] enum SchoolRole { Authenticator, Member, Teacher, Office }
policy IsOffice => RoleGrant.Any(g => g.Member == user && g.Role == SchoolRole.Office);
policy IsTeacher => RoleGrant.Any(g => g.Member == user && g.Role == SchoolRole.Teacher);
policy ManagesGuardianships => IsOffice;
policy SeesFamilies => IsOffice || IsTeacher;
policy MakesManagedAccounts => IsAuthenticated;

app.Accounts = new AccountsSetup { AppName = "Music School", SignupRole = SchoolRole.Member };
app.Guardianship = new GuardianshipSetup { AgeOfMajority = 16, AtMajority = GuardianStanding.Informed };

/// A teacher's note: the pupil reads it, and so does anyone who looks after them.
entity LessonNote {
  [Required] Principal Pupil;
  [Required, MaxLength(400)] string Text;
  security {
    allow read when IsAuthenticated where Pupil == user;
    allow read when LooksAfterSomeone where KeptInformedOf(Pupil);
    allow read, create when IsTeacher;
  }
}

/// A parent adds their child and answers the school's photo consent for them.
void Enrol(string name, DateOnly bornOn) {
  var link = AddDependant(name, bornOn);
  var photos = ConsentForm.FirstOrDefault(f => f.Key == "photos");
  if (photos != null) { AnswerConsent(link.Dependant, photos, ConsentAnswer.Given); }
}

See also#

Related

The UserAccounts kit — sign-in done properly, once

`Osysharp.UserAccounts` is the default way into an app: sign-in with a password or a provider (Google, GitHub, …)…

The UserManagement kit — the user manager: who is in the app, what each may do, and who changed it

`Osysharp.UserManagement` is an app's user manager: a searchable people list with each person's roles, two-step status…

The ClassBookings kit — classes, courses and lessons with places, a waiting list and a register

`Osysharp.ClassBookings` is bookable sessions with a fixed number of numbered places that are never overbooked: a…

The Privacy kit — erasure that keeps only what the company must or may

`Osysharp.Privacy` answers a person's GDPR request — access, portability, erasure, restriction — per category of what…