syntax = "proto3";

package com.owncloud.ocis.accounts.v0;

option go_package = "github.com/owncloud/ocis/accounts/pkg/proto/v0;proto";

import "google/api/field_behavior.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";

import "protoc-gen-openapiv2/options/annotations.proto";

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
  info: {
    title: "ownCloud Infinite Scale accounts";
    version: "1.0.0";
    contact: {
      name: "ownCloud GmbH";
      url: "https://github.com/owncloud/ocis";
      email: "support@owncloud.com";
    };
    license: {
      name: "Apache-2.0";
      url: "https://github.com/owncloud/ocis/blob/master/LICENSE";
    };
  };
  schemes: HTTP;
  schemes: HTTPS;
  consumes: "application/json";
  produces: "application/json";
  external_docs: {
    description: "Developer Manual";
    url: "https://owncloud.dev/extensions/accounts/";
  };
};

// Follow recommended Methods for rpc APIs https://cloud.google.com/apis/design/resources?hl=de#methods
// https://cloud.google.com/apis/design/standard_methods?hl=de#list
// https://cloud.google.com/apis/design/naming_convention?hl=de
service AccountsService {
    // Lists accounts
    rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse) {
        // List method maps to HTTP GET
        option (google.api.http) = {
            post: "/api/v0/accounts/accounts-list",
            body: "*"
        };
    }
    // Gets an account
    rpc GetAccount(GetAccountRequest) returns (Account) {
        option (google.api.http) = {
            post: "/api/v0/accounts/accounts-get",
            body: "*"
        };
    }
    // Creates an account
    rpc CreateAccount(CreateAccountRequest) returns (Account) {
        // Create maps to HTTP POST. URL path as the collection name.
        // HTTP request body contains the resource
        option (google.api.http) = {
            post: "/api/v0/accounts/accounts-create"
            body: "*"
        };
    }
    // Updates an account
    rpc UpdateAccount(UpdateAccountRequest) returns (Account) {
        // Update maps to HTTP PATCH. Resource name is mapped to a URL path.
        // Resource is contained in the HTTP request body
        option (google.api.http) = {
            post: "/api/v0/accounts/accounts-update"
            body: "*"
        };
    };
    // Deletes an account
    rpc DeleteAccount(DeleteAccountRequest) returns (google.protobuf.Empty) {
        // Delete maps to HTTP DELETE. Resource name maps to the URL path.
        // There is no request body
        option (google.api.http) = {
            post: "/api/v0/accounts/accounts-delete",
            body: "*"
        };
    }
}

