Skip to content

Commit

Permalink
Directory namespace & Intune API enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
vgrem committed Mar 15, 2023
1 parent 1bebf97 commit f067fc7
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 6 deletions.
14 changes: 14 additions & 0 deletions examples/onedrive/search_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
Use the Microsoft Search API in Microsoft Graph to search content stored in OneDrive or SharePoint:
files, folders, lists, list items, or sites.
https://learn.microsoft.com/en-us/graph/search-concept-files
"""
import json

from examples import acquire_token_by_username_password
from office365.graph_client import GraphClient

client = GraphClient(acquire_token_by_username_password)
result = client.search.query_drive_items("Guide.docx").execute_query()
print(json.dumps(result.value.to_json(), indent=4))

8 changes: 6 additions & 2 deletions examples/outlook/list_message.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"""
Get the messages in the signed-in user's mailbox
# The example is adapted from https://learn.microsoft.com/en-us/graph/api/user-list-messages
"""

from examples import acquire_token_by_username_password
from office365.graph_client import GraphClient

# The example is adapted from https://docs.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0
from office365.outlook.mail.messages.message import Message


Expand Down
7 changes: 7 additions & 0 deletions office365/directory/domains/dns_cname_record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from office365.directory.domains.dns_record import DomainDnsRecord


class DomainDnsCnameRecord(DomainDnsRecord):
"""
Represents a CNAME record added to the DNS zone file of a particular domain in the tenant.
"""
8 changes: 8 additions & 0 deletions office365/directory/domains/verified.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from office365.runtime.client_value import ClientValue


class VerifiedDomain(ClientValue):
"""
Specifies a domain for a tenant. The verifiedDomains property of the organization entity is a collection of
verifiedDomain objects.
"""
13 changes: 13 additions & 0 deletions office365/directory/identities/governance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from office365.entity import Entity


class IdentityGovernance(Entity):
"""
The identity governance singleton is the container for the following Azure Active Directory identity governance
features that are exposed through the following resources and APIs:
- Access reviews
- Entitlement management
- App consent
- Terms of use
"""
57 changes: 57 additions & 0 deletions office365/directory/permissions/grants/oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,60 @@ class OAuth2PermissionGrant(Entity):
Delegated permissions are sometimes referred to as "OAuth 2.0 scopes" or "scopes".
"""

@property
def client_id(self):
"""
The id of the client service principal for the application which is authorized to act on behalf of a signed-in
user when accessing an API. Required. Supports $filter (eq only).
:rtype: str
"""
return self.properties.get("clientId", None)

@property
def consent_type(self):
"""
Indicates if authorization is granted for the client application to impersonate all users or only a
specific user. AllPrincipals indicates authorization to impersonate all users.
Principal indicates authorization to impersonate a specific user. Consent on behalf of all users can be granted
by an administrator. Non-admin users may be authorized to consent on behalf of themselves in some cases,
for some delegated permissions. Required. Supports $filter (eq only).
:rtype: str
"""
return self.properties.get("consentType", None)

@property
def principal_id(self):
"""
The id of the user on behalf of whom the client is authorized to access the resource, when consentType is
Principal. If consentType is AllPrincipals this value is null. Required when consentType is Principal.
Supports $filter (eq only).
:rtype: str
"""
return self.properties.get("principalId", None)

@property
def resource_id(self):
"""
The id of the resource service principal to which access is authorized. This identifies the API which the
client is authorized to attempt to call on behalf of a signed-in user. Supports $filter (eq only).
:rtype: str
"""
return self.properties.get("resourceId", None)

@property
def scope(self):
"""
A space-separated list of the claim values for delegated permissions which should be included in access tokens
for the resource application (the API). For example, openid User.Read GroupMember.Read.All. Each claim value
should match the value field of one of the delegated permissions defined by the API, listed in the
oauth2PermissionScopes property of the resource service principal. Must not exceed 3850 characters in length.
:rtype: str
"""
return self.properties.get("scope", None)

