Skip to content

Commit 4b1d4cf

Browse files
committed
Bring in sharepoint code
Refer to samvera#418 for what is being built off of.
1 parent afb4e3a commit 4b1d4cf

4 files changed

Lines changed: 383 additions & 0 deletions

File tree

SharePoint.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Sharepoint Provider
2+
3+
This provider will allow browse-everything to access a _specific_ SharePoint location
4+
5+
First register an application on azure to give access to the relevant location
6+
7+
https://learn.microsoft.com/en-us/graph/auth-v2-service?tabs=http (steps 1,2 and 3)
8+
9+
To us the sharepoint provider add the following to config/browse_everything_providers.yml
10+
11+
```
12+
sharepoint:
13+
client_id: [MyAppClientID]
14+
client_secret: [MyAppClientSecret]
15+
tenant_id: [MyAzuerTenantID]
16+
grant_type: client_credentials
17+
scope: https://graph.microsoft.com/.default
18+
domain: mydomain.sharepoint.com
19+
site_name: [MySiteName]
20+
```

lib/browse_everything.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ module Driver
1616
autoload :Box, 'browse_everything/driver/box'
1717
autoload :GoogleDrive, 'browse_everything/driver/google_drive'
1818
autoload :S3, 'browse_everything/driver/s3'
19+
autoload :Sharepoint, 'browse_everything/driver/sharepoint'
20+
1921

2022
# Access the sorter set for the base driver class
2123
# @return [Proc]
@@ -39,6 +41,12 @@ module Google
3941
end
4042
end
4143

44+
module Auth
45+
module Sharepoint
46+
autoload :Session, 'browse_everything/auth/sharepoint/session'
47+
end
48+
end
49+
4250
class InitializationError < RuntimeError; end
4351
class ConfigurationError < StandardError; end
4452
class NotImplementedError < StandardError; end
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
require 'oauth2'
2+
3+
# BrowseEverything OAuth2 session for
4+
# Sharepoint provider
5+
module BrowseEverything
6+
module Auth
7+
module Sharepoint
8+
class Session
9+
10+
OAUTH2_URLS = {
11+
:site => 'https://login.microsoftonline.com',
12+
}
13+
# :scope => "https://graph.microsoft.com/.default"
14+
15+
def initialize(opts={})
16+
17+
@config = BrowseEverything.config['sharepoint']
18+
19+
if opts[:client_id]
20+
@oauth2_client = OAuth2::Client.new(opts[:client_id], opts[:client_secret],{:authorize_url => authorize_url, :token_url => token_url, :scope => scope}.merge!(OAUTH2_URLS.dup))
21+
@access_token = OAuth2::AccessToken.new(@oauth2_client, opts[:access_token]) if opts[:access_token]
22+
@access_token = get_access_token if opts[:access_token].blank?
23+
@refresh_token = opts[:refresh_token] if @config[:grant_type] == 'authorization_code'
24+
# @as_user = opts[:as_user]
25+
end
26+
end
27+
28+
def authorize_url
29+
@config['tenant_id']+"/oauth2/v2.0/authorize"
30+
end
31+
32+
def token_url
33+
@config['tenant_id']+"/oauth2/v2.0/token"
34+
end
35+
36+
def scope
37+
@config['scope']
38+
end
39+
40+
# def authorize_url(redirect_uri, state=nil)
41+
# opts = { :redirect_uri => redirect_uri }
42+
# opts[:state] = state if state
43+
#
44+
# @oauth2_client.auth_code.authorize_url(opts)
45+
# end
46+
47+
def get_access_token(code=nil)
48+
49+
if @config[:grant_type] == 'client_credentials'
50+
@access_token ||= @oauth2_client.client_credentials.get_token({:scope => @config[:scope]})
51+
else
52+
# assume authorization_code grant_type..?
53+
@access_token ||= @oauth2_client.auth_code.get_token(code)
54+
end
55+
end
56+
57+
def refresh_token(refresh_token)
58+
refresh_access_token_obj = OAuth2::AccessToken.new(@oauth2_client, @access_token.token, {'refresh_token' => refresh_token})
59+
@access_token = refresh_access_token_obj.refresh!
60+
end
61+
62+
def build_auth_header
63+
"BoxAuth api_key=#{@api_key}&auth_token=#{@auth_token}"
64+
end
65+
66+
def get(url, raw=false)
67+
uri = URI.parse(url)
68+
request = Net::HTTP::Get.new( uri.request_uri )
69+
resp = request( uri, request, raw )
70+
end
71+
72+
def delete(url, raw=false)
73+
uri = URI.parse(url)
74+
request = Net::HTTP::Delete.new( uri.request_uri )
75+
resp = request( uri, request, raw )
76+
end
77+
78+
def request(uri, request, raw=false, retries=0)
79+
80+
http = Net::HTTP.new(uri.host, uri.port)
81+
http.use_ssl = true
82+
#http.set_debug_output($stdout)
83+
84+
if @access_token
85+
request.add_field('Authorization', "Bearer #{@access_token.token}")
86+
else
87+
request.add_field('Authorization', build_auth_header)
88+
end
89+
90+
91+
request.add_field('As-User', "#{@as_user}") if @as_user
92+
93+
response = http.request(request)
94+
95+
if response.is_a? Net::HTTPNotFound
96+
raise RubyBox::ObjectNotFound
97+
end
98+
99+
# Got unauthorized (401) status, try to refresh the token
100+
if response.code.to_i == 401 and @refresh_token and retries == 0
101+
refresh_token(@refresh_token)
102+
return request(uri, request, raw, retries + 1)
103+
end
104+
105+
sleep(@backoff) # try not to excessively hammer API.
106+
107+
handle_errors( response, raw )
108+
end
109+
110+
def do_stream(url, opts)
111+
params = {
112+
:content_length_proc => opts[:content_length_proc],
113+
:progress_proc => opts[:progress_proc]
114+
}
115+
116+
if @access_token
117+
params['Authorization'] = "Bearer #{@access_token.token}"
118+
else
119+
params['Authorization'] = build_auth_header
120+
end
121+
122+
params['As-User'] = @as_user if @as_user
123+
124+
open(url, params)
125+
end
126+
127+
def handle_errors( response, raw )
128+
status = response.code.to_i
129+
body = response.body
130+
begin
131+
parsed_body = JSON.parse(body)
132+
rescue
133+
msg = body.nil? || body.empty? ? "no data returned" : body
134+
parsed_body = { "message" => msg }
135+
end
136+
137+
# status is used to determine whether
138+
# we need to refresh the access token.
139+
parsed_body["status"] = status
140+
141+
case status / 100
142+
when 3
143+
# 302 Found. We should return the url
144+
parsed_body["location"] = response["Location"] if status == 302
145+
when 4
146+
raise(RubyBox::ItemNameInUse.new(parsed_body, status, body), parsed_body["message"]) if parsed_body["code"] == "item_name_in_use"
147+
raise(RubyBox::AuthError.new(parsed_body, status, body), parsed_body["message"]) if parsed_body["code"] == "unauthorized" || status == 401
148+
raise(RubyBox::RequestError.new(parsed_body, status, body), parsed_body["message"])
149+
when 5
150+
raise(RubyBox::ServerError.new(parsed_body, status, body), parsed_body["message"])
151+
end
152+
raw ? body : parsed_body
153+
end
154+
end
155+
end
156+
end
157+
end

0 commit comments

Comments
 (0)