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