service GroupsService {
    // Lists groups
    rpc ListGroups(ListGroupsRequest) returns (ListGroupsResponse) {
        // List method maps to HTTP GET
        option (google.api.http) = {
            post: "/api/v0/accounts/groups-list",
            body: "*"
        };
    }
    // Gets an groups
    rpc GetGroup(GetGroupRequest) returns (Group) {
        option (google.api.http) = {
            post: "/api/v0/accounts/groups-get",
            body: "*"
        };
    }
    // Creates a group
    rpc CreateGroup(CreateGroupRequest) returns (Group) {
        // Create maps to HTTP POST. URL path as the collection name.
        // HTTP request body contains the resource
        option (google.api.http) = {
            post: "/api/v0/accounts/groups-create"
            body: "*"
        };
    }
    // Updates a group
    rpc UpdateGroup(UpdateGroupRequest) returns (Group) {
        // Update maps to HTTP PATCH. Resource name is mapped to a URL path.
        // Resource is contained in the HTTP request body
        option (google.api.http) = {
            post: "/api/v0/accounts/groups-update"
            body: "*"
        };
    };
    // Deletes a group
    rpc DeleteGroup(DeleteGroupRequest) returns (google.protobuf.Empty) {
        // Delete maps to HTTP DELETE. Resource name maps to the URL path.
        // There is no request body
        option (google.api.http) = {
            post: "/api/v0/accounts/groups-delete",
            body: "*"
        };
    }

    // additional group methods: https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#methods

    // references are accessed using $ref, see http://docs.oasis-open.org/odata/odata/v4.0/cs01/part2-url-conventions/odata-v4.0-cs01-part2-url-conventions.html#_Toc365046422
    // or the stack overflow question https://stackoverflow.com/questions/49362894/why-is-the-microsoft-graph-api-using-ref-in-the-uri

    // group:addmember https://docs.microsoft.com/en-us/graph/api/group-post-members?view=graph-rest-1.0&tabs=http
    rpc AddMember(AddMemberRequest) returns (Group) {
        //  All request parameters go into body.
        option (google.api.http) = {
            post: "/api/v0/groups/{group_id=*}/members/$ref"
            body: "*"
        };
    }
    // group:removemember https://docs.microsoft.com/en-us/graph/api/group-delete-members?view=graph-rest-1.0
    rpc RemoveMember(RemoveMemberRequest) returns (Group) {
        //  All request parameters go into body.
        option (google.api.http) = {
            // URLs are broken
            post: "/api/v0/groups/{group_id=*}/members/{account_id}/$ref"
            body: "*"
        };
    }
    // group:listmembers https://docs.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0
    rpc ListMembers(ListMembersRequest) returns (ListMembersResponse) {
        //  All request parameters go into body.
        option (google.api.http) = {
            // URLs are broken
            post: "/api/v0/groups/{id=*}/members/$ref"
            body: "*"
        };
    }

}

service IndexService {
    rpc RebuildIndex(RebuildIndexRequest) returns (RebuildIndexResponse) {
        //  All request parameters go into body.
        option (google.api.http) = {
            // URLs are broken
            post: "/api/v0/index/rebuild"
            body: "*"
        };
    }
}

message RebuildIndexRequest {
}

message RebuildIndexResponse {
}

message ListAccountsRequest {
    // Optional. The maximum number of accounts to return in the response
    int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL];

    // Optional. A pagination token returned from a previous call to `Get`
    // that indicates from where search should continue
    string page_token = 2 [(google.api.field_behavior) = OPTIONAL];

    // Optional. Used to specify a subset of fields that should be
    // returned by a get operation or modified by an update operation.
    google.protobuf.FieldMask field_mask = 3;

    // Optional. Search criteria used to select the accounts to return.
    // If no search criteria is specified then all accounts will be
    // returned
    //
    // TODO update query language
    // Query expressions can be used to restrict results based upon
    // the account properties where the operators `=`, `NOT`, `AND` and `OR`
    // can be used along with the suffix wildcard symbol `*`.
    //
    // The string properties in a query expression should use escaped quotes
    // for values that include whitespace to prevent unexpected behavior.
    //
    // Some example queries are:
    //
    // * Query `display_name=Th*` returns accounts whose display_name
    // starts with "Th"
    // * Query `email=foo@example.com` returns accounts with
    // `email` set to `foo@example.com`
    // * Query `display_name=\\"Test String\\"` returns accounts with
    // display names that include both "Test" and "String"
    string query = 4 [(google.api.field_behavior) = OPTIONAL];
}

message ListAccountsResponse {
    // The field name should match the noun "accounts" in the method name.  There
    // will be a maximum number of items returned based on the page_size field
    // in the request
    repeated Account accounts = 1;

    // Token to retrieve the next page of results, or empty if there are no
    // more results in the list
    string next_page_token = 2;
}

message GetAccountRequest {
    string id = 1;
}

message CreateAccountRequest {
    // The account resource to create
    Account account = 1;
}

message UpdateAccountRequest {
    // The account resource which replaces the resource on the server
    Account account = 1;

    // The update mask applies to the resource. For the `FieldMask` definition,
    // see https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask
    google.protobuf.FieldMask update_mask = 2;
  }

message DeleteAccountRequest {
    string id = 1;
}

