// BaseStack builds new base stack for the given context value. func (s *Stacker) BaseStack(ctx context.Context) (*BaseStack, error) { bs := &BaseStack{ Planner: &Planner{ Provider: s.Provider.Name, ResourceType: s.Provider.resourceName(), Log: s.Log, }, Provider: s.Provider, KlientIDs: make(stack.KiteMap), Klients: make(map[string]*DialState), TunnelURL: s.TunnelURL, Keys: s.SSHKey, } var ok bool if bs.Req, ok = request.FromContext(ctx); !ok { return nil, errors.New("request not available in context") } req, ok := stack.TeamRequestFromContext(ctx) if !ok { return nil, errors.New("team request not available in context") } if bs.Session, ok = session.FromContext(ctx); !ok { return nil, errors.New("session not available in context") } bs.Log = s.Log.New(req.GroupName) if traceID, ok := stack.TraceFromContext(ctx); ok { bs.Log = logging.NewCustom("kloud-"+req.Provider, true).New(traceID) bs.TraceID = traceID } if keys, ok := publickeys.FromContext(ctx); ok { bs.Keys = keys } if ev, ok := eventer.FromContext(ctx); ok { bs.Eventer = ev } builderOpts := &BuilderOptions{ Log: s.Log.New("stackplan"), CredStore: s.CredStore, } bs.Builder = NewBuilder(builderOpts) if err := bs.Builder.BuildTeam(req.GroupName); err != nil { return nil, err } if !bs.Builder.Team.IsSubActive() { return nil, stack.NewError(stack.ErrTeamSubIsNotActive) } return bs, nil }
func (p *Planner) session(ctx context.Context) (*session.Session, error) { if p.SessionFunc != nil { return p.SessionFunc(ctx) } sess, ok := session.FromContext(ctx) if !ok { return nil, errors.New("session context is not passed") } return sess, nil }
// BuildMachines fetches machines that belongs to existing b.Stack. // // It validates whether user is allowed to perform apply operation. // When nil error is returned, the b.Machines field is non-nil. func (b *Builder) BuildMachines(ctx context.Context) error { ids := b.Stack.Machines sess, ok := session.FromContext(ctx) if !ok { return errors.New("session context is not passed") } req, ok := request.FromContext(ctx) if !ok { return errors.New("request context is not passed") } mongodbIds := make([]bson.ObjectId, len(ids)) for i, id := range ids { mongodbIds[i] = bson.ObjectIdHex(id) } b.Log.Debug("Building machines with IDs: %+v", ids) machines := make([]*models.Machine, 0) if err := sess.DB.Run("jMachines", func(c *mgo.Collection) error { return c.Find(bson.M{"_id": bson.M{"$in": mongodbIds}}).All(&machines) }); err != nil { return err } b.Log.Debug("Fetched machines: %+v", machines) validUsers := make(map[string]models.MachineUser, 0) validMachines := make(map[string]*models.Machine, 0) for _, machine := range machines { // machines with empty users are supposed to allowed by default // (gokmen) if len(machine.Users) == 0 { validMachines[machine.ObjectId.Hex()] = machine continue } // for others we need to be sure they are valid // TODO(arslan): add custom type with custom methods for type // []*Machineuser for _, user := range machine.Users { // we only going to select users that are allowed: // // - team member that owns vm (sudo + owner) // - team admin that owns all vms (owner + !permanent) // // A shared user is (owner + permanent). if (user.Sudo || !user.Permanent) && user.Owner { validUsers[user.Id.Hex()] = user } } } allowedIds := make([]bson.ObjectId, 0) for _, user := range validUsers { allowedIds = append(allowedIds, user.Id) } b.Log.Debug("Building users with allowed IDs: %+v", allowedIds) users, err := modelhelper.GetUsersById(allowedIds...) if err != nil { return models.ResError(err, "jUser") } // find whether requested user is among allowed ones var reqUser *models.User for _, u := range users { if u.Name == req.Username { reqUser = u break } } b.Log.Debug("Found requester: %v (requester username: %s)", reqUser, req.Username) if reqUser != nil { // now check if the requested user is inside the allowed users list for _, m := range machines { for _, user := range m.Users { if user.Id.Hex() == reqUser.ObjectId.Hex() { validMachines[m.ObjectId.Hex()] = m break } } } } if len(validMachines) == 0 { return fmt.Errorf("no valid machines found for the user: %s", req.Username) } b.Machines = make(map[string]*models.Machine, len(validMachines)) for _, m := range validMachines { label := m.Label if s, ok := m.Meta["assignedLabel"].(string); ok { label = s } b.Machines[label] = m } b.Log.Debug("Machines built: %+v", b.Machines) return nil }
func (k *Kloud) authorizedKlient(r *kite.Request) (*klient.Klient, error) { if r.Args == nil { return nil, NewError(ErrNoArguments) } var args *AdminRequest if err := r.Args.One().Unmarshal(&args); err != nil { return nil, err } if args.MachineId == "" { return nil, errors.New("machineId is not passed") } if args.GroupName == "" { return nil, errors.New("groupName is not passed") } k.Log.Debug("Got arguments %+v for method: %s", args, r.Method) isAdmin, err := modelhelper.IsAdmin(r.Username, args.GroupName) if err != nil { return nil, err } if !isAdmin { return nil, fmt.Errorf("User '%s' is not an admin of group '%s'", r.Username, args.GroupName) } k.Log.Debug("User '%s' is an admin. Checking for machine permission", r.Username) machine, err := modelhelper.GetMachine(args.MachineId) if err != nil { return nil, fmt.Errorf("getMachine(%s) err: %s", args.MachineId, err) } g, err := modelhelper.GetGroup(args.GroupName) if err != nil { return nil, err } isGroupMember := false for _, group := range machine.Groups { if group.Id.Hex() == g.Id.Hex() { isGroupMember = true } } if !isGroupMember { return nil, fmt.Errorf("'%s' machine does not belong to '%s' group", args.MachineId, args.GroupName) } k.Log.Debug("Incoming user is authorized, setting up DB and Klient connection") // Now we are ready to go. ctx := request.NewContext(context.Background(), r) ctx = k.ContextCreator(ctx) sess, ok := session.FromContext(ctx) if !ok { return nil, errors.New("internal server error (err: session context is not available)") } k.Log.Debug("Calling Klient method: %s", r.Method) return klient.NewWithTimeout(sess.Kite, machine.QueryString, time.Second*10) }