mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-01-17 08:52:29 +01:00
feat(doit): authentication with and without scopes
It's quite rough around the edges, but has a slight chance to work. Will still to handle return values accordingly.
This commit is contained in:
@@ -55,12 +55,19 @@ struct JsonServerError {
|
||||
/// uploading media
|
||||
pub trait Delegate {
|
||||
|
||||
/// Called whenever there is an HttpError, usually if there are network problems.
|
||||
/// Called whenever there is an [HttpError](http://hyperium.github.io/hyper/hyper/error/enum.HttpError.html), usually if there are network problems.
|
||||
///
|
||||
/// Return retry information.
|
||||
fn connection_error(&mut self, hyper::HttpError) -> oauth2::Retry {
|
||||
fn http_error(&mut self, &hyper::HttpError) -> oauth2::Retry {
|
||||
oauth2::Retry::Abort
|
||||
}
|
||||
|
||||
/// Called whenever there is the need for your applications API key after
|
||||
/// the official authenticator implementation didn't provide one, for some reason.
|
||||
/// If this method returns None as well, the underlying operation will fail
|
||||
fn api_key(&mut self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -74,9 +81,16 @@ pub enum Result<T = ()> {
|
||||
/// The http connection failed
|
||||
HttpError(hyper::HttpError),
|
||||
|
||||
/// We needed an API key for authentication, but didn't obtain one.
|
||||
/// Neither through the authenticator, nor through the Delegate.
|
||||
MissingAPIKey,
|
||||
|
||||
/// We required a Token, but didn't get one from the Authenticator
|
||||
MissingToken,
|
||||
|
||||
/// An additional, free form field clashed with one of the built-in optional ones
|
||||
FieldClash(&'static str),
|
||||
|
||||
/// It worked !
|
||||
Success(T),
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,8 +38,10 @@ pub mod cmn;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
use std::borrow::BorrowMut;
|
||||
use std::default::Default;
|
||||
use std::collections::BTreeMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::io;
|
||||
use std::fs;
|
||||
use std::old_io::timer::sleep;
|
||||
@@ -82,8 +84,10 @@ ${lib.scope_enum()}
|
||||
${lib.hub_usage_example(c)}\
|
||||
</%block>
|
||||
pub struct ${hub_type}${ht_params} {
|
||||
client: RefCell<hyper::Client<NC>>,
|
||||
client: RefCell<C>,
|
||||
auth: RefCell<A>,
|
||||
|
||||
_m: PhantomData<NC>
|
||||
}
|
||||
|
||||
impl<'a, ${', '.join(HUB_TYPE_PARAMETERS)}> Hub for ${hub_type}${ht_params} {}
|
||||
@@ -91,10 +95,11 @@ impl<'a, ${', '.join(HUB_TYPE_PARAMETERS)}> Hub for ${hub_type}${ht_params} {}
|
||||
impl<'a, ${', '.join(HUB_TYPE_PARAMETERS)}> ${hub_type}${ht_params}
|
||||
where ${', '.join(hub_type_bounds())} {
|
||||
|
||||
pub fn new(client: hyper::Client<NC>, authenticator: A) -> ${hub_type}${ht_params} {
|
||||
pub fn new(client: C, authenticator: A) -> ${hub_type}${ht_params} {
|
||||
${hub_type} {
|
||||
client: RefCell::new(client),
|
||||
auth: RefCell::new(authenticator),
|
||||
_m: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
unindent_first_by, mangle_ident, mb_type, singular, scope_url_to_variant,
|
||||
PART_MARKER_TRAIT, RESOURCE_MARKER_TRAIT, METHOD_BUILDER_MARKERT_TRAIT,
|
||||
find_fattest_resource, build_all_params, pass_through, parts_from_params,
|
||||
REQUEST_MARKER_TRAIT, RESPONSE_MARKER_TRAIT) %>\
|
||||
REQUEST_MARKER_TRAIT, RESPONSE_MARKER_TRAIT, supports_scopes) %>\
|
||||
<%namespace name="util" file="util.mako"/>\
|
||||
<%namespace name="mbuild" file="mbuild.mako"/>\
|
||||
|
||||
@@ -242,7 +242,7 @@ You can read the full text at the repository's [license file][repo-license].
|
||||
###############################################################################################
|
||||
###############################################################################################
|
||||
<%def name="scope_enum()">\
|
||||
% if not auth or not auth.oauth2:
|
||||
% if not supports_scopes(auth):
|
||||
<% return '' %>\
|
||||
% endif
|
||||
/// Identifies the an OAuth2 authorization scope.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
indent_by, to_rust_type, rnd_arg_val_for_type, extract_parts, mb_type_params_s,
|
||||
hub_type_params_s, method_media_params, enclose_in, mb_type_bounds, method_response,
|
||||
METHOD_BUILDER_MARKERT_TRAIT, pass_through, markdown_rust_block, parts_from_params,
|
||||
DELEGATE_PROPERTY_NAME)
|
||||
DELEGATE_PROPERTY_NAME, struct_type_bounds_s, supports_scopes, scope_url_to_variant)
|
||||
|
||||
def get_parts(part_prop):
|
||||
if not part_prop:
|
||||
@@ -79,8 +79,7 @@ the *${m.scopes[0]}* scope to make a valid call.
|
||||
${self.usage(resource, method, m, params, request_value, parts)}\
|
||||
</%block>
|
||||
pub struct ${ThisType}
|
||||
where NC: 'a,
|
||||
A: 'a, {
|
||||
where ${struct_type_bounds_s()} {
|
||||
|
||||
hub: &'a ${hub_type_name}${hub_type_params_s()},
|
||||
## PROPERTIES ###############
|
||||
@@ -94,7 +93,7 @@ pub struct ${ThisType}
|
||||
% endfor
|
||||
## A generic map for additinal parameters. Sometimes you can set some that are documented online only
|
||||
${api.properties.params}: HashMap<String, String>,
|
||||
% if auth and auth.oauth2:
|
||||
% if supports_scopes(auth):
|
||||
## We need the scopes sorted, to not unnecessarily query new tokens
|
||||
${api.properties.scopes}: BTreeMap<String, ()>
|
||||
% endif
|
||||
@@ -131,7 +130,7 @@ ${self._setter_fn(resource, method, m, p, part_prop, ThisType, c)}\
|
||||
self
|
||||
}
|
||||
|
||||
% if auth and auth.oauth2:
|
||||
% if supports_scopes(auth):
|
||||
/// Identifies the authorization scope for the method you are building.
|
||||
///
|
||||
/// Use this method to actively specify which scope should be used, instead of relying on the
|
||||
@@ -361,6 +360,21 @@ match result {
|
||||
paddfields = 'self.' + api.properties.params
|
||||
|
||||
delegate = 'self.' + property(DELEGATE_PROPERTY_NAME)
|
||||
delegate_call = delegate + '.as_mut().unwrap()'
|
||||
auth_call = 'self.hub.auth.borrow_mut()'
|
||||
client = '(*self.hub.client.borrow_mut()).borrow_mut()'
|
||||
|
||||
if supports_scopes(auth):
|
||||
all_scopes = auth.oauth2.scopes.keys()
|
||||
default_scope = all_scopes[0]
|
||||
assert 'readonly' not in default_scope
|
||||
if m.httpMethod in ('HEAD', 'GET', 'OPTIONS', 'TRACE'):
|
||||
for scope in all_scopes:
|
||||
if 'readonly' in scope:
|
||||
default_scope = scope
|
||||
break
|
||||
# end try to find read-only default scope
|
||||
# end handle default scope
|
||||
%>
|
||||
/// Perform the operation you have build so far.
|
||||
${action_fn} {
|
||||
@@ -425,20 +439,49 @@ else {
|
||||
let mut url = "${baseUrl}${m.path}".to_string();
|
||||
% endif
|
||||
|
||||
% if not supports_scopes(auth):
|
||||
<%
|
||||
assert 'key' in parameters, "Expected 'key' parameter if there are no scopes"
|
||||
%>\
|
||||
let mut key = ${auth_call}.api_key();
|
||||
if key.is_none() && ${delegate}.is_some() {
|
||||
key = ${delegate_call}.api_key();
|
||||
}
|
||||
if key.is_some() {
|
||||
params.push(("key", key.unwrap()));
|
||||
} else {
|
||||
return cmn::Result::MissingAPIKey
|
||||
}
|
||||
% else:
|
||||
if self.${api.properties.scopes}.len() == 0 {
|
||||
self.${api.properties.scopes}.insert(${scope_url_to_variant(name, default_scope, fully_qualified=True)}.as_slice().to_string(), ());
|
||||
}
|
||||
% endif
|
||||
|
||||
url.push('?');
|
||||
url.push_str(&url::form_urlencoded::serialize(params.iter().map(|t| (t.0, t.1.as_slice()))));
|
||||
|
||||
loop {
|
||||
match self.hub.client.borrow_mut().request(Method::Extension("${m.httpMethod}".to_string()), url.as_slice())
|
||||
% if supports_scopes(auth):
|
||||
let token = ${auth_call}.token(self.${api.properties.scopes}.keys());
|
||||
if token.is_none() {
|
||||
return cmn::Result::MissingToken
|
||||
}
|
||||
let auth_header = hyper::header::Authorization(token.unwrap().access_token);
|
||||
% endif
|
||||
match ${client}.request(Method::Extension("${m.httpMethod}".to_string()), url.as_slice())
|
||||
.header(UserAgent("google-api-rust-client/${cargo.build_version}".to_string()))
|
||||
% if supports_scopes(auth):
|
||||
.header(auth_header)
|
||||
% endif
|
||||
% if request_value:
|
||||
.header(ContentType(Mime(TopLevel::Application, SubLevel::Json, Default::default())))
|
||||
.body(json::encode(&self.${property(REQUEST_VALUE_PROPERTY_NAME)}).unwrap())
|
||||
.body(json::encode(&self.${property(REQUEST_VALUE_PROPERTY_NAME)}).unwrap().as_slice())
|
||||
% endif
|
||||
.send() {
|
||||
Err(err) => {
|
||||
if ${delegate}.is_some() {
|
||||
match ${delegate}.as_mut().unwrap().http_error(&err) {
|
||||
match ${delegate_call}.http_error(&err) {
|
||||
oauth2::Retry::Abort => return cmn::Result::HttpError(err),
|
||||
oauth2::Retry::After(d) => {
|
||||
sleep(d);
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
to_fqan, indent_all_but_first_by,
|
||||
activity_input_type, TREF, IO_REQUEST, schema_to_required_property,
|
||||
rust_copy_value_s, is_required_property, organize_params, REQUEST_VALUE_PROPERTY_NAME,
|
||||
build_all_params, rb_type_params_s, hub_type_params_s, mb_type_params_s, mb_additional_type_params)
|
||||
build_all_params, rb_type_params_s, hub_type_params_s, mb_type_params_s, mb_additional_type_params,
|
||||
struct_type_bounds_s)
|
||||
%>\
|
||||
<%namespace name="util" file="util.mako"/>\
|
||||
<%namespace name="lib" file="lib.mako"/>\
|
||||
@@ -38,8 +39,7 @@ let rb = hub.${mangle_ident(resource)}();
|
||||
</%block>
|
||||
</%block>
|
||||
pub struct ${ThisType}
|
||||
where NC: 'a,
|
||||
A: 'a, {
|
||||
where ${struct_type_bounds_s()} {
|
||||
|
||||
hub: &'a ${hub_type_name}${hub_type_params_s()},
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ data_unit_multipliers = {
|
||||
'%': 1,
|
||||
}
|
||||
|
||||
HUB_TYPE_PARAMETERS = ('NC', 'A')
|
||||
HUB_TYPE_PARAMETERS = ('C', 'NC', 'A')
|
||||
|
||||
# ==============================================================================
|
||||
## @name Filters
|
||||
@@ -732,8 +732,13 @@ def hub_type_params_s():
|
||||
# return a list of where statements to server as bounds for the hub.
|
||||
def hub_type_bounds():
|
||||
return ['NC: hyper::net::NetworkConnector',
|
||||
'C: BorrowMut<hyper::Client<NC>>',
|
||||
'A: oauth2::GetToken']
|
||||
|
||||
# Returns True if this API has particular authentication scopes to choose from
|
||||
def supports_scopes(auth):
|
||||
return bool(auth) and bool(auth.oauth2)
|
||||
|
||||
# return list of type bounds required by method builder
|
||||
def mb_type_bounds():
|
||||
return hub_type_bounds()
|
||||
@@ -745,6 +750,10 @@ _rb_type_params = ("'a", ) + HUB_TYPE_PARAMETERS
|
||||
def rb_type_params_s(resource, c):
|
||||
return _to_type_params_s(_rb_type_params)
|
||||
|
||||
# type bounds for resource and method builder
|
||||
def struct_type_bounds_s():
|
||||
return ', '.join(tp + ": 'a" for tp in HUB_TYPE_PARAMETERS)
|
||||
|
||||
# type params for the given method builder, as string suitable for Rust code
|
||||
def mb_type_params_s(m):
|
||||
return _to_type_params_s(_rb_type_params)
|
||||
@@ -808,8 +817,9 @@ def parts_from_params(params):
|
||||
return part_prop, list()
|
||||
|
||||
# Convert a scope url to a nice enum variant identifier, ready for use in code
|
||||
# name = name of the api, without version
|
||||
# name = name of the api, without version, non-normalized (!)
|
||||
def scope_url_to_variant(name, url, fully_qualified=True):
|
||||
name = normalize_library_name(name)
|
||||
FULL = 'Full'
|
||||
fqvn = lambda n: fully_qualified and 'Scope::%s' % n or n
|
||||
repl = lambda n: n.replace('-', '.').replace('_', '.')
|
||||
|
||||
@@ -59,6 +59,13 @@ pub trait Delegate {
|
||||
fn http_error(&mut self, &hyper::HttpError) -> oauth2::Retry {
|
||||
oauth2::Retry::Abort
|
||||
}
|
||||
|
||||
/// Called whenever there is the need for your applications API key after
|
||||
/// the official authenticator implementation didn't provide one, for some reason.
|
||||
/// If this method returns None as well, the underlying operation will fail
|
||||
fn api_key(&mut self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -72,6 +79,13 @@ pub enum Result<T = ()> {
|
||||
/// The http connection failed
|
||||
HttpError(hyper::HttpError),
|
||||
|
||||
/// We needed an API key for authentication, but didn't obtain one.
|
||||
/// Neither through the authenticator, nor through the Delegate.
|
||||
MissingAPIKey,
|
||||
|
||||
/// We required a Token, but didn't get one from the Authenticator
|
||||
MissingToken,
|
||||
|
||||
/// An additional, free form field clashed with one of the built-in optional ones
|
||||
FieldClash(&'static str),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user