// 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 }
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) } } }
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) } } }
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 }
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 = ¶ms.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 }