// Account follows the properties of the ms graph api user resource.
// See https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties
message Account {

    // Note that field numbers in the range 1 through 15 take one byte to encode, including the field number and the field's type

    // The unique identifier for the user. Key. Not nullable. Non reassignable. Read-only.
    string id = 1;

    // *true* if the account is enabled; otherwise, *false*. This property is required when a user is created. Supports $filter.
    bool account_enabled = 2;

    // A resource account is also known as a /disabled user object/ in Azure AD, and can be used to represent resources in general.
    // In Exchange it might be used to represent conference rooms, for example, and allow them to have a phone number.
    // You could give printers or machines with a sync client resource accounts as well.
    // A resource account can be homed in Microsoft 365 or on premises using Skype for Business Server 2019.
    // *true* if the user is a resource account; otherwise, *false*. Null value should be considered false.
    bool is_resource_account = 3;

    // Indicates whether the account was created as
    // - a regular school or work account ("" / emptystring),
    // - a local account, fully managed by oCIS (LocalAccount), includes synced accounts or
    // - an external account (Invitation),
    // - self-service sign-up using email verification (EmailVerified). Read-only.
    string creation_type = 4;

    // Represents the identities that can be used to sign in to this account.
    // An identity can be provided by oCIS (also known as a local account), by organizations, or by social identity providers such as Facebook, Google, and Microsoft, and is tied to an account.
    // May contain multiple items with the same signInType value. Supports $filter.
    repeated Identities identities = 5;

    // posixaccount properties

    // The name displayed in the address book for the account.
    // This is usually the combination of the user's first name, middle initial and last name.
    // This property is required when a user is created and it cannot be cleared during updates.
    // Supports $filter and $orderby.
    // posixaccount MUST cn
    string display_name = 6;

    // The username
    // posixaccount MUST uid
    string preferred_name = 7;

    // TODO rename to on_premise_? or move to extension? see https://docs.microsoft.com/en-us/graph/extensibility-open-users
    // used for exposing the user using ldap
    // posixaccount MUST uidnumber
    int64 uid_number = 8;

    // used for exposing the user using ldap
    // posixaccount MUST gidnumber
    int64 gid_number = 9;

    // posixaccount MUST homedirectory
    // constructed dynamically in glauth

    // posixaccount MAY loginshell
    // posixaccount MAY gecos

    // For the GECOS field:
    // - User's full name (or application name, if the account is for a program) -> displayname (description)
    // - Building and room number or contact person -> -
    // - Office telephone number -> -
    // - Any other contact information (pager number, fax, etc.) -> mail
    // eg "Albert Einstein (Beware! I'm doing science!),,,einstein@example.com" as the gecos

    // The SMTP address for the user, for example, "jeff@contoso.onmicrosoft.com". Read-Only. Supports $filter.
    // inetorgperson MAY mail
    string mail = 10;

    // A description, useful for resource accounts
    // posixaccount MAY description
    string description = 11;

    // Specifies the password profile for the user.
    // The profile contains the user’s password. This property is required when a user is created.
    // The password in the profile must satisfy minimum requirements as specified by the passwordPolicies property.
    // By default, a strong password is required.
    // posixaccount MAY authPassword
    PasswordProfile password_profile = 12;

    // The groups, directory roles and administrative units that the user is a member of. Read-only. Nullable.
    // should we only respond with repeated strings of ids? no clients should a proper filter mask!
    repeated Group memberOf = 13;

    // Field numbers in the range 16 through 2047 take two bytes. So you should reserve the field numbers 1 through 15 for very frequently occurring message elements. Remember to leave some room for frequently occurring elements that might be added in the future.

    // The created date of the account object.
    google.protobuf.Timestamp created_date_time = 16;

    // The date and time the user was deleted. Returned only on $select.
    google.protobuf.Timestamp deleted_date_time = 17;

    // properties for sync

    // *true* if this object is synced from an on-premises directory;
    // *false* if this object was originally synced from an on-premises directory but is no longer synced;
    // null if this object has never been synced from an on-premises directory (default). Read-only
    bool on_premises_sync_enabled = 20;

    // This property is used to associate an on-premises LDAP user to the oCIS account object.
    // This property must be specified when creating a new user account in the Graph if you are using a federated domain for the user’s userPrincipalName (UPN) property.
    // Important: The $ and _ characters cannot be used when specifying this property. Supports $filter.
    string on_premises_immutable_id = 21;

    // Contains the on-premises security identifier (SID) for the user that was synchronized from on-premises to the cloud. Read-only.
    string on_premises_security_identifier = 22;

    // Contains the on-premises LDAP `distinguished name` or `DN`.
    // The property is only populated for customers who are synchronizing their on-premises directory to ocis-accounts. Read-only.
    string on_premises_distinguished_name = 23;

    // Contains the on-premises `samAccountName` synchronized from the on-premises directory.
    // The property is only populated for customers who are synchronizing their on-premises directory to ocis-accounts. Read-only.
    string on_premises_sam_account_name = 24;

    // Contains the on-premises `domainFQDN`, also called `dnsDomainName` synchronized from the on-premises directory
    // The property is only populated for customers who are synchronizing their on-premises directory to ocis-accounts. Read-only.
    string on_premises_domain_name = 25;

    // Contains the on-premises userPrincipalName synchronized from the on-premises directory.
    // The property is only populated for customers who are synchronizing their on-premises directory to ocis-accounts. Read-only.
    string on_premises_user_principal_name = 26;

    // Contains extensionAttributes 1-15 for the user. Note that the individual extension attributes are neither selectable nor filterable.
    // For an onPremisesSyncEnabled user, this set of properties is mastered on-premises and is read-only.
    // For a cloud-only user (where onPremisesSyncEnabled is false), these properties may be set during creation or update.
    //onPremisesExtensionAttributes onPremisesExtensionAttributes = 27

    // Indicates the last time at which the object was synced with the on-premises directory; Read-only.
    google.protobuf.Timestamp on_premises_last_sync_date_time = 28;

    // Errors when using synchronization during provisioning.
    repeated OnPremisesProvisioningError on_premises_provisioning_errors = 29;

    // TODO do we need to sync uidnumber and gidnumber from the external ldap? we may assign different numbers so eos has a non colliding namespace.
    // we can sync them to the (uid|gid)_number fields but what if they changed in the external ldap and now collide?
    // move those properties to a posixaccount message so we can exchange that part with an external (readonly or writeable) ldap server?

    // For an external user invited to the tenant using the invitation API, this property represents the invited user's invitation status.
    // For invited users, the state can be `PendingAcceptance` or `Accepted`, or "" / emptystring for all other users.
    // Returned only on $select. Supports $filter with the supported values. For example: $filter=externalUserState eq 'PendingAcceptance'.
    string external_user_state = 40;

    // Shows the timestamp for the latest change to the externalUserState property. Returned only on $select.
    google.protobuf.Timestamp external_user_state_change_date_time = 41;

    // Any refresh tokens or sessions tokens (session cookies) issued before this time are invalid, and applications will get
    // an error when using an invalid refresh or sessions token to acquire a delegated access token (to access APIs such as Microsoft Graph).
    // If this happens, the application will need to acquire a new refresh token by making a request to the authorize endpoint.
    // Returned only on $select. Read-only. Use invalidateAllRefreshTokens to reset.
    google.protobuf.Timestamp refresh_tokens_valid_from_date_time = 60;

    // Any refresh tokens or sessions tokens (session cookies) issued before this time are invalid, and applications will get
    // an error when using an invalid refresh or sessions token to acquire a delegated access token (to access APIs such as Microsoft Graph).
    // If this happens, the application will need to acquire a new refresh token by making a request to the authorize endpoint.
    // Read-only. Use revokeSignInSessions to reset.
    google.protobuf.Timestamp sign_in_sessions_valid_from_date_time = 61;
}

