You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cloudstack.apache.org by Silvano Nogueira Buback <si...@corp.globo.com> on 2014/07/14 22:59:16 UTC

[PROPOSAL] OAuth2 Single SignOn Integration

Hi gyus,

    I need to implement OAuth2 integration to provide single sign-on with
others tools in my company. I can share this implementation with the
community if you are interested. I suggest these changes in code:

1. Create a new javascript called oauth2.js. This javascript is responsible
for calling the new command called oauthRequestUrl that reads the global
option "oauth2.baseurl" and returns this url plus "/authorize" with oauth2
parameters. After receiving the answer, javascript redirects user to oauth2
server.
2. Once user is authorized by oauth2 server, javascript code reads
parameters in url and call oauthAuthorizeToken command. This command asks
the oauth2 server by the access token, and if everything is ok, calls
"oauth2.credentials.url" about user email and finds this user in the
database, like ldap implementation does and returns authentication data.
3. Javascript fills g_loginResponse with answer from command and user is
logged in.

   What do you think about this approach?


---- More details ----

Alternative flows:

* When the url has parameter direct=true, the login dialog is shown.
* When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not
set (default), oauthRequestUrl returns empty response and OAuth2
authentication is turned off.
* If authorization token is invalid, user is redirected again to oauth2
server.


Commands:
* oauthRequestUrl
* oauthAuthorizeToken


Global Options:
* oauth2.baseurl
* oauth2.client.id
* oauth2.client.secret
* oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
* oauth2.credentials.parameter.email (defaults to "email")
* oauth2.domainid


Restrictions:
* Domain Id will be a global option
* Users are always redirected to oauth2 server. Access tokens are not
stored.
* Before using Cloudstack, the administrator must insert user in an account.

Re: [PROPOSAL] OAuth2 Single SignOn Integration

Posted by Sebastien Goasguen <ru...@gmail.com>.
On Jul 15, 2014, at 2:50 PM, Silvano Nogueira Buback <si...@corp.globo.com> wrote:

> @Rohit Yadav
> I know OAuth2 is tricky and the protocol was designed for authorization,
> not authentication. But Globo.com, like many companies, use OAuth2 for
> authentication. So, SAML is not a option to me. I need to integrate with
> internal tools at Globo, and these tools work only with OAuth2.
> 
> @Sebastien
> API are accessed by user, as today, using apikey and secretkey. OAuth is
> only to UI.

To me a UI only OAuth would defeat the purpose. Google GCE has OAuth for it's API usage.
If you were to change it a the API level then you would have it in the UI.

> 
> @David,
> Thank you. I will look this lib.
> 
> @Edukulla
> I try to summarize your questions:
> 
> * In the code below, you can see all parameters needed to configure OAuth
> authentication with Google, Github and Globo. My idea is that they are
> global options (there are more global options than I sent before). Other
> providers may not work, this list is for the first plugin release. People
> can help integrating more providers.
> * Pay attention with the way to get user email. This is really tricky,
> because this is not part of specification. But if all providers answer json
> and have a key named "email", the implementation will work. Google, Github
> and Globo.co already do this.
> * ClientId and secret may store encrypted in Global Options, like others
> password.
> * As the number of global options increase, I create an option called
> "oauth2.enable", if you want to enable plugin. Of course,
> if all parameters are not set, doesn't matter if plugin is enabled or not.
> * The idea is not to authorize resources, is only to authenticate. So, I
> don't need to store access key. When user accesses
> UI, he is redirected to oauth2 server, and if authorization was granted
> before, oauth2 server will redirect user to cloudstack without asking for
> permission. This works with all 3 providers. I need only to have access to
> email when the session starts, and then cloudstack keeps its own session.
> 
> The big problem is with domainid, that must be an global option. Other
> problem happen with new accounts. May I create new user in an existed
> account. The account name for new users are stored in global settings too.
> At Globo.com we are discussing, but I guess if a user is not in cloudstack
> they can't logged using oauth. Like happen in ldap.
> 
> Bellow I put some code to show that OAuth is not different between these 3
> providers.
> 
> I will start the development of this feature next week. So, I will
> appreciate all comments to help me to understand problems. After I start I
> will create the design document.
> 
> 
> ------ BEGIN CODE -------
> # before run: pip install requests_oauthlib
> import sys
> # Credentials you get from registering a new application
> app = sys.argv[1]
> print "For app %s" % repr(app)
> 
> redirect_uri = 'https://localhost:8000/'
> 
> if app == 'globo':
>    client_id = 'XXX'
>    client_secret = 'XXX'
>    authorization_base_url = "XXX"
>    token_url = "XXX"
>    user_url = 'XXX'
>    scope = []
> elif app == 'github':
>    client_id = 'XXX'
>    client_secret = 'XXX'
>    authorization_base_url = "https://github.com/login/oauth/authorize"
>    token_url = "https://github.com/login/oauth/access_token"
>    user_url = 'https://api.github.com/user'
>    scope = ['user']
> elif app == 'google':
>    client_id = 'XXX'
>    client_secret = 'XXX'
>    authorization_base_url = "https://accounts.google.com/o/oauth2/auth"
>    token_url = "https://accounts.google.com/o/oauth2/token"
>    scope = [
>        "https://www.googleapis.com/auth/userinfo.email",
>        "https://www.googleapis.com/auth/userinfo.profile"
>    ]
>    user_url = 'https://www.googleapis.com/oauth2/v1/userinfo'
> else:
>    print "UNKNOW APP"
>    sys.exit(1)
> 
> from requests_oauthlib import OAuth2Session
> oauth2_session = OAuth2Session(client_id, scope=scope,
> redirect_uri=redirect_uri)
> 
> authorization_url, state =
> oauth2_session.authorization_url(authorization_base_url)
> print 'Please go here and authorize,', authorization_url
> 
> # Get the authorization verifier code from the callback url
> redirect_response = raw_input('Paste the full redirect URL here:')
> 
> # Fetch the access token
> oauth2_session.fetch_token(token_url, client_secret=client_secret,
>        authorization_response=redirect_response)
> 
> # Fetch a protected resource, i.e. user profile
> r = oauth2_session.get(user_url)
> print r.json()
> print "email=", r.json()['email']
> ------ END CODE -------
> 
> 
> 
> 
> 
> 
> On Tue, Jul 15, 2014 at 5:34 AM, David Nalley <da...@gnsa.us> wrote:
> 
>> https://oltu.apache.org <-- maybe as a starting place.
>> 
>> On Tue, Jul 15, 2014 at 3:25 AM, Sebastien Goasguen <ru...@gmail.com>
>> wrote:
>>> Silvano,
>>> 
>>> Seems to me you are doing it for browser based dashboard access only ?
>>> 
>>> How about if I want to use the API straight up, how do you integrate an
>> Oauth workflow there ?
>>> 
>>> On Jul 15, 2014, at 1:35 AM, Santhosh Edukulla <
>> santhosh.edukulla@citrix.com> wrote:
>>> 
>>>> Hi Silvano,
>>>> 
>>>> Few Notes:
>>>> 
>>>> 1. We had implementation details mentioned i believe, but we didn't
>> mentioned the design details and workflows.
>>>> 2. We didn't mentioned whether it is 2 legged flow or 3 legged flow.
>>>> 3. Not clear with this statement, "Once user is authorized by oauth2
>> server, javascript code reads parameters in url",
>>>> 4. Whats the difference between "oauth2.credentials.url" and
>> "oauth2.baseurl", the later is redirect uri? If yes, Where will have
>> redirect uri hosted?
>>>> 5. referring to the statement " When oauth2.baseurl, oauth2.client.id
>> and oauth2.client.secret are not set (default), oauthRequestUrl returns
>> empty response and OAuth2
>>>> authentication is turned off.",  can we use a flag to denote whether to
>> use oauth flow or not? If set to false, dont use it otherwise continue with
>> default.
>>>> 6. What about refresh token,i believe access token has limited life
>> time? Any call back mechanism to update with latest token if it gets
>> expired?
>>>> 7. Details like clientid,clientsecret needs to be encrypted when stored
>> and retrieved from global config?
>>>> 8. How do we map the user logged in to roles and hierarchy inside CS?
>> based on email mapping?
>>>> 9.  What is the significance of these two parameters  mentioned?
>>>> oauth2.credentials.parameter.email (defaults to "email")
>>>> * oauth2.domainid
>>>> 10. clientid and clientsecret key are based upon per tenant basis, so
>> what if we want to oauth mechanism from multiple tenants at any stage?
>>>> 11. Default values for clientid and clientsecret are loaded at which
>> stage? during initial installation and for which tenant?
>>>> 12. How do we verify the validity of clientid and clientsecret values?
>> If they are revoked? possibility of revoke is there?
>>>> 13. If we understand, it is only to authenticate a user through oauth
>> flow, we dont need authorization part inside of cs? I mean, what do we mean
>> by authorization from tenant once access key is granted?
>>>> 14. If access key is not stored, how do we get refresh token?
>>>> 15. What is the default sequence of authentication in case if oauth
>> fails? and order in which a given authentication mechanism will be chosen?
>>>> 16. Can we also show a ui, where user can enable\disable oauth setting
>> for a given account? here, possibility of mismatch with emailid based upon
>> current implementation and oauth retrieved emailid post authentication is
>> there? how do we handle it?
>>>> 17. Last, what is the significance of this feature, apart from
>> authentication support from third party clients?
>>>> 
>>>> 
>>>> Thanks!
>>>> Santhosh
>>>> ________________________________________
>>>> From: Silvano Nogueira Buback [silvano@corp.globo.com]
>>>> Sent: Monday, July 14, 2014 4:59 PM
>>>> To: dev@cloudstack.apache.org
>>>> Subject: [PROPOSAL] OAuth2 Single SignOn Integration
>>>> 
>>>> Hi gyus,
>>>> 
>>>>   I need to implement OAuth2 integration to provide single sign-on with
>>>> others tools in my company. I can share this implementation with the
>>>> community if you are interested. I suggest these changes in code:
>>>> 
>>>> 1. Create a new javascript called oauth2.js. This javascript is
>> responsible
>>>> for calling the new command called oauthRequestUrl that reads the global
>>>> option "oauth2.baseurl" and returns this url plus "/authorize" with
>> oauth2
>>>> parameters. After receiving the answer, javascript redirects user to
>> oauth2
>>>> server.
>>>> 2. Once user is authorized by oauth2 server, javascript code reads
>>>> parameters in url and call oauthAuthorizeToken command. This command
>> asks
>>>> the oauth2 server by the access token, and if everything is ok, calls
>>>> "oauth2.credentials.url" about user email and finds this user in the
>>>> database, like ldap implementation does and returns authentication data.
>>>> 3. Javascript fills g_loginResponse with answer from command and user is
>>>> logged in.
>>>> 
>>>>  What do you think about this approach?
>>>> 
>>>> 
>>>> ---- More details ----
>>>> 
>>>> Alternative flows:
>>>> 
>>>> * When the url has parameter direct=true, the login dialog is shown.
>>>> * When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are
>> not
>>>> set (default), oauthRequestUrl returns empty response and OAuth2
>>>> authentication is turned off.
>>>> * If authorization token is invalid, user is redirected again to oauth2
>>>> server.
>>>> 
>>>> 
>>>> Commands:
>>>> * oauthRequestUrl
>>>> * oauthAuthorizeToken
>>>> 
>>>> 
>>>> Global Options:
>>>> * oauth2.baseurl
>>>> * oauth2.client.id
>>>> * oauth2.client.secret
>>>> * oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
>>>> * oauth2.credentials.parameter.email (defaults to "email")
>>>> * oauth2.domainid
>>>> 
>>>> 
>>>> Restrictions:
>>>> * Domain Id will be a global option
>>>> * Users are always redirected to oauth2 server. Access tokens are not
>>>> stored.
>>>> * Before using Cloudstack, the administrator must insert user in an
>> account.
>>> 
>> 


RE: [PROPOSAL] OAuth2 Single SignOn Integration

Posted by Santhosh Edukulla <sa...@citrix.com>.
Silvano,

1. First thing, It seems a 3-legged oAuth flow, correct? 
 
2. In the flow, we register an app obtain client id and client secret key, users grant access to apps retrieve a authorize code at redirect uri, we use this authorize code to retrieve access token right? now to receive the authorize code, where we are hosting the redirect uri? This is the one you used at the time of app registration right? The redirect uri is mentioned as : 'https://localhost:8000/', so, is this the redirect uri you use when registering an app with any of the tenants say github? will it allow this redirect uri? If you are using a different redirect uri, where this will be hosted? i believe this is where we get the code(authorize code) as a response? 

3. How do we host different redirect uri for each tenant? I mean how do we know that the response code we got from which tenant at the same redirect uri? so, we have multiple redirect urils or you are forming them dynamically?

4. If a user ( identified by email ) is not available in CS, how the authentication works? So, in a flow, we dont need to have a user available in CS and still can work to get access token once granted? If so, how do we map the permissions for this user in CS, this user is not available in cloudstack earlier? Suppose you store the email once granted access, how do we determine the role say admin, non admin?

5. Below are the configuration parameters for each tenant? To access them you use native REST apis not their SDKS right?
uthorization_base_url = "https://github.com/login/oauth/authorize"
    token_url = "https://github.com/login/oauth/access_token"
    user_url = 'https://api.github.com/use

6. There could be a possibility that the account for which we are getting accesstoken has been revoked post the grant? I mean initially the user granted access, we retrieved access token( agreed you may want to ignore this as you are not using it further and relying on CS parameters thereafter) , what if the account is revoked immediately thereafter from tenant say google\github? but still he is active from CS perspective?

7. May be some flows explaining the changes required should help. 

Regards,
Santhosh
________________________________________
From: Silvano Nogueira Buback [silvano@corp.globo.com]
Sent: Tuesday, July 15, 2014 2:50 PM
To: dev@cloudstack.apache.org
Subject: Re: [PROPOSAL] OAuth2 Single SignOn Integration

@Rohit Yadav
I know OAuth2 is tricky and the protocol was designed for authorization,
not authentication. But Globo.com, like many companies, use OAuth2 for
authentication. So, SAML is not a option to me. I need to integrate with
internal tools at Globo, and these tools work only with OAuth2.

@Sebastien
API are accessed by user, as today, using apikey and secretkey. OAuth is
only to UI.

@David,
Thank you. I will look this lib.

@Edukulla
I try to summarize your questions:

* In the code below, you can see all parameters needed to configure OAuth
authentication with Google, Github and Globo. My idea is that they are
global options (there are more global options than I sent before). Other
providers may not work, this list is for the first plugin release. People
can help integrating more providers.
* Pay attention with the way to get user email. This is really tricky,
because this is not part of specification. But if all providers answer json
and have a key named "email", the implementation will work. Google, Github
and Globo.co already do this.
* ClientId and secret may store encrypted in Global Options, like others
password.
* As the number of global options increase, I create an option called
"oauth2.enable", if you want to enable plugin. Of course,
if all parameters are not set, doesn't matter if plugin is enabled or not.
* The idea is not to authorize resources, is only to authenticate. So, I
don't need to store access key. When user accesses
UI, he is redirected to oauth2 server, and if authorization was granted
before, oauth2 server will redirect user to cloudstack without asking for
permission. This works with all 3 providers. I need only to have access to
email when the session starts, and then cloudstack keeps its own session.

The big problem is with domainid, that must be an global option. Other
problem happen with new accounts. May I create new user in an existed
account. The account name for new users are stored in global settings too.
At Globo.com we are discussing, but I guess if a user is not in cloudstack
they can't logged using oauth. Like happen in ldap.

Bellow I put some code to show that OAuth is not different between these 3
providers.

I will start the development of this feature next week. So, I will
appreciate all comments to help me to understand problems. After I start I
will create the design document.


------ BEGIN CODE -------
# before run: pip install requests_oauthlib
import sys
# Credentials you get from registering a new application
app = sys.argv[1]
print "For app %s" % repr(app)

redirect_uri = 'https://localhost:8000/'

if app == 'globo':
    client_id = 'XXX'
    client_secret = 'XXX'
    authorization_base_url = "XXX"
    token_url = "XXX"
    user_url = 'XXX'
    scope = []
elif app == 'github':
    client_id = 'XXX'
    client_secret = 'XXX'
    authorization_base_url = "https://github.com/login/oauth/authorize"
    token_url = "https://github.com/login/oauth/access_token"
    user_url = 'https://api.github.com/user'
    scope = ['user']
elif app == 'google':
    client_id = 'XXX'
    client_secret = 'XXX'
    authorization_base_url = "https://accounts.google.com/o/oauth2/auth"
    token_url = "https://accounts.google.com/o/oauth2/token"
    scope = [
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/userinfo.profile"
    ]
    user_url = 'https://www.googleapis.com/oauth2/v1/userinfo'
else:
    print "UNKNOW APP"
    sys.exit(1)

from requests_oauthlib import OAuth2Session
oauth2_session = OAuth2Session(client_id, scope=scope,
redirect_uri=redirect_uri)

authorization_url, state =
oauth2_session.authorization_url(authorization_base_url)
print 'Please go here and authorize,', authorization_url

# Get the authorization verifier code from the callback url
redirect_response = raw_input('Paste the full redirect URL here:')

# Fetch the access token
oauth2_session.fetch_token(token_url, client_secret=client_secret,
        authorization_response=redirect_response)

# Fetch a protected resource, i.e. user profile
r = oauth2_session.get(user_url)
print r.json()
print "email=", r.json()['email']
------ END CODE -------






On Tue, Jul 15, 2014 at 5:34 AM, David Nalley <da...@gnsa.us> wrote:

> https://oltu.apache.org <-- maybe as a starting place.
>
> On Tue, Jul 15, 2014 at 3:25 AM, Sebastien Goasguen <ru...@gmail.com>
> wrote:
> > Silvano,
> >
> > Seems to me you are doing it for browser based dashboard access only ?
> >
> > How about if I want to use the API straight up, how do you integrate an
> Oauth workflow there ?
> >
> > On Jul 15, 2014, at 1:35 AM, Santhosh Edukulla <
> santhosh.edukulla@citrix.com> wrote:
> >
> >> Hi Silvano,
> >>
> >> Few Notes:
> >>
> >> 1. We had implementation details mentioned i believe, but we didn't
> mentioned the design details and workflows.
> >> 2. We didn't mentioned whether it is 2 legged flow or 3 legged flow.
> >> 3. Not clear with this statement, "Once user is authorized by oauth2
> server, javascript code reads parameters in url",
> >> 4. Whats the difference between "oauth2.credentials.url" and
> "oauth2.baseurl", the later is redirect uri? If yes, Where will have
> redirect uri hosted?
> >> 5. referring to the statement " When oauth2.baseurl, oauth2.client.id
> and oauth2.client.secret are not set (default), oauthRequestUrl returns
> empty response and OAuth2
> >> authentication is turned off.",  can we use a flag to denote whether to
> use oauth flow or not? If set to false, dont use it otherwise continue with
> default.
> >> 6. What about refresh token,i believe access token has limited life
> time? Any call back mechanism to update with latest token if it gets
> expired?
> >> 7. Details like clientid,clientsecret needs to be encrypted when stored
> and retrieved from global config?
> >> 8. How do we map the user logged in to roles and hierarchy inside CS?
> based on email mapping?
> >> 9.  What is the significance of these two parameters  mentioned?
> >> oauth2.credentials.parameter.email (defaults to "email")
> >> * oauth2.domainid
> >> 10. clientid and clientsecret key are based upon per tenant basis, so
> what if we want to oauth mechanism from multiple tenants at any stage?
> >> 11. Default values for clientid and clientsecret are loaded at which
> stage? during initial installation and for which tenant?
> >> 12. How do we verify the validity of clientid and clientsecret values?
> If they are revoked? possibility of revoke is there?
> >> 13. If we understand, it is only to authenticate a user through oauth
> flow, we dont need authorization part inside of cs? I mean, what do we mean
> by authorization from tenant once access key is granted?
> >> 14. If access key is not stored, how do we get refresh token?
> >> 15. What is the default sequence of authentication in case if oauth
> fails? and order in which a given authentication mechanism will be chosen?
> >> 16. Can we also show a ui, where user can enable\disable oauth setting
> for a given account? here, possibility of mismatch with emailid based upon
> current implementation and oauth retrieved emailid post authentication is
> there? how do we handle it?
> >> 17. Last, what is the significance of this feature, apart from
> authentication support from third party clients?
> >>
> >>
> >> Thanks!
> >> Santhosh
> >> ________________________________________
> >> From: Silvano Nogueira Buback [silvano@corp.globo.com]
> >> Sent: Monday, July 14, 2014 4:59 PM
> >> To: dev@cloudstack.apache.org
> >> Subject: [PROPOSAL] OAuth2 Single SignOn Integration
> >>
> >> Hi gyus,
> >>
> >>    I need to implement OAuth2 integration to provide single sign-on with
> >> others tools in my company. I can share this implementation with the
> >> community if you are interested. I suggest these changes in code:
> >>
> >> 1. Create a new javascript called oauth2.js. This javascript is
> responsible
> >> for calling the new command called oauthRequestUrl that reads the global
> >> option "oauth2.baseurl" and returns this url plus "/authorize" with
> oauth2
> >> parameters. After receiving the answer, javascript redirects user to
> oauth2
> >> server.
> >> 2. Once user is authorized by oauth2 server, javascript code reads
> >> parameters in url and call oauthAuthorizeToken command. This command
> asks
> >> the oauth2 server by the access token, and if everything is ok, calls
> >> "oauth2.credentials.url" about user email and finds this user in the
> >> database, like ldap implementation does and returns authentication data.
> >> 3. Javascript fills g_loginResponse with answer from command and user is
> >> logged in.
> >>
> >>   What do you think about this approach?
> >>
> >>
> >> ---- More details ----
> >>
> >> Alternative flows:
> >>
> >> * When the url has parameter direct=true, the login dialog is shown.
> >> * When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are
> not
> >> set (default), oauthRequestUrl returns empty response and OAuth2
> >> authentication is turned off.
> >> * If authorization token is invalid, user is redirected again to oauth2
> >> server.
> >>
> >>
> >> Commands:
> >> * oauthRequestUrl
> >> * oauthAuthorizeToken
> >>
> >>
> >> Global Options:
> >> * oauth2.baseurl
> >> * oauth2.client.id
> >> * oauth2.client.secret
> >> * oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
> >> * oauth2.credentials.parameter.email (defaults to "email")
> >> * oauth2.domainid
> >>
> >>
> >> Restrictions:
> >> * Domain Id will be a global option
> >> * Users are always redirected to oauth2 server. Access tokens are not
> >> stored.
> >> * Before using Cloudstack, the administrator must insert user in an
> account.
> >
>

Re: [PROPOSAL] OAuth2 Single SignOn Integration

Posted by Silvano Nogueira Buback <si...@corp.globo.com>.
@Rohit Yadav
I know OAuth2 is tricky and the protocol was designed for authorization,
not authentication. But Globo.com, like many companies, use OAuth2 for
authentication. So, SAML is not a option to me. I need to integrate with
internal tools at Globo, and these tools work only with OAuth2.

@Sebastien
API are accessed by user, as today, using apikey and secretkey. OAuth is
only to UI.

@David,
Thank you. I will look this lib.

@Edukulla
I try to summarize your questions:

* In the code below, you can see all parameters needed to configure OAuth
authentication with Google, Github and Globo. My idea is that they are
global options (there are more global options than I sent before). Other
providers may not work, this list is for the first plugin release. People
can help integrating more providers.
* Pay attention with the way to get user email. This is really tricky,
because this is not part of specification. But if all providers answer json
and have a key named "email", the implementation will work. Google, Github
and Globo.co already do this.
* ClientId and secret may store encrypted in Global Options, like others
password.
* As the number of global options increase, I create an option called
"oauth2.enable", if you want to enable plugin. Of course,
if all parameters are not set, doesn't matter if plugin is enabled or not.
* The idea is not to authorize resources, is only to authenticate. So, I
don't need to store access key. When user accesses
UI, he is redirected to oauth2 server, and if authorization was granted
before, oauth2 server will redirect user to cloudstack without asking for
permission. This works with all 3 providers. I need only to have access to
email when the session starts, and then cloudstack keeps its own session.

The big problem is with domainid, that must be an global option. Other
problem happen with new accounts. May I create new user in an existed
account. The account name for new users are stored in global settings too.
At Globo.com we are discussing, but I guess if a user is not in cloudstack
they can't logged using oauth. Like happen in ldap.

Bellow I put some code to show that OAuth is not different between these 3
providers.

I will start the development of this feature next week. So, I will
appreciate all comments to help me to understand problems. After I start I
will create the design document.


------ BEGIN CODE -------
# before run: pip install requests_oauthlib
import sys
# Credentials you get from registering a new application
app = sys.argv[1]
print "For app %s" % repr(app)

redirect_uri = 'https://localhost:8000/'

if app == 'globo':
    client_id = 'XXX'
    client_secret = 'XXX'
    authorization_base_url = "XXX"
    token_url = "XXX"
    user_url = 'XXX'
    scope = []
elif app == 'github':
    client_id = 'XXX'
    client_secret = 'XXX'
    authorization_base_url = "https://github.com/login/oauth/authorize"
    token_url = "https://github.com/login/oauth/access_token"
    user_url = 'https://api.github.com/user'
    scope = ['user']
elif app == 'google':
    client_id = 'XXX'
    client_secret = 'XXX'
    authorization_base_url = "https://accounts.google.com/o/oauth2/auth"
    token_url = "https://accounts.google.com/o/oauth2/token"
    scope = [
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/userinfo.profile"
    ]
    user_url = 'https://www.googleapis.com/oauth2/v1/userinfo'
else:
    print "UNKNOW APP"
    sys.exit(1)

from requests_oauthlib import OAuth2Session
oauth2_session = OAuth2Session(client_id, scope=scope,
redirect_uri=redirect_uri)

authorization_url, state =
oauth2_session.authorization_url(authorization_base_url)
print 'Please go here and authorize,', authorization_url

# Get the authorization verifier code from the callback url
redirect_response = raw_input('Paste the full redirect URL here:')

# Fetch the access token
oauth2_session.fetch_token(token_url, client_secret=client_secret,
        authorization_response=redirect_response)

# Fetch a protected resource, i.e. user profile
r = oauth2_session.get(user_url)
print r.json()
print "email=", r.json()['email']
------ END CODE -------






On Tue, Jul 15, 2014 at 5:34 AM, David Nalley <da...@gnsa.us> wrote:

> https://oltu.apache.org <-- maybe as a starting place.
>
> On Tue, Jul 15, 2014 at 3:25 AM, Sebastien Goasguen <ru...@gmail.com>
> wrote:
> > Silvano,
> >
> > Seems to me you are doing it for browser based dashboard access only ?
> >
> > How about if I want to use the API straight up, how do you integrate an
> Oauth workflow there ?
> >
> > On Jul 15, 2014, at 1:35 AM, Santhosh Edukulla <
> santhosh.edukulla@citrix.com> wrote:
> >
> >> Hi Silvano,
> >>
> >> Few Notes:
> >>
> >> 1. We had implementation details mentioned i believe, but we didn't
> mentioned the design details and workflows.
> >> 2. We didn't mentioned whether it is 2 legged flow or 3 legged flow.
> >> 3. Not clear with this statement, "Once user is authorized by oauth2
> server, javascript code reads parameters in url",
> >> 4. Whats the difference between "oauth2.credentials.url" and
> "oauth2.baseurl", the later is redirect uri? If yes, Where will have
> redirect uri hosted?
> >> 5. referring to the statement " When oauth2.baseurl, oauth2.client.id
> and oauth2.client.secret are not set (default), oauthRequestUrl returns
> empty response and OAuth2
> >> authentication is turned off.",  can we use a flag to denote whether to
> use oauth flow or not? If set to false, dont use it otherwise continue with
> default.
> >> 6. What about refresh token,i believe access token has limited life
> time? Any call back mechanism to update with latest token if it gets
> expired?
> >> 7. Details like clientid,clientsecret needs to be encrypted when stored
> and retrieved from global config?
> >> 8. How do we map the user logged in to roles and hierarchy inside CS?
> based on email mapping?
> >> 9.  What is the significance of these two parameters  mentioned?
> >> oauth2.credentials.parameter.email (defaults to "email")
> >> * oauth2.domainid
> >> 10. clientid and clientsecret key are based upon per tenant basis, so
> what if we want to oauth mechanism from multiple tenants at any stage?
> >> 11. Default values for clientid and clientsecret are loaded at which
> stage? during initial installation and for which tenant?
> >> 12. How do we verify the validity of clientid and clientsecret values?
> If they are revoked? possibility of revoke is there?
> >> 13. If we understand, it is only to authenticate a user through oauth
> flow, we dont need authorization part inside of cs? I mean, what do we mean
> by authorization from tenant once access key is granted?
> >> 14. If access key is not stored, how do we get refresh token?
> >> 15. What is the default sequence of authentication in case if oauth
> fails? and order in which a given authentication mechanism will be chosen?
> >> 16. Can we also show a ui, where user can enable\disable oauth setting
> for a given account? here, possibility of mismatch with emailid based upon
> current implementation and oauth retrieved emailid post authentication is
> there? how do we handle it?
> >> 17. Last, what is the significance of this feature, apart from
> authentication support from third party clients?
> >>
> >>
> >> Thanks!
> >> Santhosh
> >> ________________________________________
> >> From: Silvano Nogueira Buback [silvano@corp.globo.com]
> >> Sent: Monday, July 14, 2014 4:59 PM
> >> To: dev@cloudstack.apache.org
> >> Subject: [PROPOSAL] OAuth2 Single SignOn Integration
> >>
> >> Hi gyus,
> >>
> >>    I need to implement OAuth2 integration to provide single sign-on with
> >> others tools in my company. I can share this implementation with the
> >> community if you are interested. I suggest these changes in code:
> >>
> >> 1. Create a new javascript called oauth2.js. This javascript is
> responsible
> >> for calling the new command called oauthRequestUrl that reads the global
> >> option "oauth2.baseurl" and returns this url plus "/authorize" with
> oauth2
> >> parameters. After receiving the answer, javascript redirects user to
> oauth2
> >> server.
> >> 2. Once user is authorized by oauth2 server, javascript code reads
> >> parameters in url and call oauthAuthorizeToken command. This command
> asks
> >> the oauth2 server by the access token, and if everything is ok, calls
> >> "oauth2.credentials.url" about user email and finds this user in the
> >> database, like ldap implementation does and returns authentication data.
> >> 3. Javascript fills g_loginResponse with answer from command and user is
> >> logged in.
> >>
> >>   What do you think about this approach?
> >>
> >>
> >> ---- More details ----
> >>
> >> Alternative flows:
> >>
> >> * When the url has parameter direct=true, the login dialog is shown.
> >> * When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are
> not
> >> set (default), oauthRequestUrl returns empty response and OAuth2
> >> authentication is turned off.
> >> * If authorization token is invalid, user is redirected again to oauth2
> >> server.
> >>
> >>
> >> Commands:
> >> * oauthRequestUrl
> >> * oauthAuthorizeToken
> >>
> >>
> >> Global Options:
> >> * oauth2.baseurl
> >> * oauth2.client.id
> >> * oauth2.client.secret
> >> * oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
> >> * oauth2.credentials.parameter.email (defaults to "email")
> >> * oauth2.domainid
> >>
> >>
> >> Restrictions:
> >> * Domain Id will be a global option
> >> * Users are always redirected to oauth2 server. Access tokens are not
> >> stored.
> >> * Before using Cloudstack, the administrator must insert user in an
> account.
> >
>

Re: [PROPOSAL] OAuth2 Single SignOn Integration

Posted by David Nalley <da...@gnsa.us>.
https://oltu.apache.org <-- maybe as a starting place.

On Tue, Jul 15, 2014 at 3:25 AM, Sebastien Goasguen <ru...@gmail.com> wrote:
> Silvano,
>
> Seems to me you are doing it for browser based dashboard access only ?
>
> How about if I want to use the API straight up, how do you integrate an Oauth workflow there ?
>
> On Jul 15, 2014, at 1:35 AM, Santhosh Edukulla <sa...@citrix.com> wrote:
>
>> Hi Silvano,
>>
>> Few Notes:
>>
>> 1. We had implementation details mentioned i believe, but we didn't mentioned the design details and workflows.
>> 2. We didn't mentioned whether it is 2 legged flow or 3 legged flow.
>> 3. Not clear with this statement, "Once user is authorized by oauth2 server, javascript code reads parameters in url",
>> 4. Whats the difference between "oauth2.credentials.url" and "oauth2.baseurl", the later is redirect uri? If yes, Where will have redirect uri hosted?
>> 5. referring to the statement " When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not set (default), oauthRequestUrl returns empty response and OAuth2
>> authentication is turned off.",  can we use a flag to denote whether to use oauth flow or not? If set to false, dont use it otherwise continue with default.
>> 6. What about refresh token,i believe access token has limited life time? Any call back mechanism to update with latest token if it gets expired?
>> 7. Details like clientid,clientsecret needs to be encrypted when stored and retrieved from global config?
>> 8. How do we map the user logged in to roles and hierarchy inside CS? based on email mapping?
>> 9.  What is the significance of these two parameters  mentioned?
>> oauth2.credentials.parameter.email (defaults to "email")
>> * oauth2.domainid
>> 10. clientid and clientsecret key are based upon per tenant basis, so what if we want to oauth mechanism from multiple tenants at any stage?
>> 11. Default values for clientid and clientsecret are loaded at which stage? during initial installation and for which tenant?
>> 12. How do we verify the validity of clientid and clientsecret values? If they are revoked? possibility of revoke is there?
>> 13. If we understand, it is only to authenticate a user through oauth flow, we dont need authorization part inside of cs? I mean, what do we mean by authorization from tenant once access key is granted?
>> 14. If access key is not stored, how do we get refresh token?
>> 15. What is the default sequence of authentication in case if oauth fails? and order in which a given authentication mechanism will be chosen?
>> 16. Can we also show a ui, where user can enable\disable oauth setting for a given account? here, possibility of mismatch with emailid based upon current implementation and oauth retrieved emailid post authentication is there? how do we handle it?
>> 17. Last, what is the significance of this feature, apart from authentication support from third party clients?
>>
>>
>> Thanks!
>> Santhosh
>> ________________________________________
>> From: Silvano Nogueira Buback [silvano@corp.globo.com]
>> Sent: Monday, July 14, 2014 4:59 PM
>> To: dev@cloudstack.apache.org
>> Subject: [PROPOSAL] OAuth2 Single SignOn Integration
>>
>> Hi gyus,
>>
>>    I need to implement OAuth2 integration to provide single sign-on with
>> others tools in my company. I can share this implementation with the
>> community if you are interested. I suggest these changes in code:
>>
>> 1. Create a new javascript called oauth2.js. This javascript is responsible
>> for calling the new command called oauthRequestUrl that reads the global
>> option "oauth2.baseurl" and returns this url plus "/authorize" with oauth2
>> parameters. After receiving the answer, javascript redirects user to oauth2
>> server.
>> 2. Once user is authorized by oauth2 server, javascript code reads
>> parameters in url and call oauthAuthorizeToken command. This command asks
>> the oauth2 server by the access token, and if everything is ok, calls
>> "oauth2.credentials.url" about user email and finds this user in the
>> database, like ldap implementation does and returns authentication data.
>> 3. Javascript fills g_loginResponse with answer from command and user is
>> logged in.
>>
>>   What do you think about this approach?
>>
>>
>> ---- More details ----
>>
>> Alternative flows:
>>
>> * When the url has parameter direct=true, the login dialog is shown.
>> * When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not
>> set (default), oauthRequestUrl returns empty response and OAuth2
>> authentication is turned off.
>> * If authorization token is invalid, user is redirected again to oauth2
>> server.
>>
>>
>> Commands:
>> * oauthRequestUrl
>> * oauthAuthorizeToken
>>
>>
>> Global Options:
>> * oauth2.baseurl
>> * oauth2.client.id
>> * oauth2.client.secret
>> * oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
>> * oauth2.credentials.parameter.email (defaults to "email")
>> * oauth2.domainid
>>
>>
>> Restrictions:
>> * Domain Id will be a global option
>> * Users are always redirected to oauth2 server. Access tokens are not
>> stored.
>> * Before using Cloudstack, the administrator must insert user in an account.
>

Re: [PROPOSAL] OAuth2 Single SignOn Integration

Posted by Sebastien Goasguen <ru...@gmail.com>.
Silvano,

Seems to me you are doing it for browser based dashboard access only ?

How about if I want to use the API straight up, how do you integrate an Oauth workflow there ?

On Jul 15, 2014, at 1:35 AM, Santhosh Edukulla <sa...@citrix.com> wrote:

> Hi Silvano,
> 
> Few Notes:
> 
> 1. We had implementation details mentioned i believe, but we didn't mentioned the design details and workflows. 
> 2. We didn't mentioned whether it is 2 legged flow or 3 legged flow.
> 3. Not clear with this statement, "Once user is authorized by oauth2 server, javascript code reads parameters in url",
> 4. Whats the difference between "oauth2.credentials.url" and "oauth2.baseurl", the later is redirect uri? If yes, Where will have redirect uri hosted?
> 5. referring to the statement " When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not set (default), oauthRequestUrl returns empty response and OAuth2
> authentication is turned off.",  can we use a flag to denote whether to use oauth flow or not? If set to false, dont use it otherwise continue with default.
> 6. What about refresh token,i believe access token has limited life time? Any call back mechanism to update with latest token if it gets expired?
> 7. Details like clientid,clientsecret needs to be encrypted when stored and retrieved from global config?
> 8. How do we map the user logged in to roles and hierarchy inside CS? based on email mapping?
> 9.  What is the significance of these two parameters  mentioned?
> oauth2.credentials.parameter.email (defaults to "email")
> * oauth2.domainid
> 10. clientid and clientsecret key are based upon per tenant basis, so what if we want to oauth mechanism from multiple tenants at any stage? 
> 11. Default values for clientid and clientsecret are loaded at which stage? during initial installation and for which tenant?
> 12. How do we verify the validity of clientid and clientsecret values? If they are revoked? possibility of revoke is there? 
> 13. If we understand, it is only to authenticate a user through oauth flow, we dont need authorization part inside of cs? I mean, what do we mean by authorization from tenant once access key is granted?
> 14. If access key is not stored, how do we get refresh token?
> 15. What is the default sequence of authentication in case if oauth fails? and order in which a given authentication mechanism will be chosen?
> 16. Can we also show a ui, where user can enable\disable oauth setting for a given account? here, possibility of mismatch with emailid based upon current implementation and oauth retrieved emailid post authentication is there? how do we handle it?
> 17. Last, what is the significance of this feature, apart from authentication support from third party clients? 
> 
> 
> Thanks!
> Santhosh
> ________________________________________
> From: Silvano Nogueira Buback [silvano@corp.globo.com]
> Sent: Monday, July 14, 2014 4:59 PM
> To: dev@cloudstack.apache.org
> Subject: [PROPOSAL] OAuth2 Single SignOn Integration
> 
> Hi gyus,
> 
>    I need to implement OAuth2 integration to provide single sign-on with
> others tools in my company. I can share this implementation with the
> community if you are interested. I suggest these changes in code:
> 
> 1. Create a new javascript called oauth2.js. This javascript is responsible
> for calling the new command called oauthRequestUrl that reads the global
> option "oauth2.baseurl" and returns this url plus "/authorize" with oauth2
> parameters. After receiving the answer, javascript redirects user to oauth2
> server.
> 2. Once user is authorized by oauth2 server, javascript code reads
> parameters in url and call oauthAuthorizeToken command. This command asks
> the oauth2 server by the access token, and if everything is ok, calls
> "oauth2.credentials.url" about user email and finds this user in the
> database, like ldap implementation does and returns authentication data.
> 3. Javascript fills g_loginResponse with answer from command and user is
> logged in.
> 
>   What do you think about this approach?
> 
> 
> ---- More details ----
> 
> Alternative flows:
> 
> * When the url has parameter direct=true, the login dialog is shown.
> * When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not
> set (default), oauthRequestUrl returns empty response and OAuth2
> authentication is turned off.
> * If authorization token is invalid, user is redirected again to oauth2
> server.
> 
> 
> Commands:
> * oauthRequestUrl
> * oauthAuthorizeToken
> 
> 
> Global Options:
> * oauth2.baseurl
> * oauth2.client.id
> * oauth2.client.secret
> * oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
> * oauth2.credentials.parameter.email (defaults to "email")
> * oauth2.domainid
> 
> 
> Restrictions:
> * Domain Id will be a global option
> * Users are always redirected to oauth2 server. Access tokens are not
> stored.
> * Before using Cloudstack, the administrator must insert user in an account.


RE: [PROPOSAL] OAuth2 Single SignOn Integration

Posted by Santhosh Edukulla <sa...@citrix.com>.
Hi Silvano,

Few Notes:

1. We had implementation details mentioned i believe, but we didn't mentioned the design details and workflows. 
2. We didn't mentioned whether it is 2 legged flow or 3 legged flow.
3. Not clear with this statement, "Once user is authorized by oauth2 server, javascript code reads parameters in url",
4. Whats the difference between "oauth2.credentials.url" and "oauth2.baseurl", the later is redirect uri? If yes, Where will have redirect uri hosted?
5. referring to the statement " When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not set (default), oauthRequestUrl returns empty response and OAuth2
authentication is turned off.",  can we use a flag to denote whether to use oauth flow or not? If set to false, dont use it otherwise continue with default.
6. What about refresh token,i believe access token has limited life time? Any call back mechanism to update with latest token if it gets expired?
7. Details like clientid,clientsecret needs to be encrypted when stored and retrieved from global config?
8. How do we map the user logged in to roles and hierarchy inside CS? based on email mapping?
9.  What is the significance of these two parameters  mentioned?
oauth2.credentials.parameter.email (defaults to "email")
* oauth2.domainid
10. clientid and clientsecret key are based upon per tenant basis, so what if we want to oauth mechanism from multiple tenants at any stage? 
11. Default values for clientid and clientsecret are loaded at which stage? during initial installation and for which tenant?
12. How do we verify the validity of clientid and clientsecret values? If they are revoked? possibility of revoke is there? 
13. If we understand, it is only to authenticate a user through oauth flow, we dont need authorization part inside of cs? I mean, what do we mean by authorization from tenant once access key is granted?
14. If access key is not stored, how do we get refresh token?
15. What is the default sequence of authentication in case if oauth fails? and order in which a given authentication mechanism will be chosen?
16. Can we also show a ui, where user can enable\disable oauth setting for a given account? here, possibility of mismatch with emailid based upon current implementation and oauth retrieved emailid post authentication is there? how do we handle it?
17. Last, what is the significance of this feature, apart from authentication support from third party clients? 
 

Thanks!
Santhosh
________________________________________
From: Silvano Nogueira Buback [silvano@corp.globo.com]
Sent: Monday, July 14, 2014 4:59 PM
To: dev@cloudstack.apache.org
Subject: [PROPOSAL] OAuth2 Single SignOn Integration

Hi gyus,

    I need to implement OAuth2 integration to provide single sign-on with
others tools in my company. I can share this implementation with the
community if you are interested. I suggest these changes in code:

1. Create a new javascript called oauth2.js. This javascript is responsible
for calling the new command called oauthRequestUrl that reads the global
option "oauth2.baseurl" and returns this url plus "/authorize" with oauth2
parameters. After receiving the answer, javascript redirects user to oauth2
server.
2. Once user is authorized by oauth2 server, javascript code reads
parameters in url and call oauthAuthorizeToken command. This command asks
the oauth2 server by the access token, and if everything is ok, calls
"oauth2.credentials.url" about user email and finds this user in the
database, like ldap implementation does and returns authentication data.
3. Javascript fills g_loginResponse with answer from command and user is
logged in.

   What do you think about this approach?


---- More details ----

Alternative flows:

* When the url has parameter direct=true, the login dialog is shown.
* When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not
set (default), oauthRequestUrl returns empty response and OAuth2
authentication is turned off.
* If authorization token is invalid, user is redirected again to oauth2
server.


Commands:
* oauthRequestUrl
* oauthAuthorizeToken


Global Options:
* oauth2.baseurl
* oauth2.client.id
* oauth2.client.secret
* oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
* oauth2.credentials.parameter.email (defaults to "email")
* oauth2.domainid


Restrictions:
* Domain Id will be a global option
* Users are always redirected to oauth2 server. Access tokens are not
stored.
* Before using Cloudstack, the administrator must insert user in an account.

Re: [PROPOSAL] OAuth2 Single SignOn Integration

Posted by Rohit Yadav <ro...@shapeblue.com>.
Hi Silvano,

Thanks for sharing your work. Is it already complete or work in progress?

OAuth2 is tricky, as it (the spec and general implementation) leaves out
room for token assertion/validation mechanism, communication between
resource server and auth server, and it's dependent on the authorizing
server so the way facebook, google, github and your private org would do
for example is slightly different. I also think OAuth2 does not do
authentication but only authorization (authentication and authorization
are different) which makes things like tracking deletion/change in user
difficult.

I would recommend you add a spec/design document on cwiki.a.o?

I think if an admin has to add user before OAuth works, it sort of kills
the purpose of OAuth2. So I want to discuss how would you support
creation of users (account, domain, role etc.), handle
deletions/changes, logout etc.

I was going to post a proposal today as well on supporting alternative
auth mechanisms, perhaps refactoring/creating an auth framework for
CloudStack.

JFYI, I'm researching around SAML2 support for ACS and have a document:
https://cwiki.apache.org/confluence/display/CLOUDSTACK/SAML+2.0+Plugin

Will share more details in another thread later today.

Regards.

Silvano Nogueira Buback wrote:
> Hi gyus,
>
>      I need to implement OAuth2 integration to provide single sign-on with
> others tools in my company. I can share this implementation with the
> community if you are interested. I suggest these changes in code:
>
> 1. Create a new javascript called oauth2.js. This javascript is responsible
> for calling the new command called oauthRequestUrl that reads the global
> option "oauth2.baseurl" and returns this url plus "/authorize" with oauth2
> parameters. After receiving the answer, javascript redirects user to oauth2
> server.
> 2. Once user is authorized by oauth2 server, javascript code reads
> parameters in url and call oauthAuthorizeToken command. This command asks
> the oauth2 server by the access token, and if everything is ok, calls
> "oauth2.credentials.url" about user email and finds this user in the
> database, like ldap implementation does and returns authentication data.
> 3. Javascript fills g_loginResponse with answer from command and user is
> logged in.
>
>     What do you think about this approach?
>
>
> ---- More details ----
>
> Alternative flows:
>
> * When the url has parameter direct=true, the login dialog is shown.
> * When oauth2.baseurl, oauth2.client.id and oauth2.client.secret are not
> set (default), oauthRequestUrl returns empty response and OAuth2
> authentication is turned off.
> * If authorization token is invalid, user is redirected again to oauth2
> server.
>
>
> Commands:
> * oauthRequestUrl
> * oauthAuthorizeToken
>
>
> Global Options:
> * oauth2.baseurl
> * oauth2.client.id
> * oauth2.client.secret
> * oauth2.credentials.url: defaults to "/oauth2/v2/userinfo"
> * oauth2.credentials.parameter.email (defaults to "email")
> * oauth2.domainid
>
>
> Restrictions:
> * Domain Id will be a global option
> * Users are always redirected to oauth2 server. Access tokens are not
> stored.
> * Before using Cloudstack, the administrator must insert user in an account.
>

--
Rohit Yadav
Software Architect, ShapeBlue
M. +91 88 262 30892 | rohit.yadav@shapeblue.com
Blog: bhaisaab.org | Twitter: @_bhaisaab


Find out more about ShapeBlue and our range of CloudStack related services

IaaS Cloud Design & Build<http://shapeblue.com/iaas-cloud-design-and-build//>
CSForge – rapid IaaS deployment framework<http://shapeblue.com/csforge/>
CloudStack Consulting<http://shapeblue.com/cloudstack-consultancy/>
CloudStack Infrastructure Support<http://shapeblue.com/cloudstack-infrastructure-support/>
CloudStack Bootcamp Training Courses<http://shapeblue.com/cloudstack-training/>

This email and any attachments to it may be confidential and are intended solely for the use of the individual to whom it is addressed. Any views or opinions expressed are solely those of the author and do not necessarily represent those of Shape Blue Ltd or related companies. If you are not the intended recipient of this email, you must neither take any action based upon its contents, nor copy or show it to anyone. Please contact the sender if you believe you have received this email in error. Shape Blue Ltd is a company incorporated in England & Wales. ShapeBlue Services India LLP is a company incorporated in India and is operated under license from Shape Blue Ltd. Shape Blue Brasil Consultoria Ltda is a company incorporated in Brasil and is operated under license from Shape Blue Ltd. ShapeBlue is a registered trademark.