Affected files:
A [revision details]
M juju/agents/api.py
M juju/rapi/context.py
M juju/rapi/tests/test_context.py
M juju/state/initialize.py
M juju/state/tests/test_initialize.py
+ # Create a node that only the admin can read.
+ yield self.client.create("/login", acls=[
+ make_ace(self.admin_identity, all=True)])
+
# This must come last, since clients will wait on it.
yield self.client.create("/initialized", acls=acls)
Reviewers: mp+140268_ code.launchpad. net,
Message:
Please take a look.
Description:
Add a login api
Login support using admin credentials/secret
https:/ /code.launchpad .net/~hazmat/ juju/rapi- login/+ merge/140268
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/6935068/
Affected files: context. py tests/test_ context. py initialize. py tests/test_ initialize. py
A [revision details]
M juju/agents/api.py
M juju/rapi/
M juju/rapi/
M juju/state/
M juju/state/
Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: <email address hidden>
+New revision: <email address hidden>
Index: juju/agents/api.py api.py' transport. ws import WebSocketAPIFactory
=== modified file 'juju/agents/
--- juju/agents/api.py 2012-09-10 03:43:29 +0000
+++ juju/agents/api.py 2012-12-12 19:30:20 +0000
@@ -11,6 +11,7 @@
from juju.errors import JujuError
from juju.lib.websockets import WebSocketsResource
from juju.rapi.
+from juju.state.auth import make_ace
from twisted.web.static import Data
log. debug(" Received environment data.")
self. ws_factory = WebSocketAPIFac tory( 'zookeeper_ servers' ], self.provider) 'zookeeper_ servers' ], self.provider) factory. startFactory( )
from twisted.web.server import Site
@@ -32,7 +33,7 @@
root = Data("Juju API Server\n", "text/plain")
- self.config[
+ self.config[
yield self.ws_
@@ -79,10 +80,30 @@
+ # Lazy initialize login nodes if not present. e_login( ) fig()
config. parse(environme nt_data)
returnValue( config. get_default( ))
+ yield self._initializ
+
config = EnvironmentsCon
+ @inlineCallbacks login(self) : get_children( "/") get_acl( "/initialized" ) .startswith( 'admin' ): ace(admin_ acl['id' ], all=True)])
super( APIEndpointAgen t, self).configure (options) get("port" ):
+ def _initialize_
+ children = yield self.client.
+ if "login" in children:
+ return
+
+ acls, stat = yield self.client.
+ admin_acl = None
+
+ for a in acls:
+ if a['id']
+ admin_acl = a
+ break
+
+ yield self.client.create(
+ "/login", acls=[make_
+
def configure(self, options):
if not options.
Index: juju/rapi/ context. py context. py' context. py 2012-10-12 20:49:45 +0000 context. py 2012-12-12 19:30:20 +0000 internet. defer import maybeDeferred, succeed internet. defer import (
=== modified file 'juju/rapi/
--- juju/rapi/
+++ juju/rapi/
@@ -1,5 +1,6 @@
from StringIO import StringIO
-from twisted.
+from twisted.
+ maybeDeferred, succeed, returnValue, inlineCallbacks)
from juju.rapi.cmd import add_relation
from juju.rapi.cmd import add_unit
@@ -22,7 +23,10 @@
from juju.rapi import rest
+from juju.state.security import Principal
+
import logging
+import zookeeper
log = logging. getLogger( 'juju.rapi. context' )
@@ -78,7 +82,7 @@
lambda r: {'result': r, 'log': self.log.reset()} self._on_ error) self._on_ error)
d = maybeDeferred(func, *args, **kw)
return d.addCallback(
- ).addErrback(
+ ).addErrback(
def _on_error(self, failure):
constraints_ get.constraints _get,
io = StringIO()
@@ -134,8 +138,7 @@
return self._invoke(
self,
- named_entities
- )
+ named_entities)
def get_config(self, service_name):
constraints,
config,
config_ raw,
"""Get configuration of a service.
@@ -163,8 +166,7 @@
- num_units
- )
+ num_units)
def debug_hooks(self, unit_name, hook_names=()):
"""Enable hook debug on a unit.
@@ -206,6 +208,25 @@
self,
data)
+ def login(self, user, password): attach( self.client) get("/login" ) NoAuthException , zookeeper. AuthFailedExcep tion): error(" Invalid credentials") info("Login success") relation( self, endpoint_a, endpoint_b):
+ return self._invoke(
+ self._login,
+ user,
+ password)
+
+ @inlineCallbacks
+ def _login(self, user, password):
+ principal = Principal(user, password)
+ yield principal.
+ try:
+ yield self.client.
+ except (zookeeper.
+ self.log.
+ raise
+ else:
+ self.log.
+ returnValue(True)
+
def remove_
"""Remove a relation from a service(s).
"""
Index: juju/state/ initialize. py initialize. py' initialize. py 2012-07-18 01:41:26 +0000 initialize. py 2012-12-12 19:30:20 +0000 tateManager( self.client) set_provider_ type(self. provider_ type)
=== modified file 'juju/state/
--- juju/state/
+++ juju/state/
@@ -71,6 +71,10 @@
settings = GlobalSettingsS
yield settings.
+ # Create a node that only the admin can read. create( "/login" , acls=[ self.admin_ identity, all=True)]) create( "/initialized" , acls=acls)
+ yield self.client.
+ make_ace(
+
# This must come last, since clients will wait on it.
yield self.client.
Index: juju/rapi/ tests/test_ context. py tests/test_ context. py' tests/test_ context. py 2012-11-07 12:49:11 +0000 tests/test_ context. py 2012-12-12 19:30:20 +0000 internet. defer import inlineCallbacks tests.common import ContextTestBase
=== modified file 'juju/rapi/
--- juju/rapi/
+++ juju/rapi/
@@ -1,6 +1,9 @@
from twisted.
from juju.rapi.
+from juju.state.security import Principal
+from juju.state.auth import make_ace
+
class APIContextTest( ContextTestBase ):
@@ -159,6 +162,20 @@
self. assertEqual( response[ 'result' ], cs)
@ inlineCallbacks ace(principal. get_token( ), all=True)]) login(u, "hello") l(result[ 'log'], [('error', 'Invalid credentials')]) l(result[ 'err'], True) login(u, p) l(result[ 'result' ], True) l(result[ 'log'], [('info', 'Login success')]) delete( "/login" ) and_unexpose( self): store_charms( ["precise/ wordpress" ])
self. mocker. replay( )
+ def test_login(self):
+ u, p = "admin", "cekret"
+ principal = Principal(u, p)
+ self.client.create(
+ "/login", acls=[make_
+ result = yield self.context.
+ self.assertEqua
+ self.assertEqua
+ result = yield self.context.
+ self.assertEqua
+ self.assertEqua
+ yield self.client.
+
+ @inlineCallbacks
def test_expose_
yield self.mock_
Index: juju/state/ tests/test_ initialize. py tests/test_ initialize. py' tests/test_ initialize. py 2012-04-06 14:42:06 +0000 tests/test_ initialize. py 2012-12-12 19:30:20 +0000 tests.utils import deleteTree
=== modified file 'juju/state/
--- juju/state/
+++ juju/state/
@@ -4,11 +4,12 @@
from txzookeeper.
from juju.environmen t.tests. test_config import EnvironmentsCon figTestBase environment import ( tingsStateManag er, EnvironmentStat eManager) initialize import StateHierarchy
-from juju.state.auth import make_identity
+
from juju.state.
GlobalSet
from juju.state.
from juju.state.machine import MachineStateManager
+from juju.state.security import Principal
class LayoutTest( EnvironmentsCon figTestBase) : logging( "juju.state. init")
zookeeper. set_debug_ level(0)
self. client = self.get_ zookeeper_ client( ) "admin: genie") get_token( )
constraints_ data = {
"arch": "arm",
"cpu": None,
self. layout = StateHierarchy(
self.client, self.identity, "i-abcdef", connect( ) attach( self.client)
@@ -19,7 +20,9 @@
self.log = self.capture_
- self.identity = make_identity(
+ self.principal = Principal("admin", "genie")
+ self.identity = self.principal.
+
@@ -28,6 +31,7 @@
constraints_data, "dummy")
yield self.client.
+ yield self.principal.
def tearDown(self):
deleteTree( handle= self.client. handle) existence_ and_acl( "/machines" ) existence_ and_acl( "/relations" ) existence_ and_acl( "/initialized" ) existence_ and_acl( "/login" )
@@ -57,6 +61,7 @@
yield self.assert_
yield self.assert_
yield self.assert_
+ yield self.assert_
# To check that the constraints landed correctly, we need the
# environment config to have been sent, or we won't be able to