Mercurial > gemma
view pkg/auth/opendb.go @ 900:62a30a78ac2f
morptool right
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Tue, 02 Oct 2018 16:10:50 +0200 |
parents | 7e45aaec7081 |
children | 29c11f4bf9db |
line wrap: on
line source
package auth import ( "context" "database/sql" "errors" "sync" "github.com/jackc/pgx" "github.com/jackc/pgx/stdlib" "gemma.intevation.de/gemma/pkg/config" ) var ErrNoMetamorphUser = errors.New("No metamorphic user configured") 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 } type metamorph struct { sync.Mutex db *sql.DB } var mm metamorph func (m *metamorph) open() (*sql.DB, error) { m.Lock() defer m.Unlock() if m.db != nil { return m.db, nil } user := config.DBUser() if user == "" { return nil, ErrNoMetamorphUser } db, err := OpenDB(user, config.DBPassword()) if err != nil { return nil, err } m.db = db return db, nil } func MetamorphConn(ctx context.Context, user string) (*sql.Conn, error) { db, err := mm.open() if err != nil { return nil, err } conn, err := db.Conn(ctx) if err != nil { return nil, err } if _, err := conn.ExecContext(ctx, `SELECT public.setrole_plan($1)`, user); err != nil { conn.Close() return nil, err } return conn, 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)` 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, ctx context.Context, fn func(*sql.Conn) error) error { conn, err := MetamorphConn(ctx, role) if err != nil { return err } defer conn.Close() return fn(conn) }