view pkg/auth/opendb.go @ 453:a7dc68d8e22f

Only let users in which are listed in users.list_users.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 22 Aug 2018 11:05:59 +0200
parents 62c909dd3098
children 685b886002b8
line wrap: on
line source

package auth

import (
	"database/sql"
	"errors"
	"strings"

	"github.com/jackc/pgx"
	"github.com/jackc/pgx/stdlib"

	"gemma.intevation.de/gemma/pkg/config"
)

func OpenDB(user, password string) (*sql.DB, error) {

	// To ease SSL config ride a bit on parsing.
	cc, err := pgx.ParseConnectionString("sslmode=" + config.DBSSLMode())
	if err != nil {
		return nil, err
	}

	// Do the rest manually to allow whitespace in user/password.
	cc.Host = config.DBHost()
	cc.Port = uint16(config.DBPort())
	cc.User = user
	cc.Password = password
	cc.Database = config.DBName()

	return stdlib.OpenDB(cc), nil
}

const allRoles = `
WITH RECURSIVE cte AS (
   SELECT oid FROM pg_roles WHERE rolname = current_user
   UNION ALL
   SELECT m.roleid
   FROM   cte
   JOIN   pg_auth_members m ON m.member = cte.oid
)
SELECT rolname FROM pg_roles
WHERE oid IN (SELECT oid FROM cte) AND rolname <> current_user
AND EXISTS (SELECT 1 FROM users.list_users WHERE username = current_user)`

const InvalidRoleCharacters = `\"':;`

var ErrInvalidRoleCharacters = errors.New("rolename contains invalid character")

func AllOtherRoles(user, password string) (Roles, error) {
	db, err := OpenDB(user, password)
	if err != nil {
		return nil, err
	}
	defer db.Close()
	rows, err := db.Query(allRoles)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	roles := Roles{} // explicit empty by intention.

	for rows.Next() {
		var role string
		if err := rows.Scan(&role); err != nil {
			return nil, err
		}
		roles = append(roles, role)
	}
	return roles, rows.Err()
}

func RunAs(role string, fn func(*sql.DB) error) error {
	if strings.Contains(role, InvalidRoleCharacters) {
		return ErrInvalidRoleCharacters
	}
	db, err := OpenDB(config.MetamorphDBUser(), config.MetamorhpDBPassword())
	if err != nil {
		return nil
	}
	defer db.Close()
	if _, err := db.Exec(`SET ROLE "` + role + `"`); err != nil {
		return err
	}
	return fn(db)
}