// Identities Represents an identity used to sign in to a user account.
// An identity can be provided by oCIS, by organizations, or by social identity providers such as Facebook, Google, or Microsoft, that are tied to a user account.
// This enables the user to sign in to the user account with any of those associated identities.
// They are also used to keep a history of old usernames.
message Identities {
    // Specifies the user sign-in types in your directory, such as `emailAddress`, `userName` or `federated`.
    // Here, federated represents a unique identifier for a user from an issuer, that can be in any format chosen by the issuer.
    // Additional validation is enforced on *issuer_assigned_id* when the sign-in type is set to `emailAddress` or `userName`.
    // This property can also be set to any custom string.
    string sign_in_type = 1;

    // Specifies the issuer of the identity, for example facebook.com.
    // For local accounts (where signInType is not federated), this property is
    // the local B2C tenant default domain name, for example contoso.onmicrosoft.com.
    // For external users from other Azure AD organization, this will be the domain of
    // the federated organization, for example contoso.com.
    // Supports $filter. 512 character limit.
    string issuer = 2;

    // Specifies the unique identifier assigned to the user by the issuer. The combination of *issuer* and *issuerAssignedId* must be unique within the organization. Represents the sign-in name for the user, when signInType is set to emailAddress or userName (also known as local accounts).
    // When *signInType* is set to:
    // * `emailAddress`, (or starts with `emailAddress` like `emailAddress1`) *issuerAssignedId* must be a valid email address
    // * `userName`, issuer_assigned_id must be a valid local part of an email address
    // Supports $filter. 512 character limit.
    string issuer_assigned_id = 3;
}
message PasswordProfile {
    //	The password for the user. This property is required when a user is created.
    // It can be updated, but the user will be required to change the password on the next login.
    // The password must satisfy minimum requirements as specified by the user’s passwordPolicies property. By default, a strong password is required.
    string password = 1;

    // The time when this account last changed their password.
    google.protobuf.Timestamp last_password_change_date_time = 2;

    // Specifies password policies for the user.
    //This value is an enumeration with one possible value being “DisableStrongPassword”, which allows weaker passwords than the default policy to be specified.
    // “DisablePasswordExpiration” can also be specified.
    repeated string password_policies = 3;

    // *true* if the user must change her password on the next login; otherwise false.
    bool force_change_password_next_sign_in = 4;

    // If *true*, at next sign-in, the user must perform a multi-factor authentication (MFA) before being forced to change their password. The behavior is identical to forceChangePasswordNextSignIn except that the user is required to first perform a multi-factor authentication before password change. After a password change, this property will be automatically reset to false. If not set, default is false.
    bool force_change_password_next_sign_in_with_mfa = 5;
}

message ListGroupsRequest {
    // Optional. The maximum number of groups to return in the response
    int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL];

    // Optional. A pagination token returned from a previous call to `Get`
    // that indicates from where search should continue
    string page_token = 2 [(google.api.field_behavior) = OPTIONAL];

    // Optional. Used to specify a subset of fields that should be
    // returned by a get operation or modified by an update operation.
    google.protobuf.FieldMask field_mask = 3;

    // Optional. Search criteria used to select the groups to return.
    // If no search criteria is specified then all groups will be
    // returned
    //
    // TODO update query language
    // Query expressions can be used to restrict results based upon
    // the account properties where the operators `=`, `NOT`, `AND` and `OR`
    // can be used along with the suffix wildcard symbol `*`.
    //
    // The string properties in a query expression should use escaped quotes
    // for values that include whitespace to prevent unexpected behavior.
    //
    // Some example queries are:
    //
    // * Query `display_name=Th*` returns accounts whose display_name
    // starts with "Th"
    // * Query `display_name=\\"Test String\\"` returns groups with
    // display names that include both "Test" and "String"
    string query = 4 [(google.api.field_behavior) = OPTIONAL];
}

message ListGroupsResponse {
    // The field name should match the noun "group" in the method name.  There
    // will be a maximum number of items returned based on the page_size field
    // in the request
    repeated Group groups = 1;

    // Token to retrieve the next page of results, or empty if there are no
    // more results in the list
    string next_page_token = 2;
}

message GetGroupRequest {
    string id = 1;
}

message CreateGroupRequest {
    // The account resource to create
    Group group = 1;
}

message UpdateGroupRequest {
    // The group resource which replaces the resource on the server
    Group group = 1;

    // The update mask applies to the resource. For the `FieldMask` definition,
    // see https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask
    google.protobuf.FieldMask update_mask = 2;
  }

message DeleteGroupRequest {
    string id = 1;
}

message AddMemberRequest {
    // The id of the group to add a member to
    string group_id = 1;
    // The account id to add
    string account_id = 2;
}

message RemoveMemberRequest {
    // The id of the group to remove a member from
    string group_id = 1;
    // The account id to remove
    string account_id = 2;
}

message ListMembersRequest {
    int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL];

    // Optional. A pagination token returned from a previous call to `Get`
    // that indicates from where search should continue
    string page_token = 2 [(google.api.field_behavior) = OPTIONAL];

    // Optional. Used to specify a subset of fields that should be
    // returned by a get operation or modified by an update operation.
    google.protobuf.FieldMask field_mask = 3;

    // Optional. Search criteria used to select the groups to return.
    // If no search criteria is specified then all groups will be
    // returned
    //
    // TODO update query language
    // Query expressions can be used to restrict results based upon
    // the account properties where the operators `=`, `NOT`, `AND` and `OR`
    // can be used along with the suffix wildcard symbol `*`.
    //
    // The string properties in a query expression should use escaped quotes
    // for values that include whitespace to prevent unexpected behavior.
    //
    // Some example queries are:
    //
    // * Query `display_name=Th*` returns accounts whose display_name
    // starts with "Th"
    // * Query `display_name=\\"Test String\\"` returns groups with
    // display names that include both "Test" and "String"
    string query = 4 [(google.api.field_behavior) = OPTIONAL];

    // The id of the group to list members from
    string id = 5;
}

message ListMembersResponse {
    // The field name should match the noun "members" in the method name.  There
    // will be a maximum number of items returned based on the page_size field
    // in the request
    repeated Account members = 1;

    // Token to retrieve the next page of results, or empty if there are no
    // more results in the list
    string next_page_token = 2;
}

message Group {

    // The unique identifier for the group.
    // Returned by default. Inherited from directoryObject. Key. Not nullable. Read-only.
    string id = 1;

    // groupofnames

    // The display name for the group. This property is required when a group is created and cannot be cleared during updates.
    // Returned by default. Supports $filter and $orderby.
    // groupofnames MUST cn
    string display_name = 2;
    // groupofnames MUST/MAY member

    // Users, contacts, and groups that are members of this group. HTTP Methods: GET (supported for all groups), POST (supported for security groups and mail-enabled security groups), DELETE (supported only for security groups) Read-only. Nullable.
    // TODO accounts (users) only for now, we can add groups with the dedicated message using oneof construct later
    repeated Account members = 3;

    // groupofnames MAY businessCategory
    // groupofnames MAY o
    // groupofnames MAY ou
    // groupofnames MAY owner, SINGLE-VALUE but there might be multiple owners
    repeated Account owners = 4;

    // An optional description for the group. Returned by default.
    // groupofnames MAY description
    string description = 5;

    // groupofnames MAY seeAlso

    // posixgroup

    // used for exposing the user using ldap
    // posixgroup MUST gidnumber
    int64 gid_number = 6;
    // posixgroup MAY authPassword
    // posixgroup MAY userPassword
    // posixgroup MAY memberUid -> groupofnames member
    // posixgroup MAY description  -> groupofnames


    // Timestamp of when the group was created. The value cannot be modified and is automatically populated when the group is created
    // Returned by default. Read-only.
    google.protobuf.Timestamp created_date_time = 7;

    // For some Azure Active Directory objects (user, group, application), if the object is deleted, it is first logically deleted, and this property is updated with the date and time when the object was deleted. Otherwise this property is null. If the object is restored, this property is updated to null.
    // Returned by default. Read-only.
    google.protobuf.Timestamp deleted_date_time = 8;

    // Timestamp of when the group is set to expire. The value cannot be modified and is automatically populated when the group is created.
    // Returned by default. Read-only.
    google.protobuf.Timestamp expiration_date_time = 9;

    // True if the group is not displayed in certain parts of the Outlook user interface:
    // in the Address Book, in address lists for selecting message recipients, and in the Browse Groups dialog for searching groups; false otherwise. Default value is false.
    // Returned only on $select.
    bool hide_from_address_lists = 10;

    // 	Specifies the visibility of an Office 365 group. Possible values are: Private, Public, or Hiddenmembership; blank values are treated as public. See group visibility options to learn more.
    // Visibility can be set only when a group is created; it is not editable.
    // Returned by default.
    string visibility = 11;

    // Field numbers in the range 16 through 2047 take two bytes. So you should reserve the field numbers 1 through 15 for very frequently occurring message elements. Remember to leave some room for frequently occurring elements that might be added in the future.

    // properties for sync

    // *true* if this group is synced from an on-premises directory;
    // *false* if this group was originally synced from an on-premises directory but is no longer synced;
    // null if this object has never been synced from an on-premises directory (default).
    // Returned by default. Read-only. Supports $filter.
    bool on_premises_sync_enabled = 20;

    // This property is used to associate an on-premises LDAP user to the oCIS account object.
    // This property must be specified when creating a new user account in the Graph if you are using a federated domain for the user’s userPrincipalName (UPN) property.
    // Important: The $ and _ characters cannot be used when specifying this property. Supports $filter.
    string on_premises_immutable_id = 21;

    // Contains the on-premises security identifier (SID) for the group that was synchronized from on-premises to the cloud. Returned by default. Read-only.
    string on_premises_security_identifier = 22;

    // Contains the on-premises LDAP `distinguished name` or `DN`.
    // The property is only populated for customers who are synchronizing their on-premises directory to ocis-accounts. Read-only.
    string on_premises_distinguished_name = 23;

    // Contains the on-premises `samAccountName` synchronized from the on-premises directory.
    // The property is only populated for customers who are synchronizing their on-premises directory to ocis-accounts. Returned by default. Read-only.
    string on_premises_sam_account_name = 24;

    // Contains the on-premises domain FQDN, also called dnsDomainName synchronized from the on-premises directory. The property is only populated for customers who are synchronizing their on-premises directory to Azure Active Directory via Azure AD Connect.
    // Returned by default. Read-only.
    string on_premises_domain_name = 25;

    // 	Contains the on-premises netBios name synchronized from the on-premises directory. The property is only populated for customers who are synchronizing their on-premises directory to Azure Active Directory via Azure AD Connect.
    // Returned by default. Read-only.
    string on_premises_net_bios_name = 26;

    // Indicates the last time at which the group was synced with the on-premises directory.
    // Returned by default. Read-only. Supports $filter.
    string on_premises_last_sync_date_time = 27;

    // Errors when using synchronization during provisioning.
    repeated OnPremisesProvisioningError on_premises_provisioning_errors = 28;
}

message OnPremisesProvisioningError {
    // Category of the provisioning error. Note: Currently, there is only one possible value. Possible value: PropertyConflict - indicates a property value is not unique. Other objects contain the same value for the property.
    string category = 1;
    // The date and time at which the error occurred.
    google.protobuf.Timestamp occurred_date_time = 2;
    // Name of the directory property causing the error. Current possible values: UserPrincipalName or ProxyAddress
    string property_causing_error = 3;
    // Value of the property causing the error.
    string value = 4;
}
