コード例 #1
0
ファイル: builder.go プロジェクト: koding/koding
// BuildStack fetches stack details from MongoDB.
//
// When nil error is returned, the b.Stack field is non-nil.
func (b *Builder) BuildStack(stackID string, credentials map[string][]string) error {
	var overallErr error

	computeStack, err := modelhelper.GetComputeStack(stackID)
	if err != nil {
		return models.ResError(err, "jComputeStack")
	}

	b.Stack = &stack.Stack{
		ID:          computeStack.Id,
		Stack:       computeStack,
		Machines:    make([]string, len(computeStack.Machines)),
		Credentials: make(map[string][]string),
	}

	for i, m := range b.Stack.Stack.Machines {
		b.Stack.Machines[i] = m.Hex()
	}

	baseStackID := b.Stack.Stack.BaseStackId.Hex()

	// If fetching jStackTemplate fails, it might got deleted outside.
	// Continue building stack and let the caller decide, whether missing
	// jStackTemplate is fatal or not (e.g. for apply operations it's fatal,
	// for destroy ones - not).
	if stackTemplate, err := modelhelper.GetStackTemplate(baseStackID); err == nil {
		// first copy admin/group based credentials
		for k, v := range stackTemplate.Credentials {
			b.Stack.Credentials[k] = v
		}

		b.Stack.Template = stackTemplate.Template.Content
	} else {
		overallErr = models.ResError(err, "jStackTemplate")
	}

	// copy user based credentials
	for k, v := range computeStack.Credentials {
		// however don't override anything the admin already added
		if _, ok := b.Stack.Credentials[k]; !ok {
			b.Stack.Credentials[k] = v
		}
	}

	// Set or override credentials when passed in apply request.
	for k, v := range credentials {
		if len(v) != 0 {
			b.Stack.Credentials[k] = v
		}
	}

	b.Log.Debug("Stack built: len(machines)=%d, len(credentials)=%d, overallErr=%v",
		len(b.Stack.Machines), len(b.Stack.Credentials), overallErr)

	return overallErr
}
コード例 #2
0
ファイル: mongo.go プロジェクト: koding/koding
// fetchOne returns all user's teams.
func (m *MongoDatabase) fetchAll(user string) ([]*Team, error) {
	groups, err := m.adapter.FetchAccountGroups(user)
	if err != nil && err != mgo.ErrNotFound {
		return nil, models.ResError(err, modelhelper.GroupsCollectionName)
	}

	return groups2teams(groups...), nil
}
コード例 #3
0
ファイル: mongo.go プロジェクト: koding/koding
// fetchOne returns only specified team.
func (m *MongoDatabase) fetchOne(user, slug string) ([]*Team, error) {
	group, err := m.adapter.GetGroup(slug)
	if err == mgo.ErrNotFound {
		return []*Team{}, nil
	} else if err != nil {
		return nil, models.ResError(err, modelhelper.GroupsCollectionName)
	}

	switch participant, err := m.adapter.IsParticipant(user, slug); {
	case err != nil:
		return nil, models.ResError(err, modelhelper.RelationshipColl)
	case !participant:
		return nil, fmt.Errorf("user %q does not belong to %q group", user, slug)
	}

	return groups2teams(group), nil
}
コード例 #4
0
ファイル: builder.go プロジェクト: koding/koding
// BuildStackTemplate fetched stack template details from MongoDB.
//
// When nil error is returned, the b.StackTemplate field is guaranteed to be non-nil.
func (b *Builder) BuildStackTemplate(templateID string) error {
	var err error
	if b.StackTemplate, err = modelhelper.GetStackTemplate(templateID); err != nil {
		return models.ResError(err, "jStackTemplate")
	}

	if b.StackTemplate.Template.Content == "" {
		return errors.New("Stack template content is empty")
	}

	return nil
}
コード例 #5
0
ファイル: mongo.go プロジェクト: koding/koding
func (db *mongoDatabase) fetchTeams(perm *MongoPerm) (map[bson.ObjectId]*models.Group, error) {
	if perm.TeamModel != nil {
		return map[bson.ObjectId]*models.Group{
			perm.TeamModel.Id: perm.TeamModel,
		}, nil
	}

	belongs := modelhelper.Selector{
		"targetId":   perm.AccModel.Id,
		"sourceName": "JGroup",
		"as":         "member",
	}

	rels, err := modelhelper.GetAllRelationships(belongs)
	if err != nil {
		return nil, models.ResError(err, "jRelationship")
	}

	ids := make([]bson.ObjectId, len(rels))

	for i := range rels {
		ids[i] = rels[i].SourceId
	}

	groups, err := modelhelper.GetGroupsByIds(ids...)
	if err != nil {
		return nil, models.ResError(err, "jGroup")
	}

	teams := make(map[bson.ObjectId]*models.Group, len(groups))

	for i := range groups {
		teams[groups[i].Id] = groups[i]
	}

	return teams, nil
}
コード例 #6
0
ファイル: mongo.go プロジェクト: koding/koding
func (db *mongoDatabase) Validate(f *Filter, c *Cred) (Perm, error) {
	log := db.log().New("Validate")

	if err := f.Valid(); err != nil {
		return nil, err
	}

	if f.Matches(c.Perm) {
		return c.Perm, nil
	}

	perm := extractMongoPerm(f, c)

	log.Debug("extracted perm: %#v", perm)

	if err := db.fetchModels(f, perm); err != nil {
		// Return partially constructed perm to allow SetCreds
		// do alidate-or-create operation.
		return perm, err
	}

	belongs := modelhelper.Selector{
		"targetId": perm.CredModel.Id,
		"sourceId": bson.M{
			"$in": perm.CredGroups,
		},
		"as": bson.M{"$in": perm.Roles},
	}

	log.Debug("testing relationship for %+v", belongs)

	if count, err := modelhelper.RelationshipCount(belongs); err != nil || count == 0 {
		if err == nil {
			err = fmt.Errorf("user %q has no access to %q credential", f.Username, c.Ident)
		}

		return nil, models.ResError(err, "jRelationship")
	}

	if c.Perm == nil {
		c.Perm = perm
	}

	return perm, nil
}
コード例 #7
0
ファイル: team.go プロジェクト: koding/koding
// TestWhoami is a kite handler for a "team.whoami" kite method.
func (k *Kloud) TeamWhoami(r *kite.Request) (interface{}, error) {
	opts := &modelhelper.LookupGroupOptions{
		Username:    r.Username,
		KiteID:      r.Client.ID,
		ClientURL:   r.Client.URL,
		Environment: r.Client.Environment,
	}

	group, err := modelhelper.LookupGroup(opts)
	if err != nil {
		return nil, models.ResError(err, "jGroup")
	}

	return &WhoamiResponse{
		Team: &team.Team{
			Name:      group.Title,
			Slug:      group.Slug,
			Privacy:   group.Privacy,
			SubStatus: group.Payment.Subscription.Status,
		},
	}, nil
}
コード例 #8
0
ファイル: builder.go プロジェクト: koding/koding
// BuildCredentials fetches credential details for current b.Stack from MongoDB.
//
// When nil error is returned, the b.Koding and  b.Credentials fields are non-nil.
//
// TODO(rjeczalik): Replace with *credential.Client
func (b *Builder) BuildCredentials(method, username, groupname string, identifiers []string) error {
	// fetch jaccount from username
	account, err := modelhelper.GetAccount(username)
	if err != nil {
		return models.ResError(err, "jAccount")
	}

	// fetch jUser from username
	user, err := modelhelper.GetUser(username)
	if err != nil {
		return models.ResError(err, "jUser")
	}

	kodingMeta := &KodingMeta{
		Email:     user.Email,
		Username:  user.Name,
		Nickname:  account.Profile.Nickname,
		Firstname: account.Profile.FirstName,
		Lastname:  account.Profile.LastName,
		Hash:      account.Profile.Hash,
	}

	if b.StackTemplate != nil {
		kodingMeta.TemplateID = b.StackTemplate.Id.Hex()
	}

	if b.Stack != nil {
		kodingMeta.StackID = b.Stack.Stack.Id.Hex()
		kodingMeta.TemplateID = b.Stack.Stack.BaseStackId.Hex()
	}

	groupIDs := []bson.ObjectId{account.Id}

	if groupname != "" {
		// fetch jGroup from group slug name
		group, err := modelhelper.GetGroup(groupname)
		if err != nil {
			return models.ResError(err, "jGroup")
		}

		// validate if username belongs to groupnam
		selector := modelhelper.Selector{
			"targetId": account.Id,
			"sourceId": group.Id,
			"as": bson.M{
				"$in": []string{"member"},
			},
		}

		count, err := modelhelper.RelationshipCount(selector)
		if err != nil || count == 0 {
			return fmt.Errorf("username '%s' does not belong to group '%s'", username, groupname)
		}

		kodingMeta.Title = group.Title
		kodingMeta.Slug = group.Slug

		groupIDs = append(groupIDs, group.Id)
	}

	// 2- fetch credential from identifiers via args
	credentials, err := modelhelper.GetCredentialsFromIdentifiers(identifiers...)
	if err != nil {
		return models.ResError(err, "jCredential")
	}

	credentialTitles := make(map[string]string, len(credentials))
	for _, cred := range credentials {
		credentialTitles[cred.Identifier] = cred.Title
	}

	// 3- count relationship with credential id and jaccount id as user or
	// owner. Any non valid credentials will be discarded
	validKeys := make(map[string]string, len(credentials))

	permittedTargets, ok := credPermissions[method]
	if !ok {
		return fmt.Errorf("no permission data available for method '%s'", method)
	}

	for _, cred := range credentials {
		selector := modelhelper.Selector{
			"targetId": cred.Id,
			"sourceId": bson.M{
				"$in": groupIDs,
			},
			"as": bson.M{"$in": permittedTargets},
		}

		count, err := modelhelper.RelationshipCount(selector)
		if err != nil {
			return models.ResError(err, "jRelationship")
		}
		if count == 0 {
			return fmt.Errorf("credential with identifier '%s' is not validated: %v", cred.Identifier, err)
		}

		validKeys[cred.Identifier] = cred.Provider
	}

	// 5- return list of keys.
	b.Koding = &stack.Credential{
		Provider:   "koding",
		Credential: kodingMeta,
	}

	creds := make([]*stack.Credential, 0, len(validKeys))

	for ident, provider := range validKeys {
		creds = append(creds, &stack.Credential{
			Title:      credentialTitles[ident],
			Provider:   provider,
			Identifier: ident,
		})
	}

	if err := b.FetchCredentials(username, creds...); err != nil {
		// TODO(rjeczalik): add *NotFoundError support to CredStore
		return models.ResError(err, "jCredentialData")
	}

	b.Credentials = append(b.Credentials, creds...)

	for i, cred := range b.Credentials {
		b.Log.Debug("Built credential #%d: %# v (%+v, %+v)", i, cred, cred.Credential, cred.Bootstrap)
	}

	return nil
}
コード例 #9
0
ファイル: builder.go プロジェクト: koding/koding
// 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
}
コード例 #10
0
ファイル: mongo.go プロジェクト: koding/koding
// Machines returns all machines stored in MongoDB database that matches a given
// filter.
func (m *MongoDatabase) Machines(f *Filter) ([]*Machine, error) {
	if m.adapter == nil {
		return nil, errors.New("database adapter is unavailable")
	}

	if f == nil {
		return nil, errors.New("machine filter is not set")
	}

	if f.Username == "" {
		return nil, errors.New("machine requires user name to be provided")
	}

	// Get all machines that can be seen by provided user. This also includes
	// shared machines.
	machinesDB, err := m.adapter.GetParticipatedMachinesByUsername(f.Username)
	if err != nil {
		return nil, models.ResError(err, modelhelper.MachinesColl)
	}

	// We do not need machines from koding solo(koding provider) so, skip them.
	for i := 0; i < len(machinesDB); i++ {
		if machinesDB[i].Provider == modelhelper.MachineProviderKoding {
			machinesDB = append(machinesDB[:i], machinesDB[i+1:]...)
			i--
		}
	}

	// Leave only shared machines that user approved.
	if f.OnlyApproved {
		for i := 0; i < len(machinesDB); i++ {
			for j := range machinesDB[i].Users {
				if machinesDB[i].Users[j].Username == f.Username && // user of machine
					!machinesDB[i].Users[j].Owner && // and not an owner
					!machinesDB[i].Users[j].Approved { // who didn't approve sharing.
					machinesDB = append(machinesDB[:i], machinesDB[i+1:]...)
					i--
				}
			}
		}
	}

	// Get stack template IDs used by machines. Using map as the storage will
	// remove duplicated values.
	stackTmplIDs := make(map[bson.ObjectId]struct{})
	for i := range machinesDB {
		if machinesDB[i].GeneratedFrom != nil {
			stackTmplIDs[machinesDB[i].GeneratedFrom.TemplateId] = struct{}{}
		}
	}

	// We don't need to search in jGroups collection in order to find team name
	// because jStackTemplates also contains that information.
	stackTmplsDB, err := m.adapter.GetStackTemplateFieldsByIds(
		toSlice(stackTmplIDs),             // stack templates to find.
		[]string{"_id", "group", "title"}, // fields we need from jStackTemplates.
	)
	if err != nil {
		return nil, models.ResError(err, modelhelper.StackTemplateColl)
	}

	// Helper made to simplify searching for group and title names.
	groupTitles := make(map[bson.ObjectId][2]string, len(stackTmplsDB))
	for _, st := range stackTmplsDB {
		groupTitles[st.Id] = [2]string{st.Group, st.Title}
	}

	machines := make([]*Machine, len(machinesDB))
	for i, mdb := range machinesDB {
		machines[i] = &Machine{
			ID:          mdb.ObjectId.Hex(),
			Provider:    mdb.Provider,
			Label:       mdb.Label,
			IP:          hostOnly(mdb.IpAddress),
			QueryString: mdb.QueryString,
			RegisterURL: mdb.RegisterURL,
			CreatedAt:   mdb.CreatedAt,
			Status: Status{
				State:      mdb.Status.State,
				Reason:     mdb.Status.Reason,
				ModifiedAt: mdb.Status.ModifiedAt,
			},
			Users: filterUsers(mdb.Users, f),
		}

		if mdb.GeneratedFrom != nil {
			machines[i].Team = groupTitles[mdb.GeneratedFrom.TemplateId][0]
			machines[i].Stack = groupTitles[mdb.GeneratedFrom.TemplateId][1]
		}
	}

	return machines, nil
}
コード例 #11
0
ファイル: mongo.go プロジェクト: koding/koding
func (db *mongoDatabase) fetchCreds(f *Filter, acc *models.Account, user *models.User,
	teams map[bson.ObjectId]*models.Group, belongs modelhelper.Selector, creds *[]*Cred) error {

	db.log().Debug("fetching credentials for %+v", belongs)

	rels, err := modelhelper.GetAllRelationships(belongs)
	if err != nil && err != mgo.ErrNotFound {
		return models.ResError(err, "jRelationship")
	}

	ids := make([]bson.ObjectId, len(rels))

	for i := range rels {
		ids[i] = rels[i].TargetId
	}

	c, err := modelhelper.GetCredentialByIDs(ids...)
	if err == mgo.ErrNotFound {
		return nil // nothing to fetch, ignore dangling jRelationship
	}
	if err != nil {
		return err
	}

	credentials := make(map[bson.ObjectId]*models.Credential, len(c))

	for i := range c {
		credentials[c[i].Id] = c[i]
	}

	for _, rel := range rels {
		cred, ok := credentials[rel.TargetId]
		if !ok {
			continue // ignore dangling jRelationship
		}

		if f.Provider != "" && cred.Provider != f.Provider {
			// provider does not contains, filter out
			continue
		}

		c := &Cred{
			Ident:    cred.Identifier,
			Provider: cred.Provider,
			Title:    cred.Title,
			Perm: &MongoPerm{
				AccModel:  acc,
				UserModel: user,
				CredModel: cred,
				RoleNames: UserRole,
			},
		}

		db.log().Debug("fetched %+v", c)

		if team, ok := teams[rel.SourceId]; ok {
			c.Team = team.Slug
			c.Perm.(*MongoPerm).TeamModel = team
		}

		*creds = append(*creds, c)
	}

	return nil
}
コード例 #12
0
ファイル: mongo.go プロジェクト: koding/koding
func (db *mongoDatabase) fetchModels(f *Filter, perm *MongoPerm) (err error) {
	log := db.Log.New("fetchModels")

	if perm.AccModel == nil {
		log.Debug("fetching %q account", f.Username)

		perm.AccModel, err = modelhelper.GetAccount(f.Username)
		if err != nil {
			return models.ResError(err, "jAccount")
		}

		perm.CredGroups = append(perm.CredGroups, perm.AccModel.Id)
	}

	if perm.UserModel == nil {
		log.Debug("fetching %q user", f.Username)

		perm.UserModel, err = modelhelper.GetUser(f.Username)
		if err != nil {
			return models.ResError(err, "jUser")
		}
	}

	if f.Teamname != "" {
		if perm.TeamModel == nil {
			log.Debug("fetching %q team", f.Teamname)

			perm.TeamModel, err = modelhelper.GetGroup(f.Teamname)
			if err != nil {
				return models.ResError(err, "jGroup")
			}

			perm.CredGroups = append(perm.CredGroups, perm.TeamModel.Id)
		}

		if !perm.Member {
			belongs := modelhelper.Selector{
				"targetId": perm.AccModel.Id,
				"sourceId": perm.TeamModel.Id,
				"as":       "member",
			}

			log.Debug("testing relationship for %+v", belongs)

			if count, err := modelhelper.RelationshipCount(belongs); err != nil || count == 0 {
				if err == nil {
					err = fmt.Errorf("user %q does not belong to %q group", f.Username, f.Teamname)
				}

				return models.ResError(err, "jRelationship")
			}

			perm.Member = true
		}
	}

	if perm.CredModel == nil && f.Ident != "" {
		log.Debug("fetching %q credential", f.Ident)

		perm.CredModel, err = modelhelper.GetCredential(f.Ident)
		if err != nil {
			return models.ResError(err, "jCredential")
		}
	}

	return nil
}