19 changes: 18 additions & 1 deletion office365/directory/users/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,21 @@ def oauth2_permission_grants(self):
DeltaCollection(self.context, OAuth2PermissionGrant,
ResourcePath("oauth2PermissionGrants", self.resource_path)))

@property
def owned_devices(self):
"""Devices that are owned by the user. Read-only. Nullable. Supports $expand and $filter
(/$count eq 0, /$count ne 0, /$count eq 1, /$count ne 1). """
return self.properties.get('ownedDevices',
DirectoryObjectCollection(self.context,
ResourcePath("ownedDevices", self.resource_path)))

@property
def owned_objects(self):
"""Directory objects that are owned by the user. Read-only. Nullable. Supports $expand. """
return self.properties.get('ownedObjects',
DirectoryObjectCollection(self.context,
ResourcePath("ownedObjects", self.resource_path)))

@property
def transitive_member_of(self):
"""Get groups, directory roles that the user is a member of. This API request is transitive, and will also
Expand Down Expand Up @@ -604,7 +619,9 @@ def get_property(self, name, default_value=None):
"mailboxSettings": self.mailbox_settings,
"directReports": self.direct_reports,
"onlineMeetings": self.online_meetings,
"oauth2PermissionGrants": self.oauth2_permission_grants
"oauth2PermissionGrants": self.oauth2_permission_grants,
"ownedDevices": self.owned_devices,
"ownedObjects": self.owned_objects
}
default_value = property_mapping.get(name, None)
return super(User, self).get_property(name, default_value)
Expand Down
8 changes: 8 additions & 0 deletions office365/graph_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from office365.directory.audit.log_root import AuditLogRoot
from office365.directory.directory import Directory
from office365.directory.domains.domain import Domain
from office365.directory.identities.governance import IdentityGovernance
from office365.directory.identities.protection_root import IdentityProtectionRoot
from office365.directory.object_collection import DirectoryObjectCollection
from office365.directory.groups.collection import GroupCollection
Expand Down Expand Up @@ -241,6 +242,13 @@ def communications(self):
"""
return CloudCommunications(self, ResourcePath("communications"))

@property
def identity_governance(self):
"""
The identity governance singleton
"""
return IdentityGovernance(self, ResourcePath("identityGovernance"))

@property
def subscriptions(self):
"""
Expand Down
16 changes: 16 additions & 0 deletions office365/intune/organizations/branding_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from office365.entity import Entity


class OrganizationalBrandingProperties(Entity):
"""
An abstract type that exposes properties for configuring an organization's branding.
Organizations can customize their Azure Active Directory (Azure AD) sign-in pages which appear when users sign in to
their organization's tenant-specific apps, or when Azure AD identifies the user's tenant from their username.
A developer can also read the company's branding information and customize their app experience to tailor
it specifically for the signed-in user using their company's branding.
You can't change your original configuration's language as represented by the organizationalBranding object.
However, companies can add different branding based on locale. For language-specific branding,
see the organizationalBrandingLocalization object.
"""
9 changes: 8 additions & 1 deletion office365/intune/organizations/organization.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from office365.directory.certificates.auth_configuration import CertificateBasedAuthConfiguration
from office365.directory.domains.verified import VerifiedDomain
from office365.directory.object import DirectoryObject
from office365.directory.extensions.extension import Extension
from office365.entity_collection import EntityCollection
Expand Down Expand Up @@ -42,12 +43,18 @@ def certificate_based_auth_configuration(self):
def provisioned_plans(self):
return self.properties.get("provisionedPlans", ClientValueCollection(ProvisionedPlan))

@property
def verified_domains(self):
"""The collection of domains associated with this tenant."""
return self.properties.get("verifiedDomains", ClientValueCollection(VerifiedDomain))

def get_property(self, name, default_value=None):
if default_value is None:
property_mapping = {
"certificateBasedAuthConfiguration": self.certificate_based_auth_configuration,
"businessPhones": self.business_phones,
"provisionedPlans": self.provisioned_plans
"provisionedPlans": self.provisioned_plans,
"verifiedDomains": self.verified_domains
}
default_value = property_mapping.get(name, None)
return super(Organization, self).get_property(name, default_value)
5 changes: 4 additions & 1 deletion office365/onedrive/drives/recipient.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ class DriveRecipient(ClientValue):

def __init__(self, alias=None, email=None, object_id=None):
"""
:param str alias: The alias of the domain object, for cases where an email address is unavailable
(e.g. security groups).
:param str email: The email address for the recipient, if the recipient has an associated email address.
:param str object_id: The unique identifier for the recipient in the directory.
"""
super(DriveRecipient, self).__init__()
self.alias = alias
Expand Down
40 changes: 40 additions & 0 deletions office365/outlook/mail/conversation_thread.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,53 @@
from office365.entity import Entity
from office365.entity_collection import EntityCollection
from office365.outlook.mail.post import Post
from office365.outlook.mail.recipient import Recipient
from office365.runtime.client_value_collection import ClientValueCollection
from office365.runtime.paths.resource_path import ResourcePath
from office365.runtime.queries.service_operation import ServiceOperationQuery


class ConversationThread(Entity):
"""A conversationThread is a collection of posts."""


def reply(self, post):
"""Reply to a thread in a group conversation and add a new post to it. You can specify the parent conversation
in the request, or, you can specify just the thread without the parent conversation.
:param Post post: A comment to include. Can be an empty string.
"""
payload = {"post": post}
qry = ServiceOperationQuery(self, "reply", None, payload)
self.context.add_query(qry)
return self

@property
def cc_recipients(self):
"""The Cc: recipients for the thread."""
return self.properties.get("ccRecipients", ClientValueCollection(Recipient))

@property
def has_attachments(self):
"""Indicates whether any of the posts within this thread has at least one attachment."""
return self.properties.get("hasAttachments", None)

@property
def to_recipients(self):
"""The To: recipients for the thread."""
return self.properties.get("toRecipients", ClientValueCollection(Recipient))


@property
def posts(self):
return self.properties.get('posts',
EntityCollection(self.context, Post, ResourcePath("posts", self.resource_path)))

def get_property(self, name, default_value=None):
if default_value is None:
property_mapping = {
"ccRecipients": self.cc_recipients,
"toRecipients": self.to_recipients
}
default_value = property_mapping.get(name, None)
return super(ConversationThread, self).get_property(name, default_value)
6 changes: 6 additions & 0 deletions office365/search/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,11 @@ def query_events(self, query_string):
"""
return self.query(query_string, entity_types=[EntityType.event])

def query_drive_items(self, query_string):
"""Searches OneDrive items.
Alias to query method
:param str query_string: Contains the query terms.
"""
return self.query(query_string, entity_types=[EntityType.driveItem])

3 changes: 2 additions & 1 deletion office365/search/response.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from office365.runtime.client_value import ClientValue
from office365.runtime.client_value_collection import ClientValueCollection
from office365.runtime.types.collections import StringCollection
from office365.search.hits_container import SearchHitsContainer


Expand All @@ -13,5 +14,5 @@ def __init__(self, search_terms=None, hits_containers=None):
"""
super(SearchResponse, self).__init__()
self.searchTerms = search_terms
self.searchTerms = StringCollection(search_terms)
self.hitsContainers = ClientValueCollection(SearchHitsContainer, hits_containers)
4 changes: 4 additions & 0 deletions tests/directory/test_organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ def setUpClass(cls):
def test1_list(self):
org = self.client.organization.get().execute_query()
self.assertIsNotNone(org.resource_path)

def test2_list_contacts(self):
result = self.client.contacts.get().top(10).execute_query()
self.assertIsNotNone(result.resource_path)

0 comments on commit f067fc7

Please sign in to comment.