13
13
14
14
15
15
class MicroDict (dict ):
16
- def __getitem__ (self , key ):
17
- result = super (MicroDict , self ).get (key [:1 ].lower () + key [1 :], None )
18
- if result is None :
19
- result = super (MicroDict , self ).get (key [:1 ].upper () + key [1 :])
20
- return result
16
+ def __getitem__ (self , key ):
17
+ result = super (MicroDict , self ).get (key [:1 ].lower () + key [1 :], None )
18
+ if result is None :
19
+ result = super (MicroDict , self ).get (key [:1 ].upper () + key [1 :])
20
+ return result
21
21
22
22
23
23
class Singleton (type ):
24
- _instance = None
24
+ _instance = None
25
25
26
- def __call__ (cls , * args , ** kwargs ):
27
- if not cls ._instance :
28
- cls ._instance = super (Singleton , cls ).__call__ (* args , ** kwargs )
29
- return cls ._instance
26
+ def __call__ (cls , * args , ** kwargs ):
27
+ if not cls ._instance :
28
+ cls ._instance = super (Singleton , cls ).__call__ (* args , ** kwargs )
29
+ return cls ._instance
30
30
31
- # def __new__(cls, *args, **kwargs):
32
- # if not cls._instance:
33
- # cls._instance = object.__new__(cls)
34
- # return cls._instance
31
+ # def __new__(cls, *args, **kwargs):
32
+ # if not cls._instance:
33
+ # cls._instance = object.__new__(cls)
34
+ # return cls._instance
35
35
36
36
37
37
_default_token_file = '.o365_token'
@@ -40,186 +40,186 @@ def __call__(cls, *args, **kwargs):
40
40
41
41
42
42
def save_token (token , token_path = None ):
43
- """ Save the specified token dictionary to a specified file path
43
+ """ Save the specified token dictionary to a specified file path
44
44
45
- :param token: token dictionary returned by the oauth token request
46
- :param token_path: path to where the files is to be saved
47
- """
48
- if not token_path :
49
- token_path = default_token_path
45
+ :param token: token dictionary returned by the oauth token request
46
+ :param token_path: path to where the files is to be saved
47
+ """
48
+ if not token_path :
49
+ token_path = default_token_path
50
50
51
- with open (token_path , 'w' ) as token_file :
52
- json .dump (token , token_file , indent = True )
51
+ with open (token_path , 'w' ) as token_file :
52
+ json .dump (token , token_file , indent = True )
53
53
54
54
55
55
def load_token (token_path = None ):
56
- """ Save the specified token dictionary to a specified file path
56
+ """ Save the specified token dictionary to a specified file path
57
57
58
- :param token_path: path to the file with token information saved
59
- """
60
- if not token_path :
61
- token_path = default_token_path
58
+ :param token_path: path to the file with token information saved
59
+ """
60
+ if not token_path :
61
+ token_path = default_token_path
62
62
63
- token = None
64
- if path .exists (token_path ):
65
- with open (token_path , 'r' ) as token_file :
66
- token = json .load (token_file )
67
- return token
63
+ token = None
64
+ if path .exists (token_path ):
65
+ with open (token_path , 'r' ) as token_file :
66
+ token = json .load (token_file )
67
+ return token
68
68
69
69
70
70
def delete_token (token_path = None ):
71
- """ Save the specified token dictionary to a specified file path
71
+ """ Save the specified token dictionary to a specified file path
72
72
73
- :param token_path: path to where the token is saved
74
- """
75
- if not token_path :
76
- token_path = default_token_path
73
+ :param token_path: path to where the token is saved
74
+ """
75
+ if not token_path :
76
+ token_path = default_token_path
77
77
78
- if path .exists (token_path ):
79
- os .unlink (token_path )
78
+ if path .exists (token_path ):
79
+ os .unlink (token_path )
80
80
81
81
82
82
class Connection (with_metaclass (Singleton )):
83
- _oauth2_authorize_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
84
- _oauth2_token_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
85
-
86
- def __init__ (self ):
87
- """ Creates a O365 connection object """
88
- self .api_version = None
89
- self .auth = None
90
-
91
- self .oauth = None
92
- self .client_id = None
93
- self .client_secret = None
94
- self .token = None
95
-
96
- self .proxy_dict = None
97
-
98
- def is_valid (self ):
99
- valid = False
100
-
101
- if self .api_version == '1.0' :
102
- valid = True if self .auth else False
103
- elif self .api_version == '2.0' :
104
- valid = True if self .oauth else False
105
-
106
- return valid
107
-
108
- @staticmethod
109
- def login (username , password ):
110
- """ Connect to office 365 using specified username and password
111
-
112
- :param username: username to login with
113
- :param password: password for authentication
114
- """
115
- connection = Connection ()
116
-
117
- connection .api_version = '1.0'
118
- connection .auth = (username , password )
119
- return connection
120
-
121
- @staticmethod
122
- def oauth2 (client_id , client_secret , store_token = True , token_path = None ):
123
- """ Connect to office 365 using specified Open Authentication protocol
124
-
125
- :param client_id: application_id generated by https://apps.dev.microsoft.com when you register your app
126
- :param client_secret: secret password key generated for your application
127
- :param store_token: whether or not to store the token in file system, so u don't have to keep opening
128
- the auth link and authenticating every time
129
- :param token_path: full path to where the token should be saved to
130
- """
131
- connection = Connection ()
132
-
133
- connection .api_version = '2.0'
134
- connection .client_id = client_id
135
- connection .client_secret = client_secret
136
-
137
- if not store_token :
138
- delete_token (token_path )
139
-
140
- token = load_token (token_path )
141
-
142
- if not token :
143
- connection .oauth = OAuth2Session (client_id = client_id ,
144
- redirect_uri = 'https://outlook.office365.com/owa/' ,
145
- scope = ['https://graph.microsoft.com/Mail.ReadWrite' ,
146
- 'https://graph.microsoft.com/Mail.Send' ,
147
- 'offline_access' ], )
148
- oauth = connection .oauth
149
- auth_url , state = oauth .authorization_url (
150
- url = Connection ._oauth2_authorize_url ,
151
- access_type = 'offline' )
152
- print ('Please open {} and authorize the application' .format (auth_url ))
153
- auth_resp = input ('Enter the full result url: ' )
154
- os .environ ['OAUTHLIB_RELAX_TOKEN_SCOPE' ] = 'Y'
155
- token = oauth .fetch_token (token_url = Connection ._oauth2_token_url ,
156
- authorization_response = auth_resp , client_secret = client_secret )
157
- save_token (token , token_path )
158
- else :
159
- connection .oauth = OAuth2Session (client_id = client_id ,
160
- token = token )
161
-
162
- return connection
163
-
164
- @staticmethod
165
- def proxy (url , port , username , password ):
166
- """ Connect to Office 365 though the specified proxy
167
-
168
- :param url: url of the proxy server
169
- :param port: port to connect to proxy server
170
- :param username: username for authentication in the proxy server
171
- :param password: password for the specified username
172
- """
173
- connection = Connection ()
174
-
175
- connection .proxy_dict = {
176
- "http" : "http://{}:{}@{}:{}" .format (username , password , url , port ),
177
- "https" : "https://{}:{}@{}:{}" .format (username , password , url ,
178
- port ),
179
- }
180
- return connection
181
-
182
- @staticmethod
183
- def get_response (request_url , ** kwargs ):
184
- """ Fetches the response for specified url and arguments, adding the auth and proxy information to the url
185
-
186
- :param request_url: url to request
187
- :param kwargs: any keyword arguments to pass to the requests api
188
- :return: response object
189
- """
190
- connection = Connection ()
191
-
192
- if not connection .is_valid ():
193
- raise RuntimeError ('Connection is not configured, please use "O365.Connection" '
194
- 'to set username and password or OAuth2 authentication' )
195
-
196
- con_params = {}
197
- if connection .proxy_dict :
198
- con_params ['proxies' ] = connection .proxy_dict
199
- con_params .update (kwargs )
200
-
201
- log .info ('Requesting URL: {}' .format (request_url ))
202
-
203
- if connection .api_version == '1.0' :
204
- con_params ['auth' ] = connection .auth
205
- response = requests .get (request_url , ** con_params )
206
- else :
207
- try :
208
- response = connection .oauth .get (request_url , ** con_params )
209
- except TokenExpiredError :
210
- log .info ('Token is expired, fetching a new token' )
211
- token = connection .oauth .refresh_token (Connection ._oauth2_token_url , client_id = connection .client_id ,
212
- client_secret = connection .client_secret )
213
- log .info ('New token fetched' )
214
- save_token (token )
215
-
216
- response = connection .oauth .get (request_url , ** con_params )
217
-
218
- log .info ('Received response from URL {}' .format (response .url ))
219
-
220
- response_json = response .json ()
221
- if 'value' not in response_json :
222
- raise RuntimeError ('Something went wrong, received an unexpected result \n {}' .format (response_json ))
223
-
224
- response_values = [MicroDict (x ) for x in response_json ['value' ]]
225
- return response_values
83
+ _oauth2_authorize_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
84
+ _oauth2_token_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
85
+
86
+ def __init__ (self ):
87
+ """ Creates a O365 connection object """
88
+ self .api_version = None
89
+ self .auth = None
90
+
91
+ self .oauth = None
92
+ self .client_id = None
93
+ self .client_secret = None
94
+ self .token = None
95
+
96
+ self .proxy_dict = None
97
+
98
+ def is_valid (self ):
99
+ valid = False
100
+
101
+ if self .api_version == '1.0' :
102
+ valid = True if self .auth else False
103
+ elif self .api_version == '2.0' :
104
+ valid = True if self .oauth else False
105
+
106
+ return valid
107
+
108
+ @staticmethod
109
+ def login (username , password ):
110
+ """ Connect to office 365 using specified username and password
111
+
112
+ :param username: username to login with
113
+ :param password: password for authentication
114
+ """
115
+ connection = Connection ()
116
+
117
+ connection .api_version = '1.0'
118
+ connection .auth = (username , password )
119
+ return connection
120
+
121
+ @staticmethod
122
+ def oauth2 (client_id , client_secret , store_token = True , token_path = None ):
123
+ """ Connect to office 365 using specified Open Authentication protocol
124
+
125
+ :param client_id: application_id generated by https://apps.dev.microsoft.com when you register your app
126
+ :param client_secret: secret password key generated for your application
127
+ :param store_token: whether or not to store the token in file system, so u don't have to keep opening
128
+ the auth link and authenticating every time
129
+ :param token_path: full path to where the token should be saved to
130
+ """
131
+ connection = Connection ()
132
+
133
+ connection .api_version = '2.0'
134
+ connection .client_id = client_id
135
+ connection .client_secret = client_secret
136
+
137
+ if not store_token :
138
+ delete_token (token_path )
139
+
140
+ token = load_token (token_path )
141
+
142
+ if not token :
143
+ connection .oauth = OAuth2Session (client_id = client_id ,
144
+ redirect_uri = 'https://outlook.office365.com/owa/' ,
145
+ scope = ['https://graph.microsoft.com/Mail.ReadWrite' ,
146
+ 'https://graph.microsoft.com/Mail.Send' ,
147
+ 'offline_access' ], )
148
+ oauth = connection .oauth
149
+ auth_url , state = oauth .authorization_url (
150
+ url = Connection ._oauth2_authorize_url ,
151
+ access_type = 'offline' )
152
+ print ('Please open {} and authorize the application' .format (auth_url ))
153
+ auth_resp = input ('Enter the full result url: ' )
154
+ os .environ ['OAUTHLIB_RELAX_TOKEN_SCOPE' ] = 'Y'
155
+ token = oauth .fetch_token (token_url = Connection ._oauth2_token_url ,
156
+ authorization_response = auth_resp , client_secret = client_secret )
157
+ save_token (token , token_path )
158
+ else :
159
+ connection .oauth = OAuth2Session (client_id = client_id ,
160
+ token = token )
161
+
162
+ return connection
163
+
164
+ @staticmethod
165
+ def proxy (url , port , username , password ):
166
+ """ Connect to Office 365 though the specified proxy
167
+
168
+ :param url: url of the proxy server
169
+ :param port: port to connect to proxy server
170
+ :param username: username for authentication in the proxy server
171
+ :param password: password for the specified username
172
+ """
173
+ connection = Connection ()
174
+
175
+ connection .proxy_dict = {
176
+ "http" : "http://{}:{}@{}:{}" .format (username , password , url , port ),
177
+ "https" : "https://{}:{}@{}:{}" .format (username , password , url ,
178
+ port ),
179
+ }
180
+ return connection
181
+
182
+ @staticmethod
183
+ def get_response (request_url , ** kwargs ):
184
+ """ Fetches the response for specified url and arguments, adding the auth and proxy information to the url
185
+
186
+ :param request_url: url to request
187
+ :param kwargs: any keyword arguments to pass to the requests api
188
+ :return: response object
189
+ """
190
+ connection = Connection ()
191
+
192
+ if not connection .is_valid ():
193
+ raise RuntimeError ('Connection is not configured, please use "O365.Connection" '
194
+ 'to set username and password or OAuth2 authentication' )
195
+
196
+ con_params = {}
197
+ if connection .proxy_dict :
198
+ con_params ['proxies' ] = connection .proxy_dict
199
+ con_params .update (kwargs )
200
+
201
+ log .info ('Requesting URL: {}' .format (request_url ))
202
+
203
+ if connection .api_version == '1.0' :
204
+ con_params ['auth' ] = connection .auth
205
+ response = requests .get (request_url , ** con_params )
206
+ else :
207
+ try :
208
+ response = connection .oauth .get (request_url , ** con_params )
209
+ except TokenExpiredError :
210
+ log .info ('Token is expired, fetching a new token' )
211
+ token = connection .oauth .refresh_token (Connection ._oauth2_token_url , client_id = connection .client_id ,
212
+ client_secret = connection .client_secret )
213
+ log .info ('New token fetched' )
214
+ save_token (token )
215
+
216
+ response = connection .oauth .get (request_url , ** con_params )
217
+
218
+ log .info ('Received response from URL {}' .format (response .url ))
219
+
220
+ response_json = response .json ()
221
+ if 'value' not in response_json :
222
+ raise RuntimeError ('Something went wrong, received an unexpected result \n {}' .format (response_json ))
223
+
224
+ response_values = [MicroDict (x ) for x in response_json ['value' ]]
225
+ return response_values
0 commit comments