Hi all,

to give an update on this issue: we're still stuck but got to know the
internals of Shindig a bit better.

The key file was indeed incorrect, thanks Jesse for the script to generate
the key. Should we document this script or add it to the code base?

CommonContainerSecurityTokenCodec does use BlobCrypterSecurityTokenCodec by
default but its createToken method returns TestSecurityTokenCodec while
DefaultContainerSecurityCodes#createToken returns a
BlobCrypterSecurityToken.

We've added a shindig.auth.updateSecurityToken('${securityToken}'); before
rendering the widgets and this method seems to work but not for the actual
rendering of the gadgets.

When in rave_opensocialapi.js calls container.navigateGadget(gadgetSite,
gadget.widgetUrl, {}, renderParams); we get an iframeUrl with security token
%st% back. This is a result
of DefaultIframeUriManager#generateSecurityToken. It tries to encode an
AnonymousSecurityToken in the BlobCrypterSecurityTokenCodec class which
fails.
We can block the AnonymousSecurityToken by overriding SocialApiGuiceModel
but then the gadget is not rendered at all because the metadata call in
OpenSocialWidgetRenderer fails.

We're lost now in the magic that happens inside container.navigateGadget so
I've just posted a question on the Shindig-dev list how we can get an
iframeUrl with a valid security token.

Regards,

Jasha Joachimsthal

Europe - Amsterdam - Oosteinde 11, 1017 WT Amsterdam - +31(0)20 522 4466
US - Boston - 1 Broadway, Cambridge, MA 02142 - +1 877 414 4776 (toll free)

www.onehippo.com


On 23 June 2011 17:34, Okke Harsta <[email protected]> wrote:

> Hi Jesse,
>
> Thanks for comments. We are trying to implement it step-by-step, but things
> are kind of related in order to be able to install a gadget that does a
> three-legged call to Google to retrieve all of your contacts. Basically the
> missing piece of information is how we can get the client side (= rave
> server side) generated securityToken into the osapi call (for now Shindig
> produces an error when encountering the default token %st%).
>
> In the SURFconext codebase we did this by putting the securityToken on the
> gadget in JavaScript and calling shindig.container.addGadget:
>
> gadget.secureToken = COIN.securityTokens[i]; //generated by the Portal
> server
> gadget.userPrefs = shindig.container.userPrefStore.getPrefs(gadget);
> shindig.container.addGadget(gadget);
> }
> shindig.container.layoutManager.setGadgetChromeIds(gadgetIds);
> shindig.container.renderGadgets();
>
> However with the new common container (
> http://comments.gmane.org/gmane.comp.web.shindig.devel/6286) this seems to
> have changed.
>
> You're right about the pem file, but that is a naming miscommunication:
> gadgets.securityTokenKeyFile does point to our key file which we also use to
> create the token on the Rave server:
>
>  BasicBlobCrypter blobCrypter = new BasicBlobCrypter(key.getBytes());
>  BlobCrypterSecurityToken st = new BlobCrypterSecurityToken(blobCrypter,
>          environment.getContainerName(), "localhost");
> st.setViewerId(personId);
> st.setOwnerId(personId);
> st.setAppUrl(gadget.getDefinition().getUrl());
> String token = Utf8UrlCoder.encode(st.encrypt());
>
> To be continued...
>
> Okke
>
> On Jun 23, 2011, at 4:47 PM, Ciancetta, Jesse E. wrote:
>
> > Hi Jasha,
> >
> > Great to hear that you and Okke are taking this on -- it will be awesome
> to get security tokens and three-legged OAuth working.
> >
> > One thing that might help in general is to just try to do one piece of
> this at a time -- it sounds like your trying to get security tokens,
> three-legged OAuth and alternate config location loading all working at the
> same time which seems like it could be a challenge...  It would also
> probably be good to commit them one piece at a time too so it will be easier
> for us to understand what the changes do (which you may have been planning
> to do already).
> >
> > When I ran through this when working on OSEC internally I ran into issues
> with getting all the right keys setup in the right format and in the right
> place.  I ended up creating a script to create everything which I thought
> might be helpful (I did some search/replace on paths and filenames so
> hopefully I didn't break anything):
> >
> > ----------- BEGIN SCRIPT -----------
> >
> > # local variables
> > TMP_DIR=/tmp/shindig_keys
> > PRIVATE_KEYS_DIR=/private/shindig/keys
> > PUBLIC_KEYS_DIR=/public/shindig/keys
> >
> > # make a temporary working directory
> > mkdir $TMP_DIR
> > cd $TMP_DIR
> >
> > # generate our private key -- this is used by shindig to sign requests
> > openssl genrsa -out portal_signing_key_private.pem 1024
> >
> > # create a pkcs8 formatted version of the private key -- this seems to be
> the format shindig wants the key in -- this is the filed used for
> shindig.signing.key-file in shindig.properties
> > openssl pkcs8 -in portal_signing_key_private.pem -out
> portal_signing_key_private.pk8.pem -topk8 -nocrypt -outform PEM
> >
> > # create a DER formatted version of the private key in case we need it at
> some point in the future
> > openssl rsa -in portal_signing_key_private.pem -inform PEM -out
> portal_signing_key_private.der -outform DER
> >
> > # export the public key component in PEM format - this could be used by
> third parties to validate the signature on shindig signed requests
> > openssl rsa -in portal_signing_key_private.pem -pubout -out
> portal_signing_key_public.pem
> >
> > # export the public key component in DER format - this could be used by
> third parties to validate the signature on shindig signed requests
> > openssl rsa -in portal_signing_key_private.pem -pubout -out
> portal_signing_key_public.der -outform DER
> >
> > # create a new x509 certificate based on our private key and write it out
> in PEM format - this could be used by third parties to validate the
> signature on shindig signed requests
> > openssl req -new -x509 -key portal_signing_key_private.pem -days 365
> -subj '/C=US/ST=MA/L=Bedford/O=Foo Corp/OU=Foo/CN=foo.example.com' -out
> portal_signing_certificate.pem
> >
> > # create a DER formatted version of our x509 certificate - this could be
> used by third parties to validate the signature on shindig signed requests
> > openssl x509 -in portal_signing_certificate.pem -inform PEM -out
> portal_signing_certificate.der -outform DER
> >
> > # generate the shared secret symmetric key used by shindig and the
> container to encrypt/decrypt gadget security tokens - this is the file used
> for gadgets.securityTokenKeyFile in container.js
> > dd if=/dev/random bs=32 count=1 | openssl base64 >
> portal_security_token_key_private.txt
> >
> > # copy the private keys to the shindig directory
> > mkdir $PRIVATE_KEYS_DIR
> > cp portal_signing_key_private.* portal_security_token_key_private.txt
> $PRIVATE_KEYS_DIR
> >
> > # copy the public keys and certificates to the public keys directory
> > mkdir $PUBLIC_KEYS_DIR
> > cp portal_signing_key_public.* portal_signing_certificate.*
> $PUBLIC_KEYS_DIR
> >
> > # remove the temporary working directory
> > cd ..
> > rm -rf $TMP_DIR
> >
> > ----------- END SCRIPT -----------
> >
> > Additional comments inline below.
> >
> >> -----Original Message-----
> >> From: Jasha Joachimsthal [mailto:[email protected]]
> >> Sent: Thursday, June 23, 2011 8:37 AM
> >> To: [email protected]
> >> Subject: 3-legged oAuth
> >>
> >> Hi,
> >>
> >> Okke and I are trying to get 3 legged oAuth working in Rave but we're
> stuck
> >> on passing the security token from the portal to the (Shindig)
> container.
> >>
> >> What we have done so far:
> >> - generated a key using
> >> openssl req -newkey rsa:1024 -days 365 -nodes -x509 -keyout testkey.pem
> -
> >> out
> >> testkey.pem -subj '/CN=mytestkey'
> >> openssl pkcs8 -in testkey.pem -out oauthkey.pem -topk8 -nocrypt -outform
> >> PEM
> >>
> >> - replaced the default PropertiesModule by a properties module that can
> read
> >> shindig properties from a different location
> >> - configured our custom shindig properties to read a different
> container.js
> >> in shindig.containers.default
> >>
> >> In our custom container.js we've set
> >> "gadgets.securityTokenType" : "secure",
> >> "gadgets.securityTokenKeyFile" : "/path/to/my/oauthkey.pem", #with the
> >> correct location of course
> >
> > This doesn't look like the right file to me -- this looks like what
> shindig might use to sign outbound requests, not what's used for
> encrypting/decrypting security tokens.  Using the script I posted above I
> think the path you'd want would be:
> >
> > /private/shindig/keys/portal_security_token_key_private.txt
> >
> >>
> >> - replaced CommonContainerAuthGuiceModule with a custom module that
> >> loads DefaultSecurityTokenCodec instad of
> >> CommonContainerSecurityTokenCodec
> >
> > It doesn't seem like you should need to do this -- the
> CommonContainerSecurityTokenCodec seems to use the
> BlobCrypterSecurityTokenCodec by default when it encounters the "secure"
> configuration in container.js which seems like what we want.
> >
> >> - added a gadget from http://gadgets.jasha.eu/oauthtest.xml (and
> registered
> >> the domain in Google)
> >> - added consumer key & secret to config/oauth.json
> >>
> >> Now when I add this gadget to my page, I get a HTTP Status 401 -
> Malformed
> >> security token %st% Invalid security token %st% response.
> >> This makes sense because the portal doesn't pass a security token to
> >> Shindig. This could be done by generating a token like in [1], in Rave
> >> probably in OpenSocialWidgetRenderer, and then set the security token to
> >
> > Agreed -- OpenSocialWidgetRenderer does seem like the right place to do
> this but I think I might push the actual work off to a SecurityTokenService
> which takes the RegionWidget and returns the SecurityToken.  We've updated
> our SecurityTokenService internally and haven't pushed those updates back to
> the public OSEC codebase yet, so I'll post what we have currently here in
> hopes that it might be useful (the changes from what's in public OSEC
> currently is basically that this service no longer does any URL encoding of
> the values it deals with since this really isn't the right architectural
> layer to be doing that work):
> >
> > ----------
> >
> > import org.apache.shindig.auth.SecurityToken;
> > import org.mitre.portal.model.PersonGadget;
> > import org.mitre.portal.service.exception.SecurityTokenException;
> >
> > public interface SecurityTokenService {
> >    SecurityToken getSecurityToken(PersonGadget personGadget) throws
> SecurityTokenException;
> >    String getEncryptedSecurityToken(PersonGadget personGadget) throws
> SecurityTokenException;
> >    SecurityToken decryptSecurityToken(String encryptedSecurityToken)
> throws SecurityTokenException;
> >    String refreshEncryptedSecurityToken(String encryptedSecurityToken)
> throws SecurityTokenException;
> > }
> >
> > ----------
> >
> > import org.apache.commons.logging.Log;
> > import org.apache.commons.logging.LogFactory;
> > import org.apache.shindig.auth.BlobCrypterSecurityToken;
> > import org.apache.shindig.auth.MitreBlobCrypterSecurityToken;
> > import org.apache.shindig.auth.SecurityToken;
> > import org.apache.shindig.common.crypto.BasicBlobCrypter;
> > import org.apache.shindig.common.crypto.BlobCrypter;
> > import org.apache.shindig.common.util.CharsetUtil;
> > import org.mitre.portal.model.Gadget;
> > import org.mitre.portal.model.Person;
> > import org.mitre.portal.model.PersonGadget;
> > import org.mitre.portal.security.SecurityTokenService;
> > import org.mitre.portal.security.UserService;
> > import org.mitre.portal.service.exception.SecurityTokenException;
> > import org.springframework.beans.factory.annotation.Autowired;
> > import org.springframework.security.access.prepost.PreAuthorize;
> > import org.springframework.stereotype.Service;
> >
> > import java.io.File;
> > import java.io.IOException;
> > import java.net.MalformedURLException;
> > import java.net.URL;
> > import java.util.ResourceBundle;
> >
> > @Service(value = "securityTokenService")
> > public class EncryptedBlobSecurityTokenService implements
> SecurityTokenService {
> >    private static final Log log =
> LogFactory.getLog(EncryptedBlobSecurityTokenService.class);
> >
> >    private static final String SECURITY_PROPERTIES = "application";
> >    private static final String ENCRYPTION_PROPERTY =
> "security.encryptionkey";
> >    private static final String EMBEDDED_KEY_PREFIX = "embedded:";
> >    //TODO - should this be read in from a properties file?
> >    private static final String CONTAINER = "default";
> >    private static final String DOMAIN = " default";
> >    private static final String CHAR_ENCODING = "UTF-8";
> >
> >    private final UserService userService;
> >    private final BlobCrypter blobCrypter;
> >
> >    @Autowired
> >    public EncryptedBlobSecurityTokenService(UserService userService) {
> >        this.userService = userService;
> >
> >        ResourceBundle resourceBundle =
> ResourceBundle.getBundle(SECURITY_PROPERTIES);
> >        if (resourceBundle.containsKey(ENCRYPTION_PROPERTY)) {
> >            String key = resourceBundle.getString(ENCRYPTION_PROPERTY);
> >            if (key.startsWith(EMBEDDED_KEY_PREFIX)) {
> >                byte[] encryptionKey =
> CharsetUtil.getUtf8Bytes(key.substring(EMBEDDED_KEY_PREFIX.length()));
> >                this.blobCrypter = new BasicBlobCrypter(encryptionKey);
> >            } else {
> >                try {
> >                    this.blobCrypter = new BasicBlobCrypter(new
> File(key));
> >                } catch (IOException e) {
> >                    throw new SecurityException("Unable to load encryption
> key from file: " + key);
> >                }
> >            }
> >        } else {
> >            throw new SecurityException("Unable to find encryption key!");
> >        }
> >    }
> >
> >    @Override
> >    @PreAuthorize("#personGadget.userId == principal.userId")
> >    public SecurityToken getSecurityToken(PersonGadget personGadget)
> throws SecurityTokenException {
> >        return this.getBlobCrypterSecurityToken(personGadget);
> >    }
> >
> >    @Override
> >    @PreAuthorize("#personGadget.userId == principal.userId")
> >    public String getEncryptedSecurityToken(PersonGadget personGadget)
> throws SecurityTokenException {
> >        String encryptedToken = null;
> >
> >        try {
> >            BlobCrypterSecurityToken securityToken =
> this.getBlobCrypterSecurityToken(personGadget);
> >            encryptedToken = this.encryptSecurityToken(securityToken);
> >        } catch (Exception e) {
> >            throw new SecurityTokenException("Error creating security
> token from person gadget", e);
> >        }
> >
> >        return encryptedToken;
> >    }
> >
> >    @Override
> >    public SecurityToken decryptSecurityToken(String
> encryptedSecurityToken) throws SecurityTokenException {
> >        SecurityToken securityToken;
> >
> >        try {
> >            log.debug("Decrypting security token: " +
> encryptedSecurityToken);
> >
> >            //Remove the header container string and :
> >            encryptedSecurityToken =
> encryptedSecurityToken.substring((CONTAINER + ":").length());
> >
> >            //Decrypt
> >            securityToken =
> MitreBlobCrypterSecurityToken.decrypt(blobCrypter, CONTAINER, DOMAIN,
> encryptedSecurityToken);
> >        } catch (Exception e) {
> >            throw new SecurityTokenException("Error creating security
> token from encrypted string: " + encryptedSecurityToken, e);
> >        }
> >
> >        return securityToken;
> >    }
> >
> >    @Override
> >    public String refreshEncryptedSecurityToken(String
> encryptedSecurityToken) throws SecurityTokenException {
> >        try {
> >            //Decrypt the current token
> >            SecurityToken securityToken =
> this.decryptSecurityToken(encryptedSecurityToken);
> >
> >            //Make sure the person is authorized to refresh this token
> >            String userId =
> userService.getCurrentAuthenticatedUser().getUserId();
> >            if (!securityToken.getOwnerId().equalsIgnoreCase(userId)) {
> >                throw new SecurityTokenException("Illegal attempt by user
> " + userId + " to refresh security token owned by " +
> securityToken.getOwnerId());
> >            }
> >
> >            //Create a new PersonGadget instance from it so we can use it
> to generate a new encrypted token
> >            PersonGadget personGadget = new
> PersonGadget(securityToken.getModuleId(), securityToken.getOwnerId());
> >            personGadget.setGadget(new Gadget(-1L, "", "", new
> URL(securityToken.getAppUrl())));
> >
> >            //Create and return the newly encrypted token
> >            return getEncryptedSecurityToken(personGadget);
> >        } catch (MalformedURLException e) {
> >            throw new SecurityTokenException(e.getMessage(), e);
> >        }
> >    }
> >
> >    private BlobCrypterSecurityToken
> getBlobCrypterSecurityToken(PersonGadget personGadget) throws
> SecurityTokenException {
> >        Person user = userService.getCurrentAuthenticatedUser();
> >
> >        BlobCrypterSecurityToken securityToken = new
> BlobCrypterSecurityToken(blobCrypter, CONTAINER, DOMAIN);
> >
>  securityToken.setAppUrl(personGadget.getGadget().getUrl().toString());
> >        securityToken.setModuleId(personGadget.getPersonGadgetId());
> >        securityToken.setOwnerId(personGadget.getUserId());
> >        securityToken.setViewerId(user.getUserId());
> >        securityToken.setTrustedJson("");
> >
> >        log.debug("Token created for personGadget " +
> personGadget.toString() + " and user " + user.toString());
> >        return securityToken;
> >    }
> >
> >    private String encryptSecurityToken(BlobCrypterSecurityToken
> securityToken) throws SecurityTokenException {
> >        String encryptedToken = null;
> >
> >        try {
> >            encryptedToken = securityToken.encrypt();
> >            log.debug("Encrypted token created from security token: " +
> securityToken.toString() + " -- encrypted token is: " + encryptedToken);
> >        } catch (Exception e) {
> >            throw new SecurityTokenException("Error creating security
> token from person gadget", e);
> >        }
> >
> >        return encryptedToken;
> >    }
> > }
> >
> > ----------
> >
> > package org.apache.shindig.auth;
> >
> > import org.apache.shindig.common.crypto.BlobCrypter;
> > import org.apache.shindig.common.crypto.BlobCrypterException;
> >
> > /**
> > * This class is needed because the BlobCrypterSecurityToken.decrypt
> method is marked as protected and we
> > * need to be able to decrypt the security token in the container
> > */
> > public class MitreBlobCrypterSecurityToken extends
> BlobCrypterSecurityToken {
> >    public MitreBlobCrypterSecurityToken(BlobCrypter crypter, String
> container, String domain) {
> >        super(crypter, container, domain);
> >    }
> >
> >    public static BlobCrypterSecurityToken decrypt(BlobCrypter crypter,
> String container, String domain,
> >                                                   String token) throws
> BlobCrypterException {
> >        return BlobCrypterSecurityToken.decrypt(crypter, container,
> domain, token, null);
> >    }
> > }
> >
> > ----------
> >
> > I think some of the API's have changed on the Shindig side since this
> code was written (this was written for Shindig 2.0) but I think the concepts
> are still the same.
> >
> >> the
> >> gadget object like in [2]. In rave_opensocial.js we don't pass the
> gadget as
> >> an object to the container, just its url and render params.
> >
> > I think for now we could just add another securityToken property to the
> object we push onto the widgets array.  I haven't yet looked into how we
> push this into the Shindig common container code however.  Hopefully there's
> an easy way to do it...
> >
> >>
> >> Is there anyone who does know the right place where to handle the
> >> securityToken between the portal and container?
> >>
> >>
> >> [1]
> >> http://svn.apache.org/repos/asf/incubator/rave/donations/surfconext-
> >> portal/coin-portal/trunk/coin-portal-
> >> war/src/main/java/nl/surfnet/coin/portal/control/HomeController.java
> >> [2]
> >> http://svn.apache.org/repos/asf/incubator/rave/donations/surfconext-
> >> portal/coin-portal/trunk/coin-portal-
> >> war/src/main/webapp/js/coin/shindig/container.js
> >>
> >> Jasha Joachimsthal
> >>
> >> Europe - Amsterdam - Oosteinde 11, 1017 WT Amsterdam - +31(0)20 522 4466
> >> US - Boston - 1 Broadway, Cambridge, MA 02142 - +1 877 414 4776 (toll
> free)
> >>
> >> www.onehippo.com
>
>
> Met vriendelijke groet,
> Okke Harsta
>
> Partner Zilverline
> Agile Management Consultant en Coach
> Lean Architect
> http://www.zilverline.com
> http://nl.linkedin.com/in/okkeharsta
> tel: +31 6 118 601 74
>
>
>

Reply via email to