コード例 #1
0
ファイル: room.go プロジェクト: robot0x/heim
func (rb *RoomBinding) GetMessage(ctx scope.Context, id snowflake.Snowflake) (*proto.Message, error) {
	var msg Message

	nDays, err := rb.DbMap.SelectInt("SELECT retention_days FROM room WHERE name = $1", rb.Name)
	if err != nil {
		return nil, err
	}

	err = rb.DbMap.SelectOne(
		&msg,
		"SELECT room, id, previous_edit_id, parent, posted, edited, deleted,"+
			" session_id, sender_id, sender_name, server_id, server_era, content, encryption_key_id"+
			" FROM message WHERE room = $1 AND id = $2",
		rb.Name, id.String())
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, proto.ErrMessageNotFound
		}
		return nil, err
	}
	if nDays > 0 {
		threshold := time.Now().Add(time.Duration(-nDays) * 24 * time.Hour)
		if msg.Posted.Before(threshold) {
			return nil, proto.ErrMessageNotFound
		}
	}
	m := msg.ToBackend()
	return &m, nil
}
コード例 #2
0
ファイル: account.go プロジェクト: NotAMoose/heim
func (b *AccountManagerBinding) SetUserKey(
	ctx scope.Context, accountID snowflake.Snowflake, key *security.ManagedKey) error {

	if !key.Encrypted() {
		return security.ErrKeyMustBeEncrypted
	}

	res, err := b.DbMap.Exec(
		"UPDATE account SET encrypted_user_key = $2 WHERE id = $1", accountID.String(), key.Ciphertext)
	if err != nil {
		if err == sql.ErrNoRows {
			return proto.ErrAccountNotFound
		}
		return err
	}

	n, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if n == 0 {
		return proto.ErrAccountNotFound
	}

	return nil
}
コード例 #3
0
ファイル: account.go プロジェクト: logan/heim
func (b *AccountManagerBinding) getOTP(db gorp.SqlExecutor, kms security.KMS, accountID snowflake.Snowflake) (*proto.OTP, error) {
	encryptedOTP, err := b.getRawOTP(db, accountID)
	if err != nil {
		return nil, err
	}

	key := security.ManagedKey{
		KeyType:      OTPKeyType,
		IV:           encryptedOTP.IV,
		Ciphertext:   encryptedOTP.EncryptedKey,
		ContextKey:   "account",
		ContextValue: accountID.String(),
	}
	if err := kms.DecryptKey(&key); err != nil {
		return nil, err
	}

	uriBytes, err := security.DecryptGCM(&key, encryptedOTP.IV, encryptedOTP.Digest, encryptedOTP.EncryptedURI, nil)
	if err != nil {
		return nil, err
	}

	otp := &proto.OTP{
		URI:       string(uriBytes),
		Validated: encryptedOTP.Validated,
	}
	return otp, nil
}
コード例 #4
0
ファイル: account.go プロジェクト: NotAMoose/heim
func (b *AccountManagerBinding) RevokeStaff(ctx scope.Context, accountID snowflake.Snowflake) error {
	_, err := b.DbMap.Exec(
		"DELETE FROM capability USING account"+
			" WHERE account.id = $1 AND capability.id = account.staff_capability_id",
		accountID.String())
	return err
}
コード例 #5
0
ファイル: account.go プロジェクト: robot0x/heim
func (m *accountManager) GrantStaff(
	ctx scope.Context, accountID snowflake.Snowflake, kmsCred security.KMSCredential) error {

	m.b.Lock()
	defer m.b.Unlock()

	account, ok := m.b.accounts[accountID.String()]
	if !ok {
		return proto.ErrAccountNotFound
	}
	memAcc := account.(*memAccount)

	kms := kmsCred.KMS()
	key := memAcc.sec.SystemKey.Clone()
	if err := kms.DecryptKey(&key); err != nil {
		return err
	}

	nonce, err := kms.GenerateNonce(key.KeyType.BlockSize())
	if err != nil {
		return err
	}

	capability, err := security.GrantSharedSecretCapability(&key, nonce, kmsCred.KMSType(), kmsCred)
	if err != nil {
		return err
	}

	memAcc.staffCapability = capability
	return nil
}
コード例 #6
0
ファイル: message.go プロジェクト: logan/heim
func NewMessage(
	roomName string, sessionView proto.SessionView, id, parent snowflake.Snowflake, keyID, content string) (
	*Message, error) {

	msg := &Message{
		Room:                roomName,
		ID:                  id.String(),
		Parent:              parent.String(),
		Posted:              id.Time(),
		Content:             content,
		SessionID:           sessionView.SessionID,
		SenderID:            string(sessionView.ID),
		SenderName:          sessionView.Name,
		ServerID:            sessionView.ServerID,
		ServerEra:           sessionView.ServerEra,
		SenderClientAddress: sessionView.ClientAddress,
		SenderIsManager:     sessionView.IsManager,
		SenderIsStaff:       sessionView.IsStaff,
	}
	if keyID != "" {
		msg.EncryptionKeyID = sql.NullString{
			String: keyID,
			Valid:  true,
		}
	}
	return msg, nil
}
コード例 #7
0
ファイル: pm.go プロジェクト: logan/heim
func (t *PMTracker) Room(ctx scope.Context, kms security.KMS, pmID snowflake.Snowflake, client *proto.Client) (proto.Room, *security.ManagedKey, error) {
	row, err := t.Backend.Get(PM{}, pmID.String())
	if row == nil || err != nil {
		if row == nil || err == sql.ErrNoRows {
			return nil, nil, proto.ErrPMNotFound
		}
	}

	pm := row.(*PM).ToBackend()
	pmKey, modified, otherName, err := pm.Access(ctx, t.Backend, kms, client)
	if err != nil {
		return nil, nil, err
	}

	if modified {
		_, err := t.Backend.DbMap.Exec(
			"UPDATE pm SET receiver = $2, receiver_mac = $3, encrypted_receiver_key = $4 WHERE id = $1",
			pm.ID.String(), string(pm.Receiver), pm.ReceiverMAC, pm.EncryptedReceiverKey.Ciphertext)
		if err != nil {
			return nil, nil, err
		}
	}

	room := &PMRoomBinding{
		RoomBinding: RoomBinding{
			RoomName:  fmt.Sprintf("pm:%s", pm.ID),
			RoomTitle: fmt.Sprintf("%s (private chat)", otherName),
			Backend:   t.Backend,
		},
		pm: pm,
	}

	return room, pmKey, nil
}
コード例 #8
0
ファイル: message.go プロジェクト: kennylixi/heim
func NewMessage(
	room *Room, sessionView *proto.SessionView, id, parent snowflake.Snowflake, keyID, content string) (
	*Message, error) {

	msg := &Message{
		Room:    room.Name,
		ID:      id.String(),
		Parent:  parent.String(),
		Posted:  id.Time(),
		Content: content,
	}
	if sessionView != nil {
		msg.SessionID = sessionView.SessionID
		msg.SenderID = string(sessionView.ID)
		msg.SenderName = sessionView.Name
		msg.ServerID = sessionView.ServerID
		msg.ServerEra = sessionView.ServerEra
		msg.SenderIsManager = sessionView.IsManager
		msg.SenderIsStaff = sessionView.IsStaff
	}
	if keyID != "" {
		msg.EncryptionKeyID = sql.NullString{
			String: keyID,
			Valid:  true,
		}
	}
	return msg, nil
}
コード例 #9
0
ファイル: backend.go プロジェクト: rmasoni/heim
func (b *Backend) latest(ctx scope.Context, room *Room, n int, before snowflake.Snowflake) (
	[]proto.Message, error) {

	if n <= 0 {
		return nil, nil
	}
	// TODO: define constant
	if n > 1000 {
		n = 1000
	}

	var query string
	args := []interface{}{room.Name, n}

	// Get the time before which messages will be expired
	nDays, err := b.DbMap.SelectInt("SELECT retention_days FROM room WHERE name = $1", room.Name)
	if err != nil {
		return nil, err
	}
	if nDays == 0 {
		if before.IsZero() {
			query = ("SELECT room, id, previous_edit_id, parent, posted, edited, deleted," +
				" session_id, sender_id, sender_name, server_id, server_era, content, encryption_key_id" +
				" FROM message WHERE room = $1 AND deleted IS NULL ORDER BY id DESC LIMIT $2")
		} else {
			query = ("SELECT room, id, previous_edit_id, parent, posted, edited, deleted," +
				" session_id, sender_id, sender_name, server_id, server_era, content, encryption_key_id" +
				" FROM message WHERE room = $1 AND id < $3 AND deleted IS NULL ORDER BY id DESC LIMIT $2")
			args = append(args, before.String())
		}
	} else {
		threshold := time.Now().Add(time.Duration(-nDays) * 24 * time.Hour)
		if before.IsZero() {
			query = ("SELECT room, id, previous_edit_id, parent, posted, edited, deleted," +
				" session_id, sender_id, sender_name, server_id, server_era, content, encryption_key_id" +
				" FROM message WHERE room = $1 AND posted > $3 AND deleted IS NULL ORDER BY id DESC LIMIT $2")
		} else {
			query = ("SELECT room, id, previous_edit_id, parent, posted, edited, deleted," +
				" session_id, sender_id, sender_name, server_id, server_era, content, encryption_key_id" +
				" FROM message WHERE room = $1 AND id < $3 AND deleted IS NULL AND posted > $4 ORDER BY id DESC LIMIT $2")
			args = append(args, before.String())
		}
		args = append(args, threshold)
	}

	msgs, err := b.DbMap.Select(Message{}, query, args...)
	if err != nil {
		return nil, err
	}

	results := make([]proto.Message, len(msgs))
	for i, row := range msgs {
		msg := row.(*Message)
		results[len(msgs)-i-1] = msg.ToBackend()
	}

	return results, nil
}
コード例 #10
0
ファイル: account.go プロジェクト: rmasoni/heim
func (b *AccountManagerBinding) ChangeClientKey(
	ctx scope.Context, accountID snowflake.Snowflake, oldKey, newKey *security.ManagedKey) error {

	t, err := b.DbMap.Begin()
	if err != nil {
		return err
	}

	rollback := func() {
		if err := t.Rollback(); err != nil {
			backend.Logger(ctx).Printf("rollback error: %s", err)
		}
	}

	var account Account
	err = t.SelectOne(
		&account,
		"SELECT nonce, mac, encrypted_user_key, encrypted_private_key FROM account WHERE id = $1",
		accountID.String())
	if err != nil {
		rollback()
		if err == sql.ErrNoRows {
			return proto.ErrAccountNotFound
		}
		return err
	}

	sec := account.Bind(b.Backend).accountSecurity()
	if err := sec.ChangeClientKey(oldKey, newKey); err != nil {
		rollback()
		return err
	}

	res, err := t.Exec(
		"UPDATE account SET mac = $2, encrypted_user_key = $3 WHERE id = $1",
		accountID.String(), sec.MAC, sec.UserKey.Ciphertext)
	if err != nil {
		rollback()
		return err
	}

	n, err := res.RowsAffected()
	if err != nil {
		rollback()
		return err
	}
	if n == 0 {
		rollback()
		return proto.ErrAccountNotFound
	}

	if err := t.Commit(); err != nil {
		return err
	}

	return nil
}
コード例 #11
0
ファイル: account.go プロジェクト: robot0x/heim
func (m *accountManager) Get(ctx scope.Context, id snowflake.Snowflake) (proto.Account, error) {
	m.b.Lock()
	defer m.b.Unlock()

	account, ok := m.b.accounts[id.String()]
	if !ok {
		return nil, proto.ErrAccountNotFound
	}
	return account, nil
}
コード例 #12
0
ファイル: account.go プロジェクト: logan/heim
func (b *AccountManagerBinding) getRawOTP(db gorp.SqlExecutor, accountID snowflake.Snowflake) (*OTP, error) {
	row, err := db.Get(OTP{}, accountID.String())
	if row == nil || err != nil {
		if row == nil || err == sql.ErrNoRows {
			return nil, proto.ErrOTPNotEnrolled
		}
		return nil, err
	}
	return row.(*OTP), nil
}
コード例 #13
0
ファイル: backend.go プロジェクト: logan/heim
func (b *Backend) latest(ctx scope.Context, rb *RoomBinding, n int, before snowflake.Snowflake) (
	[]proto.Message, error) {

	if n <= 0 {
		return nil, nil
	}
	// TODO: define constant
	if n > 1000 {
		n = 1000
	}

	var query string
	args := []interface{}{rb.RoomName, n}

	// Get the time before which messages will be expired
	nDays, err := b.DbMap.SelectInt("SELECT retention_days FROM room WHERE name = $1", rb.RoomName)
	if err != nil {
		return nil, err
	}
	cols, err := allColumns(b.DbMap, Message{}, "")
	if err != nil {
		return nil, err
	}
	if nDays == 0 {
		if before.IsZero() {
			query = fmt.Sprintf("SELECT %s FROM message WHERE room = $1 AND deleted IS NULL ORDER BY id DESC LIMIT $2", cols)
		} else {
			query = fmt.Sprintf("SELECT %s FROM message WHERE room = $1 AND id < $3 AND deleted IS NULL ORDER BY id DESC LIMIT $2", cols)
			args = append(args, before.String())
		}
	} else {
		threshold := time.Now().Add(time.Duration(-nDays) * 24 * time.Hour)
		if before.IsZero() {
			query = fmt.Sprintf("SELECT %s FROM message WHERE room = $1 AND posted > $3 AND deleted IS NULL ORDER BY id DESC LIMIT $2", cols)
		} else {
			query = fmt.Sprintf(
				"SELECT %s FROM message WHERE room = $1 AND id < $3 AND deleted IS NULL AND posted > $4 ORDER BY id DESC LIMIT $2", cols)
			args = append(args, before.String())
		}
		args = append(args, threshold)
	}

	msgs, err := b.DbMap.Select(Message{}, query, args...)
	if err != nil {
		return nil, err
	}

	results := make([]proto.Message, len(msgs))
	for i, row := range msgs {
		msg := row.(*Message)
		results[len(msgs)-i-1] = msg.ToTransmission()
	}

	return results, nil
}
コード例 #14
0
ファイル: room.go プロジェクト: logan/heim
func (rb *RoomBinding) getParentPostTime(id snowflake.Snowflake) (time.Time, error) {
	var row struct {
		Posted time.Time
	}
	err := rb.DbMap.SelectOne(&row,
		"SELECT posted FROM message WHERE room = $1 AND id = $2",
		rb.RoomName, id.String())
	if err != nil {
		return time.Time{}, err
	}
	return row.Posted, nil
}
コード例 #15
0
ファイル: account.go プロジェクト: robot0x/heim
func (m *accountManager) RevokeStaff(ctx scope.Context, accountID snowflake.Snowflake) error {
	m.b.Lock()
	defer m.b.Unlock()

	account, ok := m.b.accounts[accountID.String()]
	if !ok {
		return proto.ErrAccountNotFound
	}
	memAcc := account.(*memAccount)
	memAcc.staffCapability = nil
	return nil
}
コード例 #16
0
ファイル: room.go プロジェクト: logan/heim
func (rb *RoomBinding) IsValidParent(id snowflake.Snowflake) (bool, error) {
	if id.String() == "" {
		return true, nil
	}
	if _, err := rb.getParentPostTime(id); err != nil {
		// check for nonexistant parent
		if err == sql.ErrNoRows {
			return false, nil
		}
		return false, err
	}
	return true, nil
}
コード例 #17
0
ファイル: account.go プロジェクト: NotAMoose/heim
func (b *AccountManagerBinding) get(
	db gorp.SqlExecutor, id snowflake.Snowflake) (*AccountBinding, error) {

	accountCols, err := allColumns(b.DbMap, Account{}, "a")
	if err != nil {
		return nil, err
	}

	capabilityCols, err := allColumns(b.DbMap, Capability{}, "c",
		"ID", "staff_capability_id",
		"nonce", "staff_capability_nonce")
	if err != nil {
		return nil, err
	}

	var row AccountWithStaffCapability
	err = db.SelectOne(
		&row,
		fmt.Sprintf("SELECT %s, %s FROM account a LEFT OUTER JOIN capability c ON a.staff_capability_id = c.id WHERE a.id = $1",
			accountCols, capabilityCols),
		id.String())
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, proto.ErrAccountNotFound
		}
		return nil, err
	}

	ab := row.Bind(b.Backend)

	piCols, err := allColumns(b.DbMap, PersonalIdentity{}, "")
	if err != nil {
		return nil, err
	}
	rows, err := db.Select(PersonalIdentity{}, fmt.Sprintf("SELECT %s FROM personal_identity WHERE account_id = $1", piCols), id.String())
	switch err {
	case sql.ErrNoRows:
	case nil:
		ab.identities = make([]proto.PersonalIdentity, len(rows))
		for i, row := range rows {
			ab.identities[i] = &PersonalIdentityBinding{row.(*PersonalIdentity)}
		}
	default:
		return nil, err
	}

	return ab, nil
}
コード例 #18
0
ファイル: account.go プロジェクト: NotAMoose/heim
func (b *AccountManagerBinding) ChangeName(ctx scope.Context, accountID snowflake.Snowflake, name string) error {
	res, err := b.DbMap.Exec("UPDATE account SET name = $2 WHERE id = $1", accountID.String(), name)
	if err != nil {
		if err == sql.ErrNoRows {
			return proto.ErrAccountNotFound
		}
		return err
	}
	n, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if n < 1 {
		return proto.ErrAccountNotFound
	}
	return nil
}
コード例 #19
0
ファイル: room.go プロジェクト: logan/heim
func (rb *ManagedRoomBinding) IsValidParent(id snowflake.Snowflake) (bool, error) {
	if id.String() == "" || rb.RetentionDays == 0 {
		return true, nil
	}
	posted, err := rb.getParentPostTime(id)
	if err != nil {
		// check for nonexistant parent
		if err == sql.ErrNoRows {
			return false, nil
		}
		return false, err
	}
	threshold := time.Now().Add(time.Duration(-rb.RetentionDays) * 24 * time.Hour)
	if posted.Before(threshold) {
		return false, nil
	}
	return true, nil
}
コード例 #20
0
ファイル: agent.go プロジェクト: robot0x/heim
func (t *agentTracker) SetClientKey(
	ctx scope.Context, agentID string, accessKey *security.ManagedKey,
	accountID snowflake.Snowflake, clientKey *security.ManagedKey) error {

	t.b.Lock()
	defer t.b.Unlock()

	agent, err := t.Get(ctx, agentID)
	if err != nil {
		return err
	}

	if err := agent.SetClientKey(accessKey, clientKey); err != nil {
		return err
	}

	agent.AccountID = accountID.String()
	return nil
}
コード例 #21
0
ファイル: account.go プロジェクト: logan/heim
func (b *AccountManagerBinding) ValidateOTP(ctx scope.Context, kms security.KMS, accountID snowflake.Snowflake, password string) error {
	t, err := b.DbMap.Begin()
	if err != nil {
		return err
	}

	otp, err := b.getOTP(t, kms, accountID)
	if err != nil {
		rollback(ctx, t)
		return err
	}

	if err := otp.Validate(password); err != nil {
		rollback(ctx, t)
		return err
	}

	if otp.Validated {
		rollback(ctx, t)
		return nil
	}

	res, err := t.Exec("UPDATE otp SET validated = true WHERE account_id = $1", accountID.String())
	if err != nil {
		rollback(ctx, t)
		return err
	}
	n, err := res.RowsAffected()
	if err != nil {
		rollback(ctx, t)
		return err
	}
	if n != 1 {
		rollback(ctx, t)
		return fmt.Errorf("failed to mark otp enrollment as validated")
	}

	if err := t.Commit(); err != nil {
		return err
	}

	return nil
}
コード例 #22
0
ファイル: account.go プロジェクト: rmasoni/heim
func (b *AccountManagerBinding) get(
	db gorp.SqlExecutor, id snowflake.Snowflake) (*AccountBinding, error) {

	var row AccountWithStaffCapability
	err := db.SelectOne(
		&row,
		"SELECT a.id, a.nonce, a.mac, a.encrypted_system_key, a.encrypted_user_key,"+
			" a.encrypted_private_key, a.public_key,"+
			" c.id AS staff_capability_id, c.nonce AS staff_capability_nonce,"+
			" c.encrypted_private_data, c.public_data"+
			" FROM account a LEFT OUTER JOIN capability c ON a.staff_capability_id = c.id"+
			" WHERE a.id = $1",
		id.String())
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, proto.ErrAccountNotFound
		}
		return nil, err
	}

	ab := row.Bind(b.Backend)

	rows, err := db.Select(
		PersonalIdentity{},
		"SELECT namespace, id, account_id, verified FROM personal_identity WHERE account_id = $1",
		id.String())
	switch err {
	case sql.ErrNoRows:
	case nil:
		ab.identities = make([]proto.PersonalIdentity, len(rows))
		for i, row := range rows {
			ab.identities[i] = &PersonalIdentityBinding{row.(*PersonalIdentity)}
		}
	default:
		return nil, err
	}

	return ab, nil
}
コード例 #23
0
ファイル: room_security.go プロジェクト: logan/heim
func NewRoomMessageKeyBinding(
	rb *ManagedRoomBinding, keyID snowflake.Snowflake, msgKey *security.ManagedKey,
	nonce []byte) *RoomMessageKeyBinding {

	rmkb := &RoomMessageKeyBinding{
		GrantManager: &proto.GrantManager{
			Capabilities: &RoomMessageCapabilities{
				Room:     rb.Room,
				Executor: rb.Backend.DbMap,
			},
			Managers: NewRoomManagerKeyBinding(rb),
			KeyEncryptingKey: &security.ManagedKey{
				Ciphertext:   rb.Room.EncryptedManagementKey,
				ContextKey:   "room",
				ContextValue: rb.Room.Name,
			},
			SubjectKeyPair: &security.ManagedKeyPair{
				KeyPairType:         security.Curve25519,
				IV:                  rb.Room.IV,
				EncryptedPrivateKey: rb.Room.EncryptedPrivateKey,
				PublicKey:           rb.Room.PublicKey,
			},
			PayloadKey:   msgKey,
			SubjectNonce: nonce,
		},
		MessageKey: MessageKey{
			ID:           keyID.String(),
			EncryptedKey: msgKey.Ciphertext,
			IV:           msgKey.IV,
			Nonce:        nonce,
		},
		RoomMessageKey: RoomMessageKey{
			Room:      rb.Room.Name,
			KeyID:     keyID.String(),
			Activated: time.Now(),
		},
	}
	return rmkb
}
コード例 #24
0
ファイル: agent.go プロジェクト: logan/heim
func (atb *AgentTrackerBinding) SetClientKey(
	ctx scope.Context, agentID string, accessKey *security.ManagedKey,
	accountID snowflake.Snowflake, clientKey *security.ManagedKey) error {

	t, err := atb.Backend.DbMap.Begin()
	if err != nil {
		return err
	}

	rollback := func() {
		if err := t.Rollback(); err != nil {
			logging.Logger(ctx).Printf("rollback error: %s", err)
		}
	}

	agent, err := atb.getFromDB(agentID, atb.Backend.DbMap)
	if err != nil {
		rollback()
		return err
	}

	if err := agent.SetClientKey(accessKey, clientKey); err != nil {
		rollback()
		return err
	}

	err = atb.setClientKeyInDB(
		agentID, accountID.String(), agent.EncryptedClientKey.Ciphertext, t)
	if err != nil {
		rollback()
		return err
	}

	if err := t.Commit(); err != nil {
		return err
	}

	return nil
}
コード例 #25
0
ファイル: account.go プロジェクト: robot0x/heim
func (b *AccountManagerBinding) Get(
	ctx scope.Context, id snowflake.Snowflake) (proto.Account, error) {

	var row AccountWithStaffCapability
	err := b.DbMap.SelectOne(
		&row,
		"SELECT a.id, a.nonce, a.mac, a.encrypted_system_key, a.encrypted_user_key,"+
			" a.encrypted_private_key, a.public_key,"+
			" c.id AS staff_capability_id, c.nonce AS staff_capability_nonce,"+
			" c.encrypted_private_data, c.public_data"+
			" FROM account a LEFT OUTER JOIN capability c ON a.staff_capability_id = c.id"+
			" WHERE a.id = $1",
		id.String())
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, proto.ErrAccountNotFound
		}
		return nil, err
	}

	return row.Bind(b.Backend), nil
}
コード例 #26
0
ファイル: room.go プロジェクト: robot0x/heim
func (rb *RoomBinding) IsValidParent(id snowflake.Snowflake) (bool, error) {
	if id.String() == "" || rb.RetentionDays == 0 {
		return true, nil
	}
	var row struct {
		Posted time.Time
	}
	err := rb.DbMap.SelectOne(&row,
		"SELECT posted FROM message WHERE room = $1 AND id = $2",
		rb.Name, id.String())
	if err != nil {
		// check for nonexistant parent
		if err == sql.ErrNoRows {
			return false, nil
		}
		return false, err
	}
	threshold := time.Now().Add(time.Duration(-rb.RetentionDays) * 24 * time.Hour)
	if row.Posted.Before(threshold) {
		return false, nil
	}
	return true, nil
}
コード例 #27
0
ファイル: emails.go プロジェクト: bramvdbogaerde/heim
func (et *EmailTracker) MarkDelivered(ctx scope.Context, accountID snowflake.Snowflake, id string) error {
	t, err := et.Backend.DbMap.Begin()
	if err != nil {
		return err
	}

	row, err := et.Backend.DbMap.Get(Email{}, id)
	if err != nil {
		rollback(ctx, t)
		if err == sql.ErrNoRows {
			return proto.ErrEmailNotFound
		}
		return err
	}

	email := row.(*Email)
	if email.AccountID != accountID.String() {
		rollback(ctx, t)
		return proto.ErrEmailNotFound
	}

	if email.Delivered.Valid {
		rollback(ctx, t)
		return proto.ErrEmailAlreadyDelivered
	}

	if _, err := t.Exec("UPDATE email SET delivered = NOW() WHERE id = $1", id); err != nil {
		rollback(ctx, t)
		return err
	}

	if err := t.Commit(); err != nil {
		return err
	}

	return nil
}
コード例 #28
0
ファイル: account.go プロジェクト: NotAMoose/heim
func (b *AccountManagerBinding) GrantStaff(
	ctx scope.Context, accountID snowflake.Snowflake, kmsCred security.KMSCredential) error {

	// Look up the target account's (system) encrypted client key. This is
	// not part of the transaction, because we want to interact with KMS
	// before we proceed. That should be fine, since this is an infrequently
	// used action.
	var row struct {
		EncryptedClientKey []byte `db:"encrypted_system_key"`
		Nonce              []byte `db:"nonce"`
	}
	err := b.DbMap.SelectOne(
		&row, "SELECT encrypted_system_key, nonce FROM account WHERE id = $1", accountID.String())
	if err != nil {
		if err == sql.ErrNoRows {
			return proto.ErrAccountNotFound
		}
		return err
	}

	// Use kmsCred to obtain kms and decrypt the client's key.
	kms := kmsCred.KMS()
	clientKey := &security.ManagedKey{
		KeyType:      proto.ClientKeyType,
		Ciphertext:   row.EncryptedClientKey,
		ContextKey:   "nonce",
		ContextValue: base64.URLEncoding.EncodeToString(row.Nonce),
	}
	if err := kms.DecryptKey(clientKey); err != nil {
		return err
	}

	// Grant staff capability. This involves marshalling kmsCred to JSON and
	// encrypting it with the client key.
	nonce, err := kms.GenerateNonce(clientKey.KeyType.BlockSize())
	if err != nil {
		return err
	}

	capability, err := security.GrantSharedSecretCapability(clientKey, nonce, kmsCred.KMSType(), kmsCred)
	if err != nil {
		return err
	}

	// Store capability and update account table.
	t, err := b.DbMap.Begin()
	if err != nil {
		return err
	}

	rollback := func() {
		if err := t.Rollback(); err != nil {
			backend.Logger(ctx).Printf("rollback error: %s", err)
		}
	}

	dbCap := &Capability{
		ID:                   capability.CapabilityID(),
		NonceBytes:           capability.Nonce(),
		EncryptedPrivateData: capability.EncryptedPayload(),
		PublicData:           capability.PublicPayload(),
	}
	if err := t.Insert(dbCap); err != nil {
		rollback()
		return err
	}

	result, err := t.Exec(
		"UPDATE account SET staff_capability_id = $2 WHERE id = $1",
		accountID.String(), capability.CapabilityID())
	if err != nil {
		rollback()
		return err
	}
	n, err := result.RowsAffected()
	if err != nil {
		rollback()
		return err
	}
	if n != 1 {
		rollback()
		return proto.ErrAccountNotFound
	}

	if err := t.Commit(); err != nil {
		return err
	}

	return nil
}
コード例 #29
0
ファイル: account.go プロジェクト: logan/heim
func (b *AccountManagerBinding) ChangeEmail(ctx scope.Context, accountID snowflake.Snowflake, email string) (bool, error) {
	t, err := b.DbMap.Begin()
	if err != nil {
		return false, err
	}

	account, err := b.get(t, accountID)
	if err != nil {
		rollback(ctx, t)
		return false, err
	}

	other, err := b.resolve(t, "email", email)
	if err != nil && err != proto.ErrAccountNotFound {
		rollback(ctx, t)
		return false, err
	}
	if err == nil && other.ID() != accountID {
		rollback(ctx, t)
		return false, proto.ErrPersonalIdentityInUse
	}

	for _, pid := range account.identities {
		if pid.Namespace() == "email" && pid.ID() == email {
			if pid.Verified() {
				res, err := t.Exec("UPDATE account SET email = $2 WHERE id = $1", accountID.String(), email)
				if err != nil {
					if err == sql.ErrNoRows {
						return false, proto.ErrAccountNotFound
					}
					rollback(ctx, t)
					return false, err
				}
				n, err := res.RowsAffected()
				if err != nil {
					rollback(ctx, t)
					return false, err
				}
				if n < 1 {
					rollback(ctx, t)
					return false, proto.ErrAccountNotFound
				}
				if err := t.Commit(); err != nil {
					return false, err
				}
				return true, nil
			}
			rollback(ctx, t)
			return false, nil
		}
	}

	pid := &PersonalIdentity{
		Namespace: "email",
		ID:        email,
		AccountID: accountID.String(),
	}
	if err := t.Insert(pid); err != nil {
		rollback(ctx, t)
		return false, err
	}

	if err := t.Commit(); err != nil {
		return false, err
	}

	return false, nil
}
コード例 #30
0
ファイル: room.go プロジェクト: logan/heim
func (rb *RoomBinding) GetMessage(ctx scope.Context, id snowflake.Snowflake) (*proto.Message, error) {
	var msg Message

	nDays, err := rb.DbMap.SelectInt("SELECT retention_days FROM room WHERE name = $1", rb.RoomName)
	if err != nil {
		return nil, err
	}

	cols, err := allColumns(rb.DbMap, Message{}, "")
	if err != nil {
		return nil, err
	}
	err = rb.DbMap.SelectOne(&msg, fmt.Sprintf("SELECT %s FROM message WHERE room = $1 AND id = $2", cols), rb.RoomName, id.String())
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, proto.ErrMessageNotFound
		}
		return nil, err
	}
	if nDays > 0 {
		threshold := time.Now().Add(time.Duration(-nDays) * 24 * time.Hour)
		if msg.Posted.Before(threshold) {
			return nil, proto.ErrMessageNotFound
		}
	}
	m := msg.ToBackend()
	return &m, nil
}