Files
probo/pkg/accessreview/drivers/csv.go
Sacha Al Himdani 9ac71f948f Update contact email to hello@probo.com
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-09 16:45:23 +02:00

118 lines
3.2 KiB
Go

// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package drivers
import (
"context"
"encoding/csv"
"fmt"
"io"
"strings"
"go.probo.inc/probo/pkg/coredata"
)
// CSVDriver supports both identity and access use cases from uploaded CSV
// files. No external connector is needed.
//
// Expected CSV columns (header required): email, full_name, role, job_title,
// is_admin, active, external_id
type CSVDriver struct {
reader io.Reader
}
func NewCSVDriver(reader io.Reader) *CSVDriver {
return &CSVDriver{reader: reader}
}
func (d *CSVDriver) ListAccounts(_ context.Context) ([]AccountRecord, error) {
r := csv.NewReader(d.reader)
r.FieldsPerRecord = -1
// Read header
header, err := r.Read()
if err != nil {
return nil, fmt.Errorf("cannot read CSV header: %w", err)
}
colIndex := make(map[string]int)
for i, col := range header {
colIndex[strings.TrimSpace(strings.ToLower(col))] = i
}
if _, ok := colIndex["email"]; !ok {
return nil, fmt.Errorf("cannot parse CSV: missing required column email")
}
var records []AccountRecord
for {
row, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("cannot read CSV row: %w", err)
}
record := AccountRecord{
MFAStatus: coredata.MFAStatusUnknown,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
AccountType: coredata.AccessEntryAccountTypeUser,
}
if idx, ok := colIndex["email"]; ok && idx < len(row) {
record.Email = strings.TrimSpace(row[idx])
}
if idx, ok := colIndex["full_name"]; ok && idx < len(row) {
record.FullName = strings.TrimSpace(row[idx])
}
if idx, ok := colIndex["role"]; ok && idx < len(row) {
record.Role = strings.TrimSpace(row[idx])
}
if idx, ok := colIndex["job_title"]; ok && idx < len(row) {
record.JobTitle = strings.TrimSpace(row[idx])
}
if idx, ok := colIndex["is_admin"]; ok && idx < len(row) {
record.IsAdmin = strings.TrimSpace(strings.ToLower(row[idx])) == "true"
}
if idx, ok := colIndex["active"]; ok && idx < len(row) {
record.Active = new(strings.TrimSpace(strings.ToLower(row[idx])) == "true")
}
if idx, ok := colIndex["external_id"]; ok && idx < len(row) {
record.ExternalID = strings.TrimSpace(row[idx])
}
if idx, ok := colIndex["account_type"]; ok && idx < len(row) {
if strings.TrimSpace(strings.ToUpper(row[idx])) == "SERVICE_ACCOUNT" {
record.AccountType = coredata.AccessEntryAccountTypeServiceAccount
}
}
if record.Email != "" {
records = append(records, record)
}
}
return records, nil
}