Esempio n. 1
0
// Login logs in with the provided credentials.
// All subsequent requests on the connection will
// act as the authenticated user.
func (a *srvAdmin) Login(c params.Creds) (params.LoginResult, error) {
	a.mu.Lock()
	defer a.mu.Unlock()
	if a.loggedIn {
		// This can only happen if Login is called concurrently.
		return params.LoginResult{}, errAlreadyLoggedIn
	}

	// Use the login validation function, if one was specified.
	if a.validator != nil {
		if err := a.validator(c); err != nil {
			return params.LoginResult{}, errors.Trace(err)
		}
	}

	// Users are not rate limited, all other entities are
	if kind, err := names.TagKind(c.AuthTag); err != nil || kind != names.UserTagKind {
		if !a.limiter.Acquire() {
			logger.Debugf("rate limiting, try again later")
			return params.LoginResult{}, common.ErrTryAgain
		}
		defer a.limiter.Release()
	}
	entity, err := doCheckCreds(a.root.srv.state, c)
	if err != nil {
		return params.LoginResult{}, err
	}
	if a.reqNotifier != nil {
		a.reqNotifier.login(entity.Tag().String())
	}
	// We have authenticated the user; now choose an appropriate API
	// to serve to them.
	// TODO: consider switching the new root based on who is logging in
	newRoot := newSrvRoot(a.root, entity)
	if err := a.startPingerIfAgent(newRoot, entity); err != nil {
		return params.LoginResult{}, err
	}

	// Fetch the API server addresses from state.
	hostPorts, err := a.root.srv.state.APIHostPorts()
	if err != nil {
		return params.LoginResult{}, err
	}
	logger.Debugf("hostPorts: %v", hostPorts)

	environ, err := a.root.srv.state.Environment()
	if err != nil {
		return params.LoginResult{}, err
	}

	a.root.rpcConn.ServeFinder(newRoot, serverError)
	lastConnection := getAndUpdateLastConnectionForEntity(entity)
	return params.LoginResult{
		Servers:        hostPorts,
		EnvironTag:     environ.Tag().String(),
		LastConnection: lastConnection,
		Facades:        newRoot.DescribeFacades(),
	}, nil
}
Esempio n. 2
0
func (*tagSuite) TestParseTag(c *gc.C) {
	for i, test := range parseTagTests {
		c.Logf("test %d: %q expectKind %q", i, test.tag, test.expectKind)
		tag, err := names.ParseTag(test.tag)
		if test.resultErr != "" {
			c.Assert(err, gc.ErrorMatches, test.resultErr)
			c.Assert(tag, gc.IsNil)

			// If the tag has a valid kind which matches the
			// expected kind, test that using an empty
			// expectKind does not change the error message.
			if tagKind, err := names.TagKind(test.tag); err == nil && tagKind == test.expectKind {
				tag, err := names.ParseTag(test.tag)
				c.Assert(err, gc.ErrorMatches, test.resultErr)
				c.Assert(tag, gc.IsNil)
			}
		} else {
			c.Assert(err, gc.IsNil)
			kind, id := tag.Kind(), tag.Id()
			c.Assert(err, gc.IsNil)
			c.Assert(id, gc.Equals, test.resultId)
			if test.expectKind != "" {
				c.Assert(kind, gc.Equals, test.expectKind)
			} else {
				expectKind, err := names.TagKind(test.tag)
				c.Assert(err, gc.IsNil)
				c.Assert(kind, gc.Equals, expectKind) // will be removed in the next branch
				c.Assert(tag, gc.FitsTypeOf, test.expectType)
			}
			// Check that it's reversible.
			if f := makeTag[kind]; f != nil {
				reversed := f(id).String()
				c.Assert(reversed, gc.Equals, test.tag)
			}
			// Check that it parses ok without an expectKind.
			tag, err := names.ParseTag(test.tag)
			c.Assert(err, gc.IsNil)
			c.Assert(tag, gc.FitsTypeOf, test.expectType)
			c.Assert(tag.Kind(), gc.Equals, test.expectKind) // will be removed in the next branch
			c.Assert(tag.Id(), gc.Equals, id)
		}
	}
}
Esempio n. 3
0
func (*tagSuite) TestTagKind(c *gc.C) {
	for i, test := range tagKindTests {
		c.Logf("test %d: %q -> %q", i, test.tag, test.kind)
		kind, err := names.TagKind(test.tag)
		if test.err == "" {
			c.Assert(test.kind, gc.Equals, kind)
			c.Assert(err, gc.IsNil)
		} else {
			c.Assert(kind, gc.Equals, "")
			c.Assert(err, gc.ErrorMatches, test.err)
		}
	}
}
Esempio n. 4
0
func newRsyslogConfigHandler(st *apirsyslog.State, mode RsyslogMode, tag, namespace string, stateServerAddrs []string) (*RsyslogConfigHandler, error) {
	var syslogConfig *syslog.SyslogConfig
	if mode == RsyslogModeAccumulate {
		syslogConfig = syslog.NewAccumulateConfig(
			tag, logDir, 0, namespace, stateServerAddrs,
		)
	} else {
		syslogConfig = syslog.NewForwardConfig(
			tag, logDir, 0, namespace, stateServerAddrs,
		)
	}

	// Historically only machine-0 includes the namespace in the log
	// dir/file; for backwards compatibility we continue the tradition.
	if tag != "machine-0" {
		namespace = ""
	}
	kind, err := names.TagKind(tag)
	if err != nil {
		return nil, err
	}
	if kind == names.MachineTagKind {
		if namespace == "" {
			syslogConfig.ConfigFileName = "25-juju.conf"
		} else {
			syslogConfig.ConfigFileName = fmt.Sprintf("25-juju-%s.conf", namespace)
		}
	} else {
		syslogConfig.ConfigFileName = fmt.Sprintf("26-juju-%s.conf", tag)
	}

	syslogConfig.ConfigDir = rsyslogConfDir
	syslogConfig.LogDir = logDir
	if namespace != "" {
		syslogConfig.LogDir += "-" + namespace
	}
	return &RsyslogConfigHandler{
		st:           st,
		mode:         mode,
		syslogConfig: syslogConfig,
		tag:          tag,
	}, nil
}
Esempio n. 5
0
func (a *admin) doLogin(req params.LoginRequest, loginVersion int) (params.LoginResultV1, error) {
	var fail params.LoginResultV1

	a.mu.Lock()
	defer a.mu.Unlock()
	if a.loggedIn {
		// This can only happen if Login is called concurrently.
		return fail, errAlreadyLoggedIn
	}

	// authedApi is the API method finder we'll use after getting logged in.
	var authedApi rpc.MethodFinder = newApiRoot(a.root.state, a.root.resources, a.root)

	// Use the login validation function, if one was specified.
	if a.srv.validator != nil {
		err := a.srv.validator(req)
		switch err {
		case params.UpgradeInProgressError:
			authedApi = newUpgradingRoot(authedApi)
		case AboutToRestoreError:
			authedApi = newAboutToRestoreRoot(authedApi)
		case RestoreInProgressError:
			authedApi = newRestoreInProgressRoot(authedApi)
		case nil:
			// in this case no need to wrap authed api so we do nothing
		default:
			return fail, errors.Trace(err)
		}
	}

	var agentPingerNeeded = true
	isUser := true
	kind := names.UserTagKind
	if req.AuthTag != "" {
		var err error
		kind, err = names.TagKind(req.AuthTag)
		if err != nil || kind != names.UserTagKind {
			isUser = false
			// Users are not rate limited, all other entities are.
			if !a.srv.limiter.Acquire() {
				logger.Debugf("rate limiting for agent %s", req.AuthTag)
				return fail, common.ErrTryAgain
			}
			defer a.srv.limiter.Release()
		}
	}

	serverOnlyLogin := a.root.modelUUID == ""

	entity, lastConnection, err := doCheckCreds(a.root.state, req, !serverOnlyLogin, a.srv.authCtxt)
	if err != nil {
		if err, ok := errors.Cause(err).(*common.DischargeRequiredError); ok {
			loginResult := params.LoginResultV1{
				DischargeRequired:       err.Macaroon,
				DischargeRequiredReason: err.Error(),
			}
			logger.Infof("login failed with discharge-required error: %v", err)
			return loginResult, nil
		}
		if a.maintenanceInProgress() {
			// An upgrade, restore or similar operation is in
			// progress. It is possible for logins to fail until this
			// is complete due to incomplete or updating data. Mask
			// transitory and potentially confusing errors from failed
			// logins with a more helpful one.
			return fail, MaintenanceNoLoginError
		}
		// Here we have a special case.  The machine agents that manage
		// models in the controller model need to be able to
		// open API connections to other models.  In those cases, we
		// need to look in the controller database to check the creds
		// against the machine if and only if the entity tag is a machine tag,
		// and the machine exists in the controller model, and the
		// machine has the manage state job.  If all those parts are valid, we
		// can then check the credentials against the controller model
		// machine.
		if kind != names.MachineTagKind {
			return fail, errors.Trace(err)
		}
		entity, err = a.checkCredsOfControllerMachine(req)
		if err != nil {
			return fail, errors.Trace(err)
		}
		// If we are here, then the entity will refer to a controller
		// machine in the controller model, and we don't need a pinger
		// for it as we already have one running in the machine agent api
		// worker for the controller model.
		agentPingerNeeded = false
	}
	a.root.entity = entity

	if a.reqNotifier != nil {
		a.reqNotifier.login(entity.Tag().String())
	}

	// We have authenticated the user; enable the appropriate API
	// to serve to them.
	a.loggedIn = true

	if agentPingerNeeded {
		if err := startPingerIfAgent(a.root, entity); err != nil {
			return fail, errors.Trace(err)
		}
	}

	var maybeUserInfo *params.AuthUserInfo
	var envUser *state.ModelUser
	// Send back user info if user
	if isUser && !serverOnlyLogin {
		maybeUserInfo = &params.AuthUserInfo{
			Identity:       entity.Tag().String(),
			LastConnection: lastConnection,
		}
		envUser, err = a.root.state.ModelUser(entity.Tag().(names.UserTag))
		if err != nil {
			return fail, errors.Annotatef(err, "missing ModelUser for logged in user %s", entity.Tag())
		}
		if envUser.ReadOnly() {
			logger.Debugf("model user %s is READ ONLY", entity.Tag())
		}
	}

	// Fetch the API server addresses from state.
	hostPorts, err := a.root.state.APIHostPorts()
	if err != nil {
		return fail, errors.Trace(err)
	}
	logger.Debugf("hostPorts: %v", hostPorts)

	environ, err := a.root.state.Model()
	if err != nil {
		return fail, errors.Trace(err)
	}

	loginResult := params.LoginResultV1{
		Servers:       params.FromNetworkHostsPorts(hostPorts),
		ModelTag:      environ.Tag().String(),
		ControllerTag: environ.ControllerTag().String(),
		Facades:       DescribeFacades(),
		UserInfo:      maybeUserInfo,
		ServerVersion: jujuversion.Current.String(),
	}

	// For sufficiently modern login versions, stop serving the
	// controller model at the root of the API.
	if serverOnlyLogin {
		authedApi = newRestrictedRoot(authedApi)
		// Remove the ModelTag from the response as there is no
		// model here.
		loginResult.ModelTag = ""
		// Strip out the facades that are not supported from the result.
		var facades []params.FacadeVersions
		for _, facade := range loginResult.Facades {
			if restrictedRootNames.Contains(facade.Name) {
				facades = append(facades, facade)
			}
		}
		loginResult.Facades = facades
	}

	if envUser != nil {
		authedApi = newClientAuthRoot(authedApi, envUser)
	}

	a.root.rpcConn.ServeFinder(authedApi, serverError)

	return loginResult, nil
}