Skip to content

Commit

Permalink
Merge pull request #19 from Panopto/ms-graph-connector
Browse files Browse the repository at this point in the history
Microsoft Graph Connector support (beta)
  • Loading branch information
hohno-panopto authored Mar 11, 2022
2 parents a45dfc8 + 5a6bd10 commit 31c5e71
Show file tree
Hide file tree
Showing 14 changed files with 935 additions and 113 deletions.
1 change: 1 addition & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src\panoptoindexconnector\implementations\microsoft_graph_schema.json
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ Unless required by applicable law or agreed to in writing, software distributed

This product will connect Panopto to your external search index. This can be done by either leveraging a current connector implementation, or creating your own (more on implementations below). The following sections cover how to install and run the Panopto Index Connector, how to configure an implementation for your schema, and how to develop a custom implementation if need be.

## Installation
## Installation for Microsoft 365
Please follow [this document](https://docs.google.com/document/d/1CbnS4VnoKponmPx0CaxncpjPRpT7NEJ50XamLHTm4J4/edit?usp=sharing).

## Installation for platforms other than Microsoft 365

### Method 1: From official exe (recommended for out of the box)
If you do not need to create or customize a connector's implementation, and you are running on Windows, you may use the officially published `panopto-connector.exe` in the [releases tab](https://github.com/Panopto/panopto-index-connector/releases/). For other use cases, use methods 2 or 3.
Expand Down Expand Up @@ -259,6 +262,11 @@ field_mapping:

It assumes that Panopto user names map to Attivio user names, as will be the case in the presence of a shared identity provider. The Panopto API returns the username, identity provider, and email of each user with permission to search for a given video, and you may need to customize the permissions handling in `attivio_implementation.py` to match your Attivio configuration (see [below](building-or-customizing-an-implementation)).

### The Microsoft Graph Connector implementation

The Microsoft Graph Connector implementation, under `microsoft_graph_implementation.py`, works out of the box with Microsoft 365.
To configure this implementation, please refer [this document](https://docs.google.com/document/d/1CbnS4VnoKponmPx0CaxncpjPRpT7NEJ50XamLHTm4J4/edit?usp=sharing).

### Developing or customizing an implementation

When creating or customizing an implementation, you can start by copying either an existing implementation and its config file, or to start from scratch, copy the template (`template_implementation.py` and `template.yaml`). For this tutorial, we'll call our implementation `my_implementation`.
Expand Down
138 changes: 43 additions & 95 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,105 +1,53 @@
#
# This file is autogenerated by pip-compile with python 3.7
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file=requirements.txt requirements.in
#

-e .
# via -r requirements.in
astroid==2.4.2
# via pylint
atomicwrites==1.4.0
# via pytest
attrs==20.3.0
# via pytest
certifi==2020.12.5
# via requests
chardet==4.0.0
# via requests
click==7.1.2
# via pip-tools
colorama==0.4.4
# via
# pylint
# pytest
coverage==5.3
# via
# -r requirements.in
# pytest-cov
flake8==3.8.4
# via -r requirements.in
idna==2.10
# via requests
importlib-metadata==3.3.0
# via
# flake8
# pluggy
# pytest
iniconfig==1.1.1
# via pytest
isort==5.6.4
# via pylint
lazy-object-proxy==1.4.3
# via astroid
mccabe==0.6.1
# via
# flake8
# pylint
mock==4.0.3
# via -r requirements.in
packaging==20.8
# via pytest
pip-tools==5.4.0
# via -r requirements.in
pluggy==0.13.1
# via pytest
py==1.10.0
# via pytest
pycodestyle==2.6.0
# via
# -r requirements.in
# flake8
pyflakes==2.2.0
# via
# -r requirements.in
# flake8
pylint==2.6.0
# via -r requirements.in
pyparsing==2.4.7
# via packaging
pyreadline==2.1
# via panoptoindexconnector
pytest==6.2.1
# via
# -r requirements.in
# pytest-cov
pytest-cov==2.10.1
# via -r requirements.in
pytest-runner==5.2
# via -r requirements.in
requests==2.25.1
# via panoptoindexconnector
ruamel.yaml==0.15.50
# via panoptoindexconnector
six==1.15.0
# via
# astroid
# pip-tools
toml==0.10.2
# via
# pylint
# pytest
typed-ast==1.4.1
# via astroid
typing-extensions==3.7.4.3
# via importlib-metadata
urllib3==1.26.6
# via requests
wrapt==1.12.1
# via astroid
zipp==3.4.0
# via importlib-metadata
astroid==2.4.2 # via pylint
atomicwrites==1.4.0 # via pytest
attrs==20.3.0 # via pytest
certifi==2020.12.5 # via requests
cffi==1.15.0 # via cryptography
chardet==4.0.0 # via requests
click==7.1.2 # via pip-tools
colorama==0.4.4 # via pylint, pytest
coverage==5.3 # via -r requirements.in, pytest-cov
cryptography==36.0.0 # via msal, pyjwt
flake8==3.8.4 # via -r requirements.in
idna==2.10 # via requests
importlib-metadata==3.3.0 # via flake8, pluggy, pytest
iniconfig==1.1.1 # via pytest
isort==5.6.4 # via pylint
lazy-object-proxy==1.4.3 # via astroid
mccabe==0.6.1 # via flake8, pylint
mock==4.0.3 # via -r requirements.in
msal==1.16.0 # via panoptoindexconnector
packaging==20.8 # via pytest
pip-tools==5.4.0 # via -r requirements.in
pluggy==0.13.1 # via pytest
py==1.10.0 # via pytest
pycodestyle==2.6.0 # via -r requirements.in, flake8
pycparser==2.21 # via cffi
pyflakes==2.2.0 # via -r requirements.in, flake8
pyjwt[crypto]==2.3.0 # via msal
pylint==2.6.0 # via -r requirements.in
pyparsing==2.4.7 # via packaging
pyreadline==2.1 # via panoptoindexconnector
pytest-cov==2.10.1 # via -r requirements.in
pytest-runner==5.2 # via -r requirements.in
pytest==6.2.1 # via -r requirements.in, pytest-cov
requests==2.25.1 # via msal, panoptoindexconnector
ruamel.yaml==0.15.50 # via panoptoindexconnector
six==1.15.0 # via astroid, pip-tools
toml==0.10.2 # via pylint, pytest
typed-ast==1.4.1 # via astroid
typing-extensions==3.7.4.3 # via importlib-metadata
urllib3==1.26.6 # via requests
wrapt==1.12.1 # via astroid
zipp==3.4.0 # via importlib-metadata

# The following packages are considered to be unsafe in a requirements file:
# pip
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ def readme():
with open('README.md') as readme_file:
return readme_file.read()


REQUIRES = [
'msal',
'pyreadline',
'requests',
'ruamel.yaml<=15.66.0',
Expand All @@ -26,7 +28,7 @@ def readme():
author_email='[email protected]',
description=('A general application for connecting a panopto search index to an external source'),
long_description=readme(),
keywords=['python', 'panopto', 'connector', 'attivio', 'coveo'],
keywords=['python', 'panopto', 'connector', 'attivio', 'coveo', 'microsoft_graph'],

install_requires=REQUIRES,
package_data={
Expand Down
1 change: 1 addition & 0 deletions src/panoptoindexconnector/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
*.yaml
!attivio.yaml
!coveo.yaml
!microsoft_graph.yaml
!debug.yaml
!template.yaml
8 changes: 6 additions & 2 deletions src/panoptoindexconnector/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from panoptoindexconnector.connector_config import ConnectorConfig, InvalidConfiguration
from panoptoindexconnector.helpers import format_request_secure
from panoptoindexconnector.target_handler import TargetHandler
from panoptoindexconnector.custom_exceptions import CustomExceptions


# 2 minute grace period on oauth expiration
Expand Down Expand Up @@ -297,9 +298,9 @@ def sync(config, last_update_time):

new_last_update_time = last_update_time

handler.initialize()

try:
handler.initialize()

for _ in range(1000):
# Renew the oauth token if needed
oauth_token, expiration = renew_oauth_token_if_needed(
Expand Down Expand Up @@ -334,6 +335,9 @@ def sync(config, last_update_time):
except requests.exceptions.HTTPError as ex:
LOG.exception('Received error response %s | %s', ex.response.status_code, ex.response.text)
exception = ex
except CustomExceptions.QuotaLimitExceededError as ex:
# No need to log here since it will be logged in caller method ("run" method)
exception = ex
except Exception as ex: # pylint: disable=broad-except
LOG.exception('Received general exception')
exception = ex
Expand Down
5 changes: 5 additions & 0 deletions src/panoptoindexconnector/connector_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ def target_address(self):
def target_credentials(self):
return self._yaml_config['target_credentials']

@property
def target_connection(self):
return self._yaml_config['target_connection']

@property
def target_implementation(self):
return self._yaml_config['target_implementation']
Expand All @@ -127,5 +131,6 @@ class InvalidConfiguration(Exception):
"""
The configuration specified for the connector is not valid yaml
"""

def __init__(self, message):
super().__init__('Parse failure: ' + message)
12 changes: 12 additions & 0 deletions src/panoptoindexconnector/custom_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CustomExceptions:
"""
User-defined exceptions
"""

class Error(Exception):
"""Base class for other exceptions"""
pass

class QuotaLimitExceededError(Error):
"""Raised when the Tenant quota has exceeded"""
pass
1 change: 1 addition & 0 deletions src/panoptoindexconnector/implementations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
import panoptoindexconnector.implementations.coveo_implementation
import panoptoindexconnector.implementations.debug_implementation
import panoptoindexconnector.implementations.iterator_implementation
import panoptoindexconnector.implementations.microsoft_graph_implementation
import panoptoindexconnector.implementations.template_implementation # noqa
68 changes: 68 additions & 0 deletions src/panoptoindexconnector/implementations/microsoft_graph.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#
# Panopto index connector configuration file
#


# The address to your panopto site
panopto_site_address: https://your.site.panopto.com

# The oauth credentials to connect to the panopto API
panopto_oauth_credentials:
username: myconnectoruser
password: mypassword
client_id: 123
client_secret: 456
grant_type: password

# Your index integration target endpoint
target_address: https://graph.microsoft.com/v1.0/external/connections

# Your Microsoft graph data for the connector
target_credentials:
tenant_id: 00000000-0000-0000-0000-000000000000
client_id: 00000000-0000-0000-0000-000000000000
client_secret: myclientsecret
grant_type: client_credentials
authority: https://login.microsoftonline.com
scopes:
- https://graph.microsoft.com/.default

# Your Microsoft graph connection (connector) that will be used to push Panopto items to
target_connection:
# 'id' can only have ASCII alphanumeric characters and no empty spaces.
id: sampleConnectionId
name: Sample connection name
description: Sample connection description

# The name of your implementation
target_implementation: microsoft_graph_implementation

# Set to "true" if we should not push permissions to the target;
# often used with the principal_allowlist to control permissions by
# what is synced rather than matching the ID Provider on the target
skip_permissions: false

# Sleep time to avoid limitation between synced items per second.
# Panopto API allows 100 request per minute.
sleep_seconds: 0.6

# Define the mapping from Panopto fields to the target field names
field_mapping:

# Id in panopto maps to id in Microsoft graph
Id: id

# Top level data
Info:
Title: title
Url: uri
ThumbnailUrl: thumbnailUrl

# Content data
Metadata:
Folder: folder
Summary: description
MachineTranscription: machineTranscription
HumanTranscription: humanTranscription
ScreenCapture: screenCapture
Presentation: presentation
Loading

0 comments on commit 31c5e71

Please sign in to comment.