5.4. Administration (admin.rsl)

(****
 *
 * Module Admin defines the objects and operations related to maintaining the
 * user, group, location, and global options databases of the Calendar Tool.
 *
 *)
module Admin;

  export UserDB, GroupDB, LocationDB, UserId;

  object UserDB is
    components: UserRecord*;
    description: (*
        UserDB is the repository of registered user information.  It is a
        collection of UserRecords.
    *);
  end UserDB;

  object UserRecord is
    components: name:UserName and id:UserId and
        phone:PhoneNumber and email:EmailAddress and host:HostComputer and
        uid:ComputerUserID and home_dir:HomeDirectory and
        item_limit:ItemLimit and privileges:AdminPrivileges;
    description: (*
        A UserRecord is the information stored about a registered user of the
        Calendar Tool.  The Name component is the user's real-world name.  The
        nickname is an indvidualizable short-hand name for the user.  The
        UserId is the unique identifier by which the user is known to the
        Calendar Tool.  The EmailAddress is the electronic mail address used by
        the Calendar Tool to contact the user when necessary.  The PhoneNumber
        is for information purposes; it is not used by the Calendar Tool for
        contacting the user.
    *);
  end UserRecord;

  object Name is string
    description: (* The name of a registered user. *);
  end;

  object UserId is string
    description: (* The unique id of a registered user. *);
  end;

  object EmailAddress is string
    description: (* The electronic mail address of a registered user. *);
  end;

  object PhoneNumber is area:Area and number:Number
    description: (*
        A phone number consists of a three-digit area code and seven-digit
        number.
    *);
  end;

  object Area is integer
    description:  (*
        The three-digit area code component of a user phone number.
    *);
  end Area;

  object Number is integer
    description:  (*
        The seven-digit number component of a user phone number.
    *);
  end Number;

  object HostComputer is string
    description: (*
        The unique identifier of the host computer on which the user runs the
        Calendar Tool. The format of the identifier is platform-dependent,
        based on the type of network to which host computers are attached.  IP
        address is a typical format.
    *);
  end HostComputer;

  object ComputerUserID is string
    description: (*
       The user's identification on the host computer.
    *);
  end ComputerUserID;

  object HomeDirectory is string
    description: (*
        The file directory (i.e., folder) assigned as the user's home directory
        on the host computer.
    *);
  end HomeDirectory;

  object ItemLimit is integer
    description: (*
        The maximum number of scheduled items that can be stored for the user
        in the Calendar Tool central repository.  The limit value is optional.
        If it is absent, there is no limit.  The item limit can be zero, in
        which case the user has an empty calendar in the central repository.
    *);
  end ItemLimit;

  object AdminPrivileges is boolean
    description: (*
       Indicates if the has administrative privileges.
    *);
  end AdminPrivileges;

  operation AddUser is
    inputs: udb:UserDB, ur:UserRecord;
    outputs: udb':UserDB;
    description: (*
        Add the given UserRecord to the given UserDB.  The UserId of the given
        user record must not be the same as a user record already in the
        UserDB.  The UserId component is required and must be eight characters
        or less.  The email address is required.  The phone number is optional;
        if given, the area code and number must be 3 and 7 digits respectively.
    *);
    precondition:
        (*
         * There is no user record in the input UserDB with the same id as the
         * record to be added.
         *)
        (not (exists (ur' in udb) ur'.id = ur.id))

            and

        (*
         * The id of the given user record is not empty and 8 characters or
         * less.
         *)
        (ur.id != nil) and (#(ur.id) <= 8)

            and

        (*
         * The email address is not empty.
         *)
        (ur.email != nil)

            and

        (*
         * If the phone area code and number are present, they must be 3 digits
         * and 7 digits respectively.
         *)
        (if (ur.phone.area != nil) then (#(ur.phone.area) = 3)) and
        (if (ur.phone.number != nil) then (#(ur.phone.number) = 7));

    postcondition:
        (*
         * A user record is in the output db if and only if it is the new
         * record to be added or it is in the input db.
         *)
        forall (ur':UserRecord)
            (ur' in udb') iff ((ur' = ur) or (ur' in udb));

  end AddUser;

  operation FindUser is
    inputs: udb:UserDB, id:UserId;
    outputs: ur':UserRecord;
    description: (*
        Find a user by unique id.
    *);

    precondition: ;

    postcondition:
        (*
         * If there is a record with the given id in the input db, then the
         * output record is equal to that record, otherwise the output record
         * is empty.
         *)
        (exists (ur in udb) (ur.id = id) and (ur' = ur))
            or
        (ur' = nil);

  end FindUser;

  operation FindUser is
    inputs: udb:UserDB, n:Name;
    outputs: url:UserRecord*;
    description: (*
        Find a user or users by real-world name.  If more than one is found,
        the output list is sorted by id.
    *);

    precondition: ;

    postcondition:
        (*
         * The output list consists of all records of the given name in the
         * input db.
         *)
        (forall (ur' in url) (ur' in udb) and (ur'.name = n))

            and

        (*
         * The output list is sorted alphabetically by id.
         *)
        (forall (i:integer | (i >= 1) and (i < #url))
            url[i].id < url[i+1].id);

  end FindUser;

  operation FindUser is
    inputs: udb:UserDB, id:UserId, n:Name;
    outputs: ur':UserRecord;
    description: (*
        Find a user by both name and id.  This overload of FindUser is
        presumably used infrequently.  Its utility is to confirm that a
        particular user name and id are paired as assumed.
    *);

    precondition: ;

    postcondition:
        (*
         * If there is a record with the given name and id in the input db,
         * then the output record is equal to that record, otherwise the output
         * record is empty.
         *)
        (exists (ur in udb) (ur.name = n) and (ur.id = id) and (ur' = ur))
            or
        (ur' = nil);

  end FindUser;

  operation ChangeUser is
    inputs: udb:UserDB, gdb:GroupDB, old_ur:UserRecord, new_ur:UserRecord;
    outputs:  udb':UserDB, gdb':GroupDB;
    description: (*
        Change the given old UserRecord to the given new record.  The old and
        new records must not be the same.  The old record must already be in
        the input db.  The new record must meet the same conditions as for the
        input to the AddUser operation.  Typically the user runs the FindUser
        operation prior to Change to locate an existing record to be changed.
                                                                            <p>
        If the user record id is changed, then change all occurrences of the
        old id in the group db to the new id.
    *);

    precondition:
        (*
         * The old and new user records are not the same.
         *)
        (old_ur != new_ur)

            and

        (*
         * The old record is in the given db.
         *)
        (old_ur in udb)

            and

        (*
         * There is no user record in the input UserDB with the same id as the
         * new record to be added.
         *)
        (not (exists (new_ur' in udb) new_ur'.id = new_ur.id))

            and

        (*
         * The id of the new record is not empty and 8 characters or less.
         *)
        (new_ur.id != nil) and (#(new_ur.id) <= 8)

            and

        (*
         * The email address is not empty.
         *)
        (new_ur.email != nil)

            and

        (*
         * If the phone area code and number are present, they must be 3 digits
         * and 7 digits respectively.
         *)
        (if (new_ur.phone.area != nil) then (#(new_ur.phone.area) = 3)) and
        (if (new_ur.phone.number != nil) then (#(new_ur.phone.number) = 7));

    postcondition:
        (*
         * A user record is in the output db if and only if it is the new
         * record to be added or it is in the input db, and it is not the old
         * record.
         *)
        forall (ur':UserRecord)
            (ur' in udb') iff (((ur' = new_ur) or (ur' in udb)) and
                                   (ur' != old_ur))

            and

        (*
         * If new id is different than old id, then all occurrences of old id
         * in the GroupDB are replaced by new id.
         *)
        if (old_ur.id != new_ur.id)
        then
            ... (* Logic left as exercise for the reader. *) ;

  end ChangeUser;

  operation DeleteUser is
    inputs: udb:UserDB, gdb:GroupDB, ur:UserRecord;
    outputs: udb':UserDB, gdb':GroupDB, lgw:LeaderlessGroupsWarning;
    description: (*
        Delete the given user record from the given UserDB.  The given record
        must already be in the input db.  Typically the user runs the FindUser
        operation prior to Delete to locate an existing record to delete.
                                                                            <p>
        In addition, delete the user from all groups of which the user is a
        member.  If the deleted user is the only leader of a one more groups,
        output a warning indicating that those groups have become leaderless.
    *);

    precondition:
        (*
         * The given UserRecord is in the given UserDB.
         *)
        ur in udb;

    postcondition:
        (*
         * A user record is in the output db if and only if it is not the
         * existing record to be deleted and it is in the input db.
         *)
        (forall (ur':UserRecord)
            (ur' in udb') iff ((ur' != ur) and (ur' in udb)))

            and

        (*
         * The id of the deleted user is not in the leader or member lists of
         * any group in the output GroupDB.  (NOTE: This clause is not as
         * strong as a complete "no junk, no confusion" spec.  Why not?  Should
         * it be?)
         *)
        (forall (gr in gdb')
            (not (ur.id in gr.leaders)) and (not (ur.id in gr.members)))

            and

        (*
         * The LeaderlessGroupsWarning list contains the ids of all groups
         * whose only leader was the user who has just been deleted.
         *)
        (forall (gr in gdb)
            forall (id:UserId)
                (id in lgw) iff ((#(gr.leaders) = 1) and
                                   (gr.leaders[1] = ur.id)));

  end DeleteUser;

  object LeaderlessGroupsWarning is
    components: Name*;
    description: (*
        LeaderlessGroupsWarning is an secondary output of the Change and
        DeleteUser operations, indicating the names of zero or more groups that
        have become leaderless as the result of a user having been deleted.
    *);
  end LeaderlessGroupsWarning;

  object GroupDB is
    components: GroupRecord*;
    description: (*
        UserDB is the repository of user group information.
    *);
  end GroupDB;

  object GroupRecord is
    components: name:Name and id:GroupId and leaders:Leaders and
        members:Members;
    description: (*
        A GroupRecord is the information stored about a user group.  The Name
        component is a unique group name of any length.  Leaders is a list of
        zero or more users designated as group leader.  Members is the list of
        group members, including the leaders.  Both lists consist of user id's.
        Normally a group is required to have at least one leader.  The only
        case that a group becomes leaderless is when its leader is deleted as a
        registered user.
    *);
  end GroupRecord;

  object GroupId is string
    description: (* The unique id of a registed group. *);
  end GroupId;

  object Leaders is UserId*
    description: (*
        List of user ids for the zero or more leaders of a group.
    *);
  end;

  object Members is UserId*
    description: (*
        List of user ids for the zero or more members of a group.
    *);
  end;

  operation AddGroup is
    inputs: gdb:GroupDB, udb:UserDB, gr:GroupRecord;
    outputs: gdb':GroupDB;
    description: (*
        Add the given GroupRecord to the given GroupDB.  The name of the given
        group must not be the same as a group already in the GroupDB.  All
        group members must be registered users.  The leader(s) of the group
        must be members of it.
    *);

    precondition:
        (*
         * All group members are registered users.
         *)
        (forall (id in gr.members) exists (ur in udb) ur.id = id)

            and

        (*
         * All group leaders are members of the group.
         *)
        (forall (id in gr.leaders) id in gr.members);

    postcondition:
        (*
         * A group record is in the output db if and only if it is the new
         * record to be added or it is in the input db.
         *)
        forall (gr':GroupRecord)
            (gr' in gdb') iff ((gr' = gr) or (gr' in gdb));

  end AddGroup;

  operation FindGroup is
    inputs: gdb:GroupDB, n:Name;
    outputs: gr':GroupRecord;
    description: (*
        Find a group by unique name.
    *);

    precondition: ;

    postcondition:
        (*
         * If there is a record with the given name in the input db, then the
         * output record is equal to that record, otherwise the output record
         * is empty.
         *)
        (exists (gr in gdb) (gr.name = n) and (gr' = gr))
            or
        (gr' = nil);

  end FindGroup;

  operation ChangeGroup is
    inputs: gdb:GroupDB, udb:UserDB, old_gr:GroupRecord, new_gr:GroupRecord;
    outputs: gdb':GroupDB;
    description: (*
        Change the given old GroupRecord to the given new record.  The old and
        new records must not be the same.  The old record must already be in
        the input db.  The new record must meet the same conditions as for the
        input to the AddGroup operation.  Typically the user runs the FindGroup
        operation prior to Change to locate an existing record to be changed.
    *);

    precondition:
        (*
         * The old and new group records are not the same.
         *)
        (old_gr != new_gr)

            and

        (*
         * All group members are registered users.
         *)
        (forall (id in new_gr.members) exists (ur in udb) ur.id = id)

            and

        (*
         * All group leaders are members of the group.
         *)
        (forall (id in new_gr.leaders) id in new_gr.members);

    postcondition:
        (*
         * A group record is in the output db if and only if it is the new
         * record to be added or it is in the input db, and it is not the old
         * record.
         *)
        forall (gr':GroupRecord)
            (gr' in gdb') iff (((gr' = new_gr) or (gr' in gdb)) and
                                   (gr' != old_gr));

  end ChangeGroup;

  operation DeleteGroup is
    inputs: gdb:GroupDB, gr:GroupRecord;
    outputs: gdb':GroupDB;
    description: (*
        Delete the given group record from the given GroupDB.  The given record
        must already be in the input db.  Typically the user runs the FindGroup
        operation prior to Delete to locate an existing record to delete.
    *);

    precondition:
        (*
         * The given GroupRecord is in the given GroupDB.
         *)
        gr in gdb;

    postcondition:
        (*
         * A group record is in the output db if and only if it is not the
         * existing record to be deleted and it is in the input db.
         *)
        (forall (gr':GroupRecord)
            (gr' in gdb') iff ((gr' != gr) and (gr' in gdb)));

  end DeleteGroup;

  object LocationDB is
    components: LocationRecord*;
    description: (*

    *);
  end LocationDB;

  object LocationRecord is
    components: Name and Number and Uses and Remarks;
    description: (*

    *);
  end LocationRecord;


  (*
   * Global options TBD.
   *)

end Admin;





Prev: view.rsl | Next: options.rsl | Up: spec | Top: index