File: darwin.go

package info (click to toggle)
golang-github-azuread-microsoft-authentication-extensions-for-go 0.0~git20231002.7e3b8e2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 260 kB
  • sloc: makefile: 4
file content (83 lines) | stat: -rw-r--r-- 2,379 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

//go:build darwin && cgo
// +build darwin,cgo

package accessor

import (
	"context"
	"errors"

	"github.com/keybase/go-keychain"
)

type option func(*Storage) error

// WithAccount sets an optional account name for the keychain item holding cached data.
func WithAccount(name string) option {
	return func(s *Storage) error {
		s.account = name
		return nil
	}
}

// Storage stores data as a password on the macOS keychain. The keychain must be unlocked before Storage can read
// or write data. macOS may not allow keychain access from a headless environment such as an SSH session.
type Storage struct {
	account, service string
}

// New is the constructor for Storage. "servName" is the service name for the keychain item holding cached data.
func New(servName string, opts ...option) (*Storage, error) {
	if servName == "" {
		return nil, errors.New("servName can't be empty")
	}
	s := Storage{service: servName}
	for _, o := range opts {
		if err := o(&s); err != nil {
			return nil, err
		}
	}
	return &s, nil
}

// Delete deletes the stored data, if any exists.
func (s *Storage) Delete(context.Context) error {
	err := keychain.DeleteGenericPasswordItem(s.service, s.account)
	if errors.Is(err, keychain.ErrorItemNotFound) || errors.Is(err, keychain.ErrorNoSuchKeychain) {
		return nil
	}
	return err
}

// Read returns data stored on the keychain or, if the keychain item doesn't exist, a nil slice and nil error.
func (s *Storage) Read(context.Context) ([]byte, error) {
	data, err := keychain.GetGenericPassword(s.service, s.account, "", "")
	if err != nil {
		return nil, err
	}
	return data, nil
}

// Write stores data on the keychain.
func (s *Storage) Write(_ context.Context, data []byte) error {
	pw, err := keychain.GetGenericPassword(s.service, s.account, "", "")
	if err != nil {
		return err
	}
	item := keychain.NewGenericPassword(s.service, s.account, "", nil, "")
	if pw == nil {
		// password not found: add it to the keychain
		item.SetData(data)
		err = keychain.AddItem(item)
	} else {
		// password found: update its value
		update := keychain.NewGenericPassword(s.service, s.account, "", data, "")
		err = keychain.UpdateItem(item, update)
	}
	return err
}

var _ Accessor = (*Storage)(nil)