Re: [Launchpad-dev] brainstorm: cheaper API collection iteration

2011-03-25 Thread Leonard Richardson



My understanding, may be way off.

Pages in collections are just links, so you can generate what you
like. You can generate the bare link to the collection to get the first
page, and then when that page is returned it would say

   next_page_link:
   
https://api.launchpad.net/devel/some_collection?start_key=endkey_of_this_collection

then when the next page is needed launchpadlib simply hits that URL.

Therefore I think this would be fairly straightforward to change in the
current webservice. It also looks as though just changing
lazr.batchnavigator will get most of the way there, so it may be that
fixing the web UI fixes the API too.


In general, this is right. lazr.restfulclient follows the URL it finds 
in next_collection_link without caring what that URL is. launchpadlib 
doesn't mess with the collection links at all, and lazr.restfulclient 
never uses previous_collection_link. So if you come up with a better way 
to paginate records, you can just change next_collection_link and the 
client will adapt.


I know of two exceptions, both optimizations in the code that handles 
slices (_get_slice() in lazr.restfulclient).


1. If you ask for a slice like launchpad.bugs[:76], lazr.restful gets 
the first page (which happens to have 75 entries), and then this code runs:


if more_needed  0 and more_needed  first_page_size:
# An optimization: it's likely that we need less than
# a full page of entries, because the number we need
# is less than the size of the first page we got.
# Instead of requesting a full-sized page, we'll
# request only the number of entries we think we'll
# need. If we're wrong, there's no problem; we'll just
# keep looping.
page_url = self._with_url_query_variable_set(
page_url, 'ws.size', more_needed)

So, we take next_collection_link, and we hack ws.size to only give us 
(in this case) one additional entry. We don't need another 75 entries, 
we only need one.


I think this will continue to work no matter what next_collection_link 
looks like, so long as ws.size  continues to work. Worst case, we can 
simply remove the optimization.


2. When you ask for a slice like 'launchpad.bugs[70:200]', this code runs:

# No part of this collection has been loaded yet, or the
# slice starts beyond the part that has been loaded. We'll
# use our secret knowledge of lazr.restful to set a value for
# the ws.start variable. That way we start reading entries
# from the first one we want.
first_page_size = None
entry_dicts = []
page_url = self._with_url_query_variable_set(
self._wadl_resource.url, 'ws.start', start)

This secret knowledge of lazr.restful would be invalidated by the 
change. We could stop supporting this syntax in later versions of 
launchpadlib, or we could just remove the optimization: make 
lazr.restfulclient load subsequent pages from the beginning until it has 
200, and then perform the slice.


I did a quick search of Launchpad for references to ws.start and 
ws.size. It shows up a lot in tests, but that's about it. In particular, 
it doesn't seem to be used in the Javascript code (though client.js has 
support for it). I did see some batch navigation code in picker.js. It 
looks like Launchpad code, not web service code, but it may have to be 
changed for the same reasons as the web service needs to be changed.


Leonard

___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Instead of authorizing individual applications against the Launchpad web service, let's authorize the Ubuntu desktop as a whole

2010-10-05 Thread Leonard Richardson
 We support KDE via Kubuntu and many Ubuntu developers use it;
 gnome-keyring seems rather specific; can we, as Sidnei suggests, use
 an abstraction layer to provide the benefits [currently only
 encryption?] to those environments too?

This is an excellent idea. I've asked Benji to implement this before
landing his launchpadlib branch.

 We *have* had credentials for key members of Ubuntu compromised in the
 past (through them pasting a openid cookie IIRC) and that route
 remains unaltered. The change that you're doing makes oauth roughly
 the same as openid cookies.

I can see what you're talking about but I'm not sure what exactly
happens in these incidents. (Part of this is likely my lack of
knowledge about OpenID.)  Can you give more details about this
compromise, or point me to someone who can?  Did someone paste a URL
containing their OpenID secret, or the cookie itself? (Like from an
HTTP dump?)

I'm curious about this because I hadn't really considered _accidental_
security breaches, even though those are the most common kind. I'm
going to give this some thought and see if there are any easy
countermeasures. This list may not be the best place to have this
discussion, but I'd like to offer three preliminary ideas:

1. My change doesn't change our use of OAuth very much. Once it hits
   the network, a DESKTOP_INTEGRATION token is exactly the same as a
   WRITE_PRIVATE token. The procedure for authorizing a token is
   exactly the same as before; it just happens less often. And I don't
   think the distinction between a file on disk and a key in the GNOME
   keyring is relevant here. So if we have a problem, I don't think
   it's a _new_ problem.

2. I have no idea if you can revoke an OpenID cookie, but it's easy to
   revoke an OAuth token if you accidentally publicize the
   secret. It's also easy to get a new token to replace the one you
   revoked.

3. We never put a token secret into a URL. We frequently put a token
   *key* into a URL, but it's the key to a *request* token. The main
   attack here is that Alice intercepts Bob's +authorize-request URL,
   waits for him to sign the request token, and then tries to exchange
   the request token for the signed access token, before Bob gets
   around to doing the exchange himself. This is a real attack, but
   it's not new.

 Perhaps we should set a vision for what we'd like to achieve here:
 Single sign on (web, casual apps, daemons like Ubuntu One), persistent
 credentials (if the user wants - I would!), easy authorisation of new
 apps (single clicky clicky dialog, in some trusted computing base),
 timeouts (defaulting on for high privilege levels), powerful per-app
 controls (like the openid prompt which says what is being disclosed),
 and only one protocol needed (rather than two, managed separately but
 with equivalent issues).

Let's do this, but let's keep the vision classified in tiers or
something. Persistent credentials is trivial. Only one protocol
may not be 100% possible, but the possible part is worth
doing. Trusted computing base seems like a project we could devote
an entire Ubuntu release to, assuming it's possible at all.

If we're aiming so high that trusted computing base is actually on
the table, then I'll stop shooting down ideas that assume it, and
start putting them in the appropriate tier.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Instead of authorizing individual applications against the Launchpad web service, let's authorize the Ubuntu desktop as a whole

2010-10-01 Thread Leonard Richardson
After talks with the USSO desktop team, Kees Cook, Ubuntu developer
Didier Roche, and Kubuntu developer Johnathan Riddell, I'm back to
update this thread. I know a lot more about the GNOME keyring, I've
made some tweaks, and I think I can present my design in a more
convincing way.

Restatement of the status quo
=

On the server side, Launchpad credentials are associated with a
consumer key (the name of the application that requested the
credential). Each credential has an associated access level which
limits what the application can do under your name.

On the client side, launchpadlib's login_with() method caches
credentials unencrypted in your ~/.launchpadlib/[host]/credentials
directory. The get_token_and_login() method can be used to get a token
without writing it to /credentials, but most applications use
login_with(), and most of the ones I've seen use get_token_and_login()
also write the token to disk somewhere.

This setup works great when integrating other websites into Launchpad,
because you don't have any control over (eg.) Facebook, and because
third-party websites don't all share a data store. Although
third-party websites can use launchpadlib, they can't use
login_with(), which is designed for desktop use.

This setup doesn't make much sense when integrating desktop
applications running in a GNOME environment. You have control over the
environment. Any application running as you can easily access your
credentials and act as the union of all your OAuth tokens. Not only
that, login_with() stores the tokens unencrypted on disk. Unless
you've enabled home directory encryption, if your hard drive is
stolen, all of your credentials will be compromised.

Restatement of my design


Now I'll restate my design as a two-step process. I hope I can make at
least the first step sound uncontroversial.

Step 1: Store credentials in the GNOME keyring
--

The first improvement is to change login_with() to start storing
Launchpad credentials in the GNOME keyring (if available), with disk
storage as a fallback. This will prevent the stolen-hard-drive attack.

With Launchpad credentials in the GNOME keyring, a suitably paranoid
end-user can also take countermeasures against certain other attacks
by moving their Launchpad credentials out of the login keyring
(where they will be stored by default) and into a keyring for which
they've set an idle timeout. You can do this from the Passwords and
Encryption Keys desktop accessory.

Idle timeout sidebar
~~~

 Setting an idle timeout means that if you unlock a keyring and then
 walk away from your computer for N seconds, the keyring will
 automatically lock back up. The next time any application wants
 access to one of the keys in that keyring, you'll have to type your
 unlock phrase again.

 This stops an attack where someone stops by your running computer
 when you're not around, and snoops on your passwords. It also offers
 a little protection against malware that tries to dump an unlocked
 keyring: if you suddenly get a password dialogue out of nowhere,
 you'd do well to be suspicious. It doesn't protect against a
 keylogger.

 If you set the idle timeout to 0 seconds, you will have to type your
 unlock password *every* time an application wants access to *any* key
 in that keyring. If you're careful, this will give you complete
 protection against malware that tries to dump a keyring. It still
 won't protect against a keylogger, though.

Step 2: Authorize the desktop, not the application
--

Setting an idle timeout offers you some additional options for
security. But even if you set your idle timeout to zero, you have no
way of ensuring that the 'apport' credential is only used by the
apport application.[Footnote 1] Any authorized application can access
your entire keyring.

The second improvement is to acknowledge this, by relaxing the
requirement that each desktop app have its own individually authorized
OAuth token, while maintaining that requirement for integrated
websites. This should have the side effect of solving the problem I'm
actually trying to solve--getting third-party developers to accept the
token authorization process instead of trying to hack around it.

The desktop authorization will be done through a one-time browser
open, as described in my earlier email. Desktop authorization can be
done permanently or on a time-limited basis, and revoked at any time
from the Launchpad website. Token expiration or revocation will cause
another browser open.

(About) as good as it gets
==

Why am I basing this design around a system (the GNOME keyring) that
has obvious shortcomings? Because what we're doing now is worse, and
because there's nothing lying around that's better than the GNOME
keyring.

It's not hard to come up with a better system than the one I
propose. But my 

[Launchpad-dev] Instead of authorizing individual applications against the Launchpad web service, let's authorize the Ubuntu desktop as a whole

2010-09-23 Thread Leonard Richardson
Introduction: Can We Do As Well As Ubuntu One?
==

Let's say you boot up your new Maverick system, fire up Rhythmbox, and
decide to buy some music from the Ubuntu One store. What happens?

An application window pops up on top of Rhythmbox, asking you for your
Ubuntu SSO login and password so that you can get access to Ubuntu
One. Being a new user, you don't have an Ubuntu SSO account, so you
get a chance to create one. Once you have an account, you accept the
Ubuntu One TC, and the application window secretly uses your
authorization to store a token on your computer.

This token allows applications to use Ubuntu One under your name.
It's stored in the GNOME keyring. You never see the token, but it's
there, and usable by other applications.

You buy your music, and then you get to work, writing some notes in
Tomboy. Once you're done, you decide to sync your notes to the cloud
using the Ubuntu One integration features of Tomboy. What happens?

Well, nothing happens. The Ubuntu One token is available to *any*
application that uses Ubuntu One. You authorized Rhythmbox, but Tomboy
can use the token too. Tomboy grabs the token you created from
Rhythmbox from the GNOME keyring and syncs your notes.

The Control Panel, desktopcouch, cloudserver, and every other
application that uses Ubuntu One can grab this token from the GNOME
keyring, without asking you, and access your Ubuntu One account.

Uh-oh! Rhythmbox just crashed while it was playing your music! Now a
dialogue box pops up, asking you if you'd like to report a bug. You
would like to report a bug, so you go through a process that's pretty
similar to the process you went through when you wanted to buy some
music:

A browser window pops up, and you're asked to log in to Launchpad, the
site that hosts the bug reports. You're able to log in using the
Ubuntu SSO account you just created, and then you get a message like
this:

 The application identified as apport wants to access Launchpad on
 your behalf. Will you allow apport access to your Launchpad account?

 * Yes.
 * No, thanks.

You click Yes, and Launchpad grants your computer a token that lets
apport use Launchpad on your behalf, without asking you every
time. This token is stored in the GNOME keyring, right next to your
Ubuntu One token.

But of course you don't see the tokens. You just see that apport has
done its job and now there's a Rhythmbox bug in Launchpad with your
name on it. Now you're curious about Launchpad, so you poke around the
site.

You decide that Launchpad is the perfect site to host the little open
source project you're working on, so you start a project and import
your source tree. And you discover there's an application called
Ground Control, which lets you integrate your Launchpad work into your
desktop.

You install Ground Control, start it up, and... your browser opens
again.

 The application identified as Ground Control wants to access
 Launchpad on your behalf. Will you allow Ground Control access
 to your Launchpad account?

 * Yes.
 * No, thanks.

What happened? How come Tomboy didn't need to ask you for permission
to access your Ubuntu One account, but Ground Control had to ask you
for permission to access your Launchpad account?

This is a question that our users have been asking (albeit not in this
form) since the Launchpad web service was first released. We don't
have a good answer. It turns out there *is* no good answer. It's time
to change this.

Authorize the entire desktop at once


I have a branch in review that needs some discussion. Here's the merge
proposal:

https://code.edge.launchpad.net/~leonardr/launchpad/rename-grant-permissions

I'm changing the social norms around Launchpad's OAuth token
authorization protocol, *as it's used on the Ubuntu desktop*, to make
it more like the system used by Ubuntu One. Instead of authorizing
individual applications (apport, Ground Control, etc.), the end-user
will authorize the Ubuntu desktop itself. The protocol itself is
unchanged; I'm simply reducing the number of times the end-user has to
go through it.

Robert pulled the brake on my branch because he has legitimate
concerns about my process: I didn't use this list to hash out the
design, and the design has changed radically over the past couple
months. Fair enough. Let me present the stable design in public, tell
you who I've discussed it with, and try to convince you that it's good
on the merits.

The ultimate purpose of this branch is to stop third-party developers
from writing horrible hacks that bypass the OAuth token authorization
protocol. These people are my customers. I can't stop them through
technical means, any more than music companies can stop *their*
customers from sharing songs with each other. But I can get my
customers back on my side, by improving the experience of the
legitimate alternative.

I went through a couple iterations of a design in consultation with
these 

Re: [Launchpad-dev] Instead of authorizing individual applications against the Launchpad web service, let's authorize the Ubuntu desktop as a whole

2010-09-23 Thread Leonard Richardson
Thanks for your responses. I have a couple follow-up questions for
Kees and Robert.

First, Robert, one of your concerns is with making the web service
safe for privileged users to use. I believe it's possible to configure
the GNOME keyring so that the keyring is never permanently
unlocked. Any attempt to access a key from the keyring will require
that you enter your keyring password. If you didn't just do something
that requires accesss to one of your keys, you don't enter your
password.

I imagine it would be very annoying to live with this kind of
setup. But that's the setup I'd live with if I were totally serious
about protecting my SSH keys and OAuth tokens from malware. How close
would this come to making you feel that your OAuth tokens were safe?
And, Kees, how much would this actually improve security?

 Using sensibly short timeouts for session tokens as a default is good.
 I'm not opposed to making forever available for those that want it,
 but it should probably not be the default.

We do plan to add timeouts for session tokens in two
circumstances. (These are both mentioned in the design document I
linked to last time, http://pastebin.ubuntu.com/499131/) Here's the
one that pertains to my last two questions:

There are a couple actions (uploading SSH/GPG keys) that are so
fraught with peril that we currently don't publish them on the web
service at all. We plan to publish these operations but only make them
accessible through time-limited OAuth tokens that have a new
permission level. Normal desktop integration permission will not be
good enough to use applications that upload new SSH keys, like
Quickly. You will have to do the browser dance and get a brand new
time-limited token every time you want to upload a new SSH key.

Now, suppose we take existing web service operations like toggle
whether this bug is a security bug or toggle this bug's public
flag. Things that we're kind of worried about, but not so worried
that we were afraid to publish them at all. And we put those in the
same category as upload a new SSH key. A category that forces you to
do the browser dance every 15 minutes as long as you're doing that
kind of thing. And added some code that kept those keys from being
stored in the GNOME keyring, making it more difficult for malware to
grab them.

Robert, how far would *this* go towards addressing your concerns about
making the web service safe for privileged users? Think about what
you're most afraid of when it comes to malware acting as a privileged
user. Are you afraid of things that only privileged users tend to do?
Things like uploading SSH keys and changing the security status of
bugs? Or are you afraid of things that everybody does all the time,
but that can be especially damaging when privileged users do them?

This brings me to my third question. Imagine that the existing system
really did work the way I imagined it could, before I found out about
the GNOME keyring. Imagine that when you grant an OAuth token to
apport, apport is the only application that can see or use that
token. You have to authorize every one of your desktop apps
individually. It works like a dream.

Here's the problem: my developers will not put up with that. They
don't put up with it now, and they won't suddenly start putting up
with it if the security benefits ever become real.

Pretty much every third-party developer (and at least one internal
developer) has responded to our OAuth token authorization protocol by
hacking around it, creating some native-GUI way of asking the user for
their Launchpad username and password, so that their users don't have
to do the browser dance.

That's the whole reason I'm trying to change this system--my customers
simply refuse to make their users do the browser dance before using an
application. Making the tokens time-limited, or making the permissions
more fine-grained, will just make things worse, as developers try
harder to find workarounds.

The system we have now, for all its shortcomings, is intolerable to my
developers. I'm trying to find a system that's at least as secure and
that can get developer buy-in. My proposed design seems congruent with
existing Ubuntu best practices when it comes to your SSH keys and the
data you've got stored in Ubuntu One. The only realistic alternative I
can think of is to enforce draconian rules like we won't let your
program into Ubuntu unless it does the browser dance properly, but you
can tell your users to blame us for the bad user experience.

And now, finally, my third question. Suppose we were to move the
scary activities into a time-limited token, a token that required a
browser dance every fifteen minutes but which ordinary users would
rarely need to acquire. Would it then be below the scary threshold to
manage your ordinary activities on the web service with a single,
desktop-wide token?

(And, to restate my second question, would there be enough non-scary
activities left that an ordinary user would not find 

Re: [Launchpad-dev] tuesday - performance day!

2010-08-09 Thread Leonard Richardson
 Now, I'd like to ask for some advice. There seem to be three ways of
 fixing this:
  - (somehow - I don't know the magic) tell lazr.restful to skip some
 attributes (e.g. karma, coc_signed etc) for representations.
 https://bugs.edge.launchpad.net/launchpad-foundations/+bug/251284 -
 makes note of things being removed from representations, but I don't
 know whats involved in doing that (yet). Or I may be misunderstanding
 and actually its an all-or-nothing solution. Leonard, if you could
 comment here (or there) and help enlighten me, I'd really love that :)

lazr.restful will publish an attribute in representations only if it is
tagged with the exported() decorator. If you remove the exported()
decorator from an attribute that attribute will instantly disappear.

But unless the performance problem is catastrophically bad, you want to
un-export an attribute only in the latest version, so as to preserve
backwards compatibility. For that, let me quote my earlier email to
Julian:

---
And here's how to get rid of queue_status in the latest version while
maintaining it in the old versions.

queue_status = exported(
Choice(
title=_('Status'),
vocabulary=BranchMergeProposalStatus, required=True,
readonly=True,
description=_(The current state of the proposal.)),
('devel', dict(exported=False)))
---

So you call exported(), but then you take it back, as of a specific
version of the web service.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] How do I support older API calls when changing the underlying model?

2010-07-27 Thread Leonard Richardson
On Mon, 2010-07-26 at 14:55 -0400, James Westby wrote:
 On Mon, 26 Jul 2010 12:16:48 -0400, Leonard Richardson 
 leonard.richard...@canonical.com wrote:
  @operation_removed_in(devel)
 
  @operation_for_version(devel)
 
 Do these need to be updated next time devel is snapshotted, to create
 2.0 or whatever it will be?

Yes, exactly. When we snapshot a release we'll go through and replace
all the devel with the next version name.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] How do I support older API calls when changing the underlying model?

2010-07-26 Thread Leonard Richardson
 I'm in the situation where I'm wanting to split up a status field on the 
 branch 
 merge proposal into two.  However there is an existing API that is used to 
 fetch merge proposals.

OK, let me quote the part of the system I think you're changing.

From IHasMergeProposals:

@operation_parameters(
status=List(
title=_(A list of merge proposal statuses to filter by.),
value_type=Choice(vocabulary=BranchMergeProposalStatus)))
@call_with(visible_by_user=REQUEST_USER)
@operation_returns_collection_of(Interface) # Really
IBranchMergeProposal.
@export_read_operation()
def getMergeProposals(status=None, visible_by_user=None):

From IBranchMergeProposal:

queue_status = exported(
Choice(
title=_('Status'),
vocabulary=BranchMergeProposalStatus, required=True,
readonly=True,
description=_(The current state of the proposal.)))

 How do we continue to support the older methods when changing the underlying 
 model?  Should we even try?

Sometimes it's possible to determine that no one is using the old
version of a named operation, so you can change it without pain, or that
only a couple people are using it and you can coordinate a change with
them.

However, this change doesn't look too difficult, so let's try
maintaining the backwards compatibility.

 I'm wanting to break the queue_status member into merge_status and 
 review_status.  I was also considering re-using the 
 BranchMergeProposalStatus enum for the merge_status with changed values.
 
 However I feel that this is more likely to break something.
 
 I'm at a bit of a loss to know where to start with supporting the old method 
 where the method directly gets values from an enumerated type I'm changing.
 
 Should I instead of reusing the BranchMergeProposalStatus enum, create two 
 new 
 ones (instead of just one new one)?

You should. All of the versions of the web service coexist in a single
application. Launchpad now has three enumerated types: one old one and
two new ones. They all need to be present.

 How do I have a method that is only supported in an old API version?

With the @operation_removed_in() decorator. Here's the old method,
renamed to getMergeProposalsOLD internally, published as
getMergeProposals externally, and removed in the latest version of the
web service:

@operation_removed_in(devel)
@operation_parameters(
status=List(
title=_(A list of merge proposal statuses to filter by.),
value_type=Choice(vocabulary=BranchMergeProposalStatus)))
@call_with(visible_by_user=REQUEST_USER)
@operation_returns_collection_of(Interface) # Really
IBranchMergeProposal.
@export_as(getMergeProposals)
@export_read_operation()
def getMergeProposalsOLD(status=None, visible_by_user=None):

Here's the new method, introduced in the latest version of the web
service.

@operation_parameters(
   merge_status=List(
   value_type=Choice(
   Vocabulary=BranchMergeProposalMergeStatus,
   review_status=List(
   value_type=Choice(
   Vocabulary=BranchMergeProposalReviewStatus)))
)
@call_with(visible_by_user=REQUEST_USER)
@operation_returns_collection_of(Interface) # Really
IBranchMergeProposal.
@export_read_operation()
@operation_for_version(devel)
def getMergeProposals(merge_status=None, review_status=None,
visible_by_user=None):

This way the 'getMergeProposals' named operation will have the existing
behavior in 'beta' and '1.0' (because it's mapped to
getMergeProposalsOLD()), and new behavior in subsequent versions
(because it's mapped to getMergeProposals()).

And here's how to get rid of queue_status in the latest version while
maintaining it in the old versions.

queue_status = exported(
Choice(
title=_('Status'),
vocabulary=BranchMergeProposalStatus, required=True,
readonly=True,
description=_(The current state of the proposal.)),
('devel', dict(exported=False)))


Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Quickly and Launchpad

2010-07-13 Thread Leonard Richardson
 Agree.  My argument about client-side security is that there is little
 point forcing the user to do an interaction through a web browser
 rather than through any other client-side program.

I have completely capitulated on this point. Even so, once Launchpad
becomes an OpenID consumer, a web browser will be the only tool general
enough to complete the Launchpad authentication process, since that
process might involve a redirect to Livejournal or Facebook and a trip
through whatever HTML forms they throw at you.

With the desktop credential-granting service, you'll go through the web
browser only once, to create a GRANT_PERMISSIONS OAuth token for the
credential-granting application itself.

The credential-granting application then uses the GRANT_PERMISSIONS
token to grant additional tokens _using the web service itself_, without
going through the Launchpad authentication process.

  2. If we really consider scenario 4 and scenario 1 equally insecure, we
  might as well go for the superior usability of scenario 4.
 
 Sorry, I'm not sure what API to create a new OAuth token means.  API
 on the desktop credential-granting service?  I don't think you should
 be able to use a less-privileged token to make a WRITE_SENSITIVE_DATA
 one.

Yes, API to create a new OAuth token means a new feature of the
Launchpad web service which will allow the credential-granting service
to create new OAuth tokens without making the end user re-authenticate
with Launchpad. The launchpadlib code would look something like this:

new_token = launchpad.me.oauth_access_tokens.new(
consumer=consumer, access_level=WRITE_PRIVATE)

This action requires the GRANT_PERMISSIONS access level.
GRANT_PERMISSIONS is, by definition, the most privileged access level.
The only application that should ever have GRANT_PERMISSIONS is the
desktop credential-granting service.

I like the idea of making WRITE_SENSITIVE_DATA tokens expire so that old
tokens found on backups or abandoned drives won't work anymore. But I
don't think that will help, because typically the GRANT_PERMISSIONS
token will still be available, and an attacker can just use it to create
a new WRITE_SENSITIVE_DATA token.

The two solutions to this problem:

1. Make the GRANT_PERMISSIONS token also expire after a while.
2. Prohibit the GRANT_PERMISSIONS token from creating
WRITE_SENSITIVE_DATA tokens, even though it can create other kinds of
tokens.

Both of these solutions require opening the web browser more often, at
seemingly random intervals. Not, at this point because the web browser
is more trustworthy than the permission-granting app, but because the
web browser is the one that can negotiate a redirect to a Facebook
login.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Quickly and Launchpad

2010-07-12 Thread Leonard Richardson

 I can write some code that simply reads the token from
 ~/.launchpadlib/credentials/quickly and attempts to use it to add a new
 GPG key if you are in ~ubuntu-core-dev, and so gets a secret key that
 can sign packages that can end up on millions of machines. A very
 tagetted attack like that would hard to prevent, because there is clearly a
 lot of desire. However, as one of the people that could be exploited in
 this manner I am wary of anything that makes it possible.

OK, we can make WRITE_SECURITY_SENSITIVE a time-limited token that must
be reacquired, say, once an hour. Or we can make it a one-shot token
that must be acquired anew every time you want to perform one of these
actions. However, such a system destructively interferes with our plan
for a desktop credential management app.

Let me explain. I'm now working on adding *another* OAuth access level,
GRANT_PERMISSIONS. This access level is only officially available to the
credential management app. The point of GRANT_PERMISSIONS is to make it
possible to create new OAuth tokens without opening the user's web
browser. The credential management app will ask the end-user to grant or
deny access within a native GUI window rather than within the web
browser.

You point out that it's easy for a malicious application to get another
application's OAuth credentials. This is true whether the credential's
access level is WRITE_SECURITY_SENSITIVE or GRANT_PERMISSIONS. (Hiding
the GRANT_PERMISSIONS credential somewhere other than the file system
won't help: in the worst case, a malicious application can impersonate
the Launchpad credential management app over DBus.) If you have
GRANT_PERMISSIONS, you can grant yourself any other level of access,
including WRITE_SECURITY_SENSITIVE.

It's no secret that I think the desktop credential management app,
although superior from a UI standpoint, is insecure. Up to this point
the counter-argument has prevailed that malicious client code on an
Ubuntu desktop is rare, so we shouldn't worry about it. I think this
counter-argument has an additional premise that has just been revealed:
malicious client code on an Ubuntu desktop is rare, *and if it does
exist, the worst it can do is screw up your own system/Launchpad
account*. With GRANT_PERMISSIONS plus the ability to upload GPG keys,
once malicious code gets on an Ubuntu system it can easily infect
thousands of other systems.

The obvious solution is to prohibit GRANT_PERMISSIONS from being used to
grant WRITE_SECURITY_SENSITIVE. This means that WRITE_SECURITY_SENSITIVE
cannot be obtained through the desktop credential management app. Every
time you want WRITE_SECURITY_SENSITIVE, you will have to open the user's
web browser and tell them: 

  Quickly wants to do a very dangerous thing with your Launchpad
account. Because the thing Quickly wants to do is so dangerous, 1) you
need to make doubly certain that you trust Quickly with your Launchpad
account, 2) you need to authorize Quickly in your web browser because we
can't even trust the Launchpad credential manager in this situation.

Since WRITE_SECURITY_SENSITIVE is either a time-limited or a one-shot
authorization, the end-user will find themselves authorizing in the web
browser every time they want to do something dangerous, plus authorizing
in the Launchpad credential app every time they start using a new app,
plus using the web browser to authorize the Launchpad credential manager
itself.

To summarize, a totally new user will need to do the following to upload
a GPG key to Launchpad:

1. Authorize the Launchpad credential manager using the web browser.
2. Authorize Quickly for WRITE_PUBLIC using the credential manager.
3. Authorize Quickly for WRITE_SECURITY_SENSITIVE using the web browser.
4. Upload the SSH key.

Or, if WRITE_SECURITY_SENSITIVE is time-limited rather than one-shot,
you could implement this:

1. Authorize Quickly for WRITE_SECURITY_SENSITIVE using the web browser.
2. Upload the SSH key before the credential expires.

(In the second case, once the credential expires, the end-user will
completely lose the ability to use Quickly. Next time the end-user wants
to use Quickly, they will need to once again authorize Quickly for
WRITE_SECURITY_SENSITIVE, or else authorize the Launchpad credential
manager and then use the credential manager to authorize Quickly for
WRITE_PUBLIC .)

Is this acceptable? Is there a better solution?

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Quickly and Launchpad

2010-07-12 Thread Leonard Richardson
 That is true but to me the main point is that if there is malicious
 code running within your desktop machine, you have bigger problems
 than your Launchpad account.

Sure; the question I'm trying to answer is whether malicious code
running on your desktop machine also means bigger problems for lots of
innocent people.

 Can you unpack the logic there?  Do you mean that if malicious code
 gets onto an Ubuntu system of a user who can write to the main archive
 or a popular PPA, it can propagate to thousands of other machines.
 That is true, but orthogonal to whether there is an API to manipulate
 credentials.

I'm not sure what you mean by API to manipulate credentials. I don't
know whether you mean API to upload a new GPG key or API to create a
new OAuth token without user input. Forgive the verbiage that follows;
I want to be really precise about this.

Let's take Bob to be a user who can write to the main archive of a
popular PPA, and let's stipulate that Bob has malware on his computer.

OK, what are the scenarios?

1. The current scenario. There is no API to upload a new GPG key and no
API to create a new OAuth token. Can the malware on Bob's computer
reproduce itself in the PPA? I guess, but it would have to include a
keylogger (to sniff the passphrase to Bob's existing GPG key) or
something fairly sophisticated.

2. There is an API to create a new OAuth token, but no API to upload a
new GPG key. This is identical to scenario #1. The malware can exploit
the API to create new OAuth tokens for itself, but it can't use the API
to do what it really wants to do--reproduce.

3. There is an API to upload a new GPG key, but no API to create a new
OAuth token. Now an exploit is trivial. The malware can grab Bob's
existing OAuth token, impersonate one of his other applications, and
upload a new GPG key. We can stop this from working by making upload a
new GPG key require a single-purpose, one-use OAuth token.

4. There is an API to upload a new GPG key, and an API to create a new
OAuth token. Now it doesn't help to require a single-purpose, one-use
OAuth token, because the malware can create new OAuth tokens whenever it
wants. The only way to plug the trivial exploit is to prohibit create a
new OAuth token from creating the kind of OAuth token that can be used
to upload a new GPG key.

If we take the hard line that once there's malware on your system,
you're screwed, then all four of these scenarios are identical--the
difference between fairly sophisticated exploit and trivial exploit
is not worth considering. I don't know if this is what you mean by [The
existence of an exploit] is orthogonal to whether there is an API to
manipulate credentials. Is it?

If we take this hard line, then there's no reason to make
WRITE_SENSITIVE_DATA tokens temporary or prohibit the OAuth token
generator from generating them--we're just making the malware's job
marginally easier.

I think users are likely to need WRITE_SENSITIVE_DATA fairly rarely, so
I don't have a problem with requiring these tokens to be acquired
through the web browser. But:

1. The reaction to 'tokens through the web browser' has been incredibly
negative. Others have spent a lot of time hacking around it, and I've
spent a lot of time trying to broker and implement a compromise.
Reintroducing the browser at (what will appear to the end-user as)
random intervals isn't good for the user experience--you could argue
it's worse than our current strategy of using the browser all the time.

2. If we really consider scenario 4 and scenario 1 equally insecure, we
might as well go for the superior usability of scenario 4.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Quickly and Launchpad

2010-07-07 Thread Leonard Richardson
 However, for maverick, we need to go further and help the user to create
 and get his full environment, which means:
 - uploading his gpg key (for uploading his package) if he doesn't have
 one
 - upload his ssh key (for bzr)
 - creating one ppa if he hasn't one
 - this imply make him sign the Code Of Conduct in a simple way.

There's no reason these functions can't be published through the web
service (except of course lack of person-hours). But, it might be
dangerous to publish 'upload GPG key' and 'upload SSH key'. For
instance, allowing a web service client to upload a new GPG key could
allow a malicious client to create its own GPG key, upload it as yours,
and then upload packages 'signed' by you. 

In a sense this problem already exists--a malicious client can currently
post atrocious comments in your name, etc. But this is more worrisome
because 1) anything to do with GPG or SSH triggers one's security
Spidey-sense, 2) a malicious app that uploads packages in your name can
infect other peoples' computers.

If we were to implement these features today, they would be available to
clients that were granted the OAuth access level WRITE_PRIVATE or
WRITE_PUBLIC. But almost every nontrivial web service client needs to
write the dataset, which means it needs WRITE_PRIVATE or WRITE_PUBLIC.
That leaves quite a lot of scope for abuse.

But let's say we create a new level of access WRITE_SECURITY_SENSITIVE.
Clients with WRITE_PRIVATE can do everything they can do now, but they
can't do especially sensitive things like upload SSH keys. Clients with
WRITE_SECURITY_SENSITIVE can do everything a WRITE_PRIVATE client can
do, and can also upload SSH keys.

A client like Quickly would need to ask for, and be granted,
WRITE_SECURITY_SENSITIVE to function properly. If there are any existing
activities protected with WRITE_PRIVATE clients, that we want to protect
with WRITE_SECURITY_SENSITIVE instead, we could do that, at the cost of
breaking existing clients until the end-user granted them the new
permission.

We can't prevent malicious clients from existing, but we can reduce the
amount of trust the end-user needs to put in any particular client. If
you grant WRITE_PUBLIC or WRITE_PRIVATE to most of your apps, and only
grant WRITE_SECURITY_SENSITIVE to Quickly, then at least you can rest
assured that your other apps aren't going to upload fake GPG keys to
your account. You only have to worry about whether Quickly is
well-audited.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Adapters API

2010-06-30 Thread Leonard Richardson
 What we would like to do is avoid registry importing from so many of the
 other apps, to avoid the circular dependencies.
 
 To do this we are currently looking at having ISomething interfaces in
 the apps (bugs, code, soyuz etc.) that define an interface relevant to
 them. This is standard practice for a lot of things, see IBugTarget,
 IHasBranches etc.

When the issue of circular dependencies first came up, we decided to
stumble along until we could use a planned newer version of Zope which
would allow us to use some kind of deferred reference. I didn't follow
this development very closely, so I don't know if this feature actually
made it into Zope, but if so it seems like a simpler solution to your
problem than creating a lot of adapters.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Can we generate the apidoc directory at build time?

2010-06-24 Thread Leonard Richardson

 Since the apidoc directory under version control is empty, can remove it from 
 the repository and generate it at build time?  That would eliminate these 
 merge 
 conflicts once and for all.  It would solve our accidental deletion problems 
 too.

I don't have a problem with this.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] API issue moving branches

2010-05-21 Thread Leonard Richardson
 While its true that this is a particular concern, many scripts are
 written without knowledge of it - and for things like hydrazine
 designed to be single user, putting fingers in the ears and going
 la-la-la seems to be the best answer at the moment (and thats what it
 does).

The lack of a thread-safe cache has recently caused problems in
groundcontrol (a GUI application). Since using a prepopulated cache is
the single biggest performance/bandwidth win we have, I'll definitely be
taking a look at this soon.

Hopefully we can contribute the thread-safe cache back to httplib2.
Hopefully the reason httplib2 doesn't already have a thread-safe cache
is that most people don't really need one, not that it's especially
difficult to give it one.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] API issue moving branches

2010-05-20 Thread Leonard Richardson
 I don't intend to put you on the spot - that is, if you don't have an
 answer, its fine. But it would comfort me to know that some of the
 following are intended to be addressed:
  - the transaction vs non-transactional model:
  * for instance: in LPAPI's, how do you create a set of objects that
 temporarily violate DB constraints, which appserver code can do
 easily. As a concrete example, rename two objects A and B to be B and
 A, safely
  * or call methods on altered objects without saving them (saving them
 means you can't check you have achieved what you wanted to achieve)
  * LPAPI's cannot do repeated reads, so any appserver code that
 depends on that can't really be ported to LPAPI's.
  - speed

I feel like we're going around in circles with more detail on each
go-round. I'll re-state the problem and the plan as I understand it:

1. There are enormous conceptual and practical obstacles standing in the
way of a fully unified API that is also a web service we're happy using.
Huge obstacles!

2. Nonetheless, I would like to try to get through those obstacles with
a unified API, because the alternative is to design and implement two
parallel APIs.

3. In the cases where we can't get to a unified API, we have a fallback
plan: two parallel APIs. This fallback plan is *annoying* and requires a
lot of duplication of effort, and refactoring that doesn't give you the
satisfying feeling you ought to get when you refactor something. But
that's what it means to have two parallel APIs.

Look at it in terms of manpower. The web service team is me. One
person. Not only am I responsible for the lazr.restful and
lazr.restfulclient frameworks, I have de facto responsibility for the
concrete instantiation of those frameworks: the Launchpad web service
itself. The *only way I can survive* is to ensure that the complexity of
the Launchpad web service remains a very small fraction of the
complexity of Launchpad. 

If we start writing parallel APIs, the complexity of the Launchpad web
service will start to approach the complexity of Launchpad. This will
not scale. This is why I am so obsessed with finding solutions that work
by reducing the complexity of the Launchpad web service and/or
increasing the complexity of lazr.restful.

   * It takes 13 seconds to log into launchpad, find out that there are
 some pending merges for bzr and quit hydrazine again.
  This may not seem like a long time, but really - it is, for
 something that should be a single HTTP request [I say that because in
 other web services API's *it is* a single HTTP request].

13 seconds is an INCREDIBLY LONG TIME. It is TOTALLY UNACCEPTABLE. Let's
figure out how to fix it.

The first step is to profile. I added timing code to lazr.restfulclient
and wrote come code that retrieves the pending merges for bzr, basically
this:

Launchpad(...).projects['bzr'].getMergeProposals()

The results:

Request for https://api.edge.launchpad.net/1.0/bzr took 0.82 seconds.
Request for
https://api.edge.launchpad.net/1.0/bzr?ws.op=getMergeProposals took 3.03
seconds.

OK, there are two HTTP requests, not one. Fortunately, the first request
is unnecessary: it happens because you can't get a reference to
launchpad.projects['bzr'] without making an HTTP request to /bzr.

Now, we've already solved this problem for collections--note that we
were able to get a reference to launchpad.projects without making an
HTTP request to /projects. Similarly, we can have launchpadlib return a
shim object that only requests /bzr when you try to access some data
specific to that project. If you just want to invoke a named operation
on the project, that request will never be made.

The downside of this solution is that if you write
launchpad.projects['nosuchproject'], you won't find out right away the
project doesn't exist. You'll only find out when you try to access
project.owner or invoke project.getMergeProposals(). This seems like an
acceptable price to pay for not making that extra request.

I filed bug 583318 to deal with this. We can implement this solution or
not, but I wouldn't say it has anything to do with the server-side
design of Launchpad. It's a problem with the way the client translates
the programmer's desires into HTTP requests. The getMergeProposals call
itself is pretty slow on the server-side, but optimizing it shouldn't
require changing the API.

I don't know what launchpadlib setup you used to get your 13 seconds
number, but it was either a new setup or an old setup. Let's say it was
a new setup. You made two HTTP requests and it took 13 seconds (due to
latency, that's longer than it took me to make the same two requests).

With an old setup, you would have made four HTTP requests and with those
latency numbers it would have taken 25 seconds or even longer. I've
spent the past month or so improving performance and this is the result.
You may have already seen
https://dev.launchpad.net/Foundations/Webservice/Performance but it has
detailed information and measurements 

Re: [Launchpad-dev] API issue moving branches

2010-05-20 Thread Leonard Richardson

 I think you may be going a little far in assuming that he would have
 been making 2 http requests in a new setup. That requires using a
 shared cache, which is unacceptable to some applications without writing
 their own cache implementation right now. (The alternative is fixing the
 bug in httplib2 or lazr.restfulclient and waiting for it to be widely
 deployed.)

Can you elaborate? I'm not sure which bug you're talking about. I know
that shared caches are problematic when you're running launchpadlib in a
multithreaded environment.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] API issue moving branches

2010-05-19 Thread Leonard Richardson

 And there is the issue I think: what makes a great web services API is
 fundamentally different to what makes a great in-app-server API. The
 latency and bandwidth constraints are radically different.

I think one of three things will happen when we try refactoring a given
part of the data model.

1. It works fine. There is no fundamental reason why the internal API
has to be different from the public API. For small things like
setStatus, I think we will find almost all the time that we can refactor
the code into one superior API. (Apart from backwards compatibility
problems.)

2. The refactoring succeeds, but some unimportant part of the resulting
data model cannot be made efficient or comprehensible enough to justify
putting it in the web service. For instance, we may know that Launchpad
will run a certain inefficient query very rarely, but if we publish it
we have no control over how often it's run.

In this case, we would proceed with the refactoring and not expose the
less important part. The public data model will be a subset of the
internal data model.

We know we need special features to deal with the greater latency of the
web service, but I'm not convinced these features have anything to do
with specific data models. The ability to group objects into ad hoc
collections and perform operations on the collection (or however we want
to implement this) can be implemented in lazr.restful. We shouldn't need
to implement this ability separately for bugs and for people.

3. The refactoring fails. For complex systems like the HWDB, it may be
possible to design an internal data model _and_ a public data model, but
fundamentally impossible to make the same design serve both purposes. In
this case we would need to retreat from the annotation-based model and
create our own Entry classes. I don't think this is incredibly likely,
but life is complicated, so who knows? We have this option.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] API issue moving branches

2010-05-18 Thread Leonard Richardson

 I find this quite hard to do since the API exposes our model objects.
 
 I'd love to have a separate layer, like we have with the browser.

In theory, you are 100% free to write your own Entry classes instead of
having them generated from annotated model objects. As an example, the
web service defined in lazr.restful doc/webservice.txt creates its Entry
classes from scratch. But, we would rather not have two different data
models (one for internal use, one for the web service). We'd rather have
one, and when we first designed the web service we had a specific policy
for making sure we only had one.

We developed the annotation technique to make it really easy for the
Launchpad developers to publish a web service for their field of
responsibility, without having to learn a lot about web service design
or make a lot of changes. The price we paid was a coupling of the web
service model to the underlying data model. There's no free lunch. You
either put some work into designing your web service, or you get a web
service that reflects the work you already did.

The original idea was that when we ran into a mismatch between the
underlying data model and the web service data model, we would refactor
the underlying data model. We would think to ourselves: design X is
going to confuse our users; it would be nice to publish design Y
instead, and then we'd start using design Y _everywhere_. Because why
should we have a confusing design when our users get a nice design? The
web service would be a way to make piecemeal improvements to our
underlying design.

Yeah, the named operation setTarget(package, project) is confusing. But
the internal method setTarget(self, package, project) is also confusing,
in exactly the same way! If we leave it in place internally and patch
over it for the web service (which is what we talk about when we talk
about annotations), we're letting our users have the good stuff but
we're still using the not-so-good stuff internally.

It sounds like we are approaching the point where we either need to
start refactoring, or decide that the refactoring idea won't work after
all and we need to fall back to writing our own Entry classes. In this
particular instance (setTarget) I think we can resolve the problem with,
at most, a minor change to the underlying data model. But let's take
another example.

When I look at the HWDB data model (as published through the web
service) I have absolutely no idea what is going on. If I wanted to
write a web service script using the HWDB it would take me days just to
get started. I'm sure the domain experts know what is going on, and I'm
also sure that if they put some work into client use cases they could
come up with a public-facing data model that was simpler. 

At this point, I'd suggest we refactor the existing HWDB data model into
the simpler data model, rather than have to keep two different data
models in our heads. Why not? It's a lot of work, but not that much more
work than writing a whole other data model, and at the end our
application will be better. Because the public-facing data model is less
efficient? That's a reason not to publish at all--it's a bad idea to
publish an inefficient data model and hope our users don't use it too
much. Are the two data models just totally irreconcilable for some other
reason? In that case, it probably makes more sense to go ahead and
create our own Entry classes.

I hope it doesn't feel like I'm suddenly springing this on you. This has
been our plan for quite a while, but up to this point we've been
focusing on creating a web service with a solid foundation that
publishes all the Launchpad artifacts. Now is a good time to start
talking about the best way to *improve* the design of the web service.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] API issue moving branches

2010-05-10 Thread Leonard Richardson
 I agree with Francis, but I'd also like to add that it seems hard to
 expose 'never both' over API's AIUI - that is, setTarget doesn't
 permit both package and project to be set, but individual fields would
 permit that.

If the individual fields have setTarget() as their mutator, then any
attempt to set both 'package' and 'project' would fail, just as any
attempt to invoke setTarget(package=foo, project=bar) would fail. Any
attempt to set 'package' when 'project' is already set would implicitly
clear out 'project'.

I take Francis's general point that we should think about the API that
will feel most natural to the end-user, rather than publishing our
internal API directly.

I also take his specific point that having a single, polymorphous
'target' field  two fields  named operation. You can set 'target' to a
package or a project, if you need to see what kind of object the target
is you can GET it, if you want there to be no target you can set a
single field to None.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] API issue moving branches

2010-05-07 Thread Leonard Richardson

 Not surprising, but how can we tell the restfulclient that the url for the 
 entry has changed?

Named operations don't really have hooks for this kind of thing. OTOH if
you set a regular field (possibly through a mutator) and that happens to
change the object's URL, lazr.restful knows to send a redirect to the
new URL.

I would like to get rid of that named operation altogether if possible. 
setTarget() is a method that sets _either_ project _or_ source_package.
Both of these are normal fields of the branch, and if you made them
directly settable the API would be simpler. The only part of
setTarget()'s logic the end-user needs to know is if the branch is ever
missing both project and source_package, it gets automatically moved
into the junk namespace of the branch owner. That information can go
into the descriptions of project and source_package.

But setTarget sets _either_ project or source_package. lazr.restful
prohibits one field from having two mutators, but you might be able to
declare a single method with different fixed arguments as the mutator
for two different fields. Realistically, though, this has never been
tried before and strains the capabilities of the system.

A more realistic solution might be to implement setattr for 'project'
and 'source_package' in Branch. This would let us treat them like normal
fields without having to do any lazr.restful hacks.

On top of this, I can implement something in lazr.restful that checks
the URL of an object before and after invoking a POST named operation.
But this named operation needs special treatment is a code smell. It
indicates that the system is more complex than it needs to be.

In this case, if the user thinks I want to move this branch to project
foo, they have to look at the apidoc, discover that setting the project
is a special case of setting the target, and invoke a named operation
called setTarget. Ideally they would be able to set .project=foo and
save.

Summary: I should extend the 'check if the URL has changed' code to run
after a POST named operation, but I don't think we need to publish this
particular named operation, and turning project_link and
sourcepackage_link into read-write fields will also solve your problem.

Leonard


___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Want feedback on LaunchpadSharp

2010-05-04 Thread Leonard Richardson
Manish,

 Launchpad provides a WADL file describing the API for the web-service. I 
 wrote a WADL-C# converter called wadlsharp[1] . Actually with minor 
 modifications it can even create VB code but that is not the current 
 target. wadlsharp is just a library and I am yet to write it's 
 documentation. I created wadlsharp just to create the C# client code so 
 that customizing the generated C# code can as easy as possible. I have 
 released v0.1 of wadlsharp. Get the code from bzr[2] or download the 
 tarball[3]

First, I'm grateful that you're taking the time to write a .NET client
for the web service. However, I think your current design will cause
trouble later on for you and your users. Let me explain.

I don't know any C#, so I don't understand what happens inside
wadlsharp, but I believe your problems start there, with the generation
of C# client code for later customization. I'm looking at
LpClientProxy.cs in lpsharp, and it looks like it's the output of
wadlsharp, or some wadlsharp-aided transformation of the WADL served by
Launchpad on a specific date.

I believe this design is a mistake, for three reasons. All three reasons
are variants on a single reason: changes to the web service are not
automatically reflected in the client. To see the changes, you must
upgrade lpsharp.

1. Let's imagine that the Launchpad web service changes in a
backwards-incompatible way. Scripts based on launchpadlib will break if
they use the feature that changed, but launchpadlib itself will still
function, and rewriting the launchpad-based script will fix the problem.
Scripts that do not use the feature will still work correctly.

From what I can see about the way lpsharp works, lpsharp scripts that do
not use the modified feature will still work. But, lpsharp scripts that
do use the feature will break, and these scripts _cannot be fixed_
without upgrading lpsharp and _then_ rewriting the script. Because the
lpsharp code was generated from an old version of the WADL, lpsharp has
no way to find out what the new service looks like.

In real life, we take great pains not to make backwards-incompatible
changes to the web service. So this scenario is unlikely to happen. But
I can't rule it out, especially since it can happen by accident. If it
does happen by accident, your users (unlike launchpadlib users) won't be
able to use a temporary hack to get their scripts working: that would
require a new lpsharp release, and you don't want to do a new release
just to deal with a temporary problem on the server side.

2. OK, now imagine that we add a backwards-compatible improvement to the
web service. You don't really have to imagine this because we do it all
the time. Looking at the service_root_json class in LpClientProxy.cs, I
see members like:

...
private string _pillars_link;
private string _translation_import_queue_entries_collection_link;
private string _bug_trackers_collection_link;
private string _countries_collection_link;
...

For (almost) any one of these links, there was a time in the past year
when the web service did not publish that link. The same is true for
(almost) all the named operations defined in LPClientProxy.cs as
people_findPerson, etc. When we add anything new to the web service,
existing installations of launchpadlib will pick up on it. Existing
installations of lpslash will not.

This is not a catastrophe--obviously scripts written before a feature
was published will not break just because they use a lpslash that
doesn't know about that feature. But it will be annoying to your users,
and it will tie lpslash's upgrade schedule to Launchpad's.

Your users will also be effectively unable to use the bleeding-edge
'devel' version of the web service, which changes on a regular basis and
which may break its own backwards compatibility from day to day. Which
brings me to...

3. Currently there are three versions of the Launchpad web service:
beta, 1.0 and devel. Each publishes a slightly (or drastically)
different WADL document. This is how we're able to make the strong
promises we make about backwards compatibility (see #1).

launchpadlib's Launchpad constructor takes a version name, and reads
(from the server or from a cache) the appropriate WADL document at
startup. If there are 5 versions of the web service, there can be up to
5 cached WADL documents on the user's hard drive, but there's only one
copy of launchpadlib. Five pieces of data, one piece of code.

By contrast, LpClientProxy.cs is derived from a specific version of the
WADL as it was served on a specific date. If there are 5 versions of the
web service, there need to be 5 versions of LpClientProxy.cs in a
lpslash release. That's five similar pieces of code packaged with
lpslash, which need to be updated whenever the web service is updated
(once a month, in the limiting case).

This isn't a showstopper (unless you consider the inability to access
the new features of 'devel' a showstopper), but it violates Don't Repeat
Yourself and it'll be 

Re: [Launchpad-dev] Adapters API (was Re: Exposing newCodeImport)

2010-03-31 Thread Leonard Richardson
  I guess you could implement IBranchTarget(obj).name and
  IBranchTarget(obj).newCodeImport() on the client-side too.
 
  As sugar for this case, or are you speaking about something else?
 
 
 I meant as a more general sugar.
 
 And, since my IRC backlog indicates I didn't make it clear enough
 here, I'm not saying Go forth and implement this solution, I'm
 discussing possible solutions to an interesting problem. It would be
 wise to seek Gary or Leonard's advice on this topic.

Please don't implement anything special on the client side. I'm not
totally opposed to a solution based on client-side conversion (as
opposed to my earlier server-side include the adapters mechanism) but
I want to be involved with that design and I don't have time right now.

If you need to do something right now, I suggest implementing and
exporting branch_target_name and branch_target_import() on the objects
you want to see them on. If we do the server-side solution I proposed
earlier, switching over will be invisible to the client. If we do a
client-side solution, we can make branch_target_name and
branch_target_import() disappear as of the first web service version to
support the client-side solution.

Leonard


___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] webservice futures

2010-03-17 Thread Leonard Richardson
 Hi Gary, Leonard,
 
 How will the API's version numbers track or match up with Ubuntu release?
 Will there be a new API version for every Ubuntu release?  Is there a wiki
 page on dev.lp.net that documents the mapping?

Barry,

There's no guarantee there will be a new API version for every Ubuntu
release. There's also no necessary relationship between the version
numbers and the name of the first Ubuntu release to use it by default--I
think that would confuse more than it would help.

The only relationship between API versions and Ubuntu releases is that
we can't deprecate an API version until the EOL of the last Ubuntu
release to use it by default.

If you go to the apidoc index page on edge
(https://edge.launchpad.net/+apidoc/), it now shows a list of all the
active versions, and the description of each version shows the last
Ubuntu release to use it.

Leonard


___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] Bug heat and the API

2010-03-09 Thread Leonard Richardson

  I really think that that excluding bug heat from the etag calculation is 
  the 
  right thing to do at this point. It's not like this information is 
  up-to-date 
  anyway. Bug heat isn't a real time data, so it shouldn't be included in the 
  Etag.
  
  As a general solution to the problem, Bjorn's suggest seems like a good 
  one, 
  but on this particular problem, simply remove it from the calculation. And 
  it's probable that simply having a way to exclude some fields from the Etag 
  would solve 80% of our problems very cheaply.
 
 For bug heat specifically, perhaps removing it from ETag is appropriate.
 However, do bear in mind that folk may be getting stale responses many
 months later if no other fields change: caches can be quite stubborn;
 must-revalidate will, after all, look at the ETag.

OK, I've finally had some time to think about this. 

1. If a field is important enough to include in the representation, it's
important enough to include in the ETag. As Robert points out, omitting
a read-only field from the ETag will stop clients from knowing when the
value changes.

2. I like Bjorn's idea of a two-part ETag, where both parts are checked
for GET requests/caching, but only one part is checked for PATCH/PUT
requests. But, I don't think this would comply with the HTTP standard.

There are two types of ETags, strong and weak. A strong ETag changes
whenever the entity (the entity-body or any entity-headers) changes in
any way. A weak ETag changes only on semantically significant changes,
and not when insignificant aspects of the entity change. These quotes
are from section 13.3.3.

Consider a two-part ETag, where one part describes the state of the
read-only fields and the other part describes the state of the
read-write fields. Taken together, the whole thing is a strong ETag.
Look at only the second part and you have a weak ETag.

But (13.3.3 again) The only function that the HTTP/1.1 protocol defines
on [ETags] is comparison. There's no looking at the second half of an
ETag. The whole thing has to match.

OK, so let's define another function on ETags, another weak comparison
function which only looks at the second half of the ETag. That goes
beyond HTTP/1.1 but it doesn't contradict the standard. Now:

We would like to validate GET requests using the strong comparison
function (looking at the strong ETag), so that even minor changes will
invalidate the cache and the client will always have the most up-to-date
information. We would like to validate PATCH requests by using the weak
comparison function, so that irrelevant changes to the resource don't
affect the success of the PATCH.

But, the HTTP standard says we have to do the opposite. Section 14.26
says The weak comparison function can only be used with GET or HEAD
requests.

The IETF draft that defines PATCH says, Clients wishing to apply a
patch document to a known entity can first acquire the strong ETag of
the resource to be modified, and use that Etag in the If-Match header on
the PATCH request to verify that the resource is still unchanged.

As I read the standards, if bug_heat has changed and I try to PATCH an
unrelated field, the correct behavior is that my PATCH should fail. I
don't think this makes sense for us given the way we implement PATCH,
but it does make sense in general.

I don't think anything bad will happen if we implement the two-part
ETag. The whole ETag string is in fact a strong ETag, so anything that
treats it as a strong ETag will still work. We'll just have some
server-side magic that secretly implements a weak comparison function to
make PATCH requests work. And if anyone actually tries to PATCH bug_heat
they'll still get an error.

I'd like to hear other peoples' readings of the standards, especially
Robert's.

Leonard


___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] launchpadlib release?

2009-12-17 Thread Leonard Richardson
Hey,

We're actually going to do a launchpadlib release today. (It's ready to
release, but I'm not doing it myself because gary and salgado want to
test the release process, which itself uses launchpadlib:
https://dev.launchpad.net/HackingLazrLibraries#Releases)

So, I am developing launchpadlib and lazr.restfulclient, but as Gary
says I'm focusing intently on the server side, which means the client
side (launchpadlib and lazr.restfulclient) suffers.

1. I went through the launchpadlib review queue:
https://code.edge.launchpad.net/launchpadlib/+activereviews
There were a lot of branches in there, but almost all of them had been
merged (there was a time when Launchpad thought launchpadlib was managed
by PQM, even though it wasn't, so a merged branch didn't get picked up
as 'merged') or disapproved and not marked 'rejected'. I see one very
small branch that slipped through the cracks in June, and a branch from
July that I thought had landed but now seems to have been reverted--I'm
not sure what's up with that, and I'll investigate today.

2. I also looked at the lazr.restfulclient review queue:
https://code.edge.launchpad.net/lazr.restfulclient/+activereviews
I see two branches from this month, both from non-Launchpad developers.
One of the branches was reviewed but the reviewer forgot to land it. I
was the first Launchpad developer to look at the other one. I'm taking
care of both of those today.

3. There were no pending branches for wadllib or lazr.restful.

So I should definitely pay closer attention to the review queues, but I
don't think there's a huge backlog. 

I'll add a section to https://dev.launchpad.net/HackingLazrLibraries on
the review and landing process, because it is a little different from
Launchpad, but it's also easier. If you're a Launchpad dev, you should
feel comfortable developing launchpadlib or lazr.restfulclient.

The reviews don't need to go through me--other Launchpad developers
review my branches all the time, and as jml points out, there were
changes committed (by allenap on behalf of others) to launchpadlib while
I was on vacation. With gary trying out the release process today,
there's now no reason I have to do the releases, either.

I'm interested in the idea of a predictable release schedule for
launchpadlib and lazr.restfulclient. I don't think we release only once
a month makes sense, because that would slow down the rate at which
improvements to these packages make it into Launchpad. But a policy of
we release _at least_ once a month would avoid the problem I think jml
is complaining about, where a useful launchpadlib change lands on
November 26 and there's no release until December 17, when I need to do
a release so I can land a Launchpad branch.

Leonard



signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp


Re: [Launchpad-dev] When will the table of contents reach the +apidoc page?

2009-10-05 Thread Leonard Richardson
 I don't want to sound impatient, but I would like to know when the
 fix for bug 325367 (The API docs should have a table of contents)
 will appear on https://edge.launchpad.net/+apidoc/? It would really
 improve the navigation on that page.
 
 I asked Karl Fogel about it (as he helped me to get the needed changes
 for it merged) and his reply sounded like it might happen around LP 3.0
 release, but now he doesn't know himself either.

Karl's change went into launchpadlib, not Launchpad itself. (1.5.2, the
most recent launchpadlib release, has it.) To get the change into
Launchpad we need to bring Launchpad's launchpadlib dependency up to
date. I don't know how we go about doing that, but that's what we have
to do.

At this point, since Launchpad is open source, it might make more sense
to move the XSLT file back into Launchpad. It was moved into
launchpadlib so that third parties could use it.

Leonard


signature.asc
Description: This is a digitally signed message part
___
Mailing list: https://launchpad.net/~launchpad-dev
Post to : launchpad-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~launchpad-dev
More help   : https://help.launchpad.net/ListHelp