Skip to content

Commit 53b08b5

Browse files
Prompt Safari users to install HTTPS certificates and check if they are outdated when the Agent is started
1 parent bc7154c commit 53b08b5

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

certificates/certificates.go

+35
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"math/big"
3131
"net"
3232
"os"
33+
"os/exec"
3334
"strings"
3435
"time"
3536

@@ -279,3 +280,37 @@ func isExpired() (bool, error) {
279280
date, _ := time.Parse(time.DateTime, strings.ReplaceAll(dateS, " +0000", ""))
280281
return date.Before(bound), nil
281282
}
283+
284+
// PromptInstallCertsSafari prompts the user to install the HTTPS certificates if they are using Safari
285+
func PromptInstallCertsSafari() bool {
286+
if GetDefaultBrowserName() != "Safari" {
287+
return false
288+
}
289+
oscmd := exec.Command("osascript", "-e", "display dialog \"The Arduino Agent needs a local HTTPS certificate to work correctly with Safari.\nIf you use Safari, you need to install it.\" buttons {\"Do not install\", \"Install the certificate for Safari\"} default button 1 with title \"Install Certificates\"")
290+
pressed, _ := oscmd.Output()
291+
return string(pressed) == "button returned:Install the certificate for Safari"
292+
}
293+
294+
// PromptExpiredCerts prompts the user to update the HTTPS certificates if they are using Safari
295+
func PromptExpiredCerts(certDir *paths.Path) {
296+
if expired, err := isExpired(); err != nil {
297+
log.Errorf("cannot check if certificates are expired something went wrong: %s", err)
298+
} else if expired {
299+
oscmd := exec.Command("osascript", "-e", "display dialog \"The Arduino Agent needs a local HTTPS certificate to work correctly with Safari.\nYour certificate is expired or close to expiration. Do you want to update it?\" buttons {\"Do not update\", \"Update the certificate for Safari\"} default button 1 with title \"Update Certificates\"")
300+
if pressed, _ := oscmd.Output(); string(pressed) == "button returned:Update the certificate for Safari" {
301+
err := UninstallCertificates()
302+
if err != nil {
303+
log.Errorf("cannot uninstall certificates something went wrong: %s", err)
304+
} else {
305+
DeleteCertificates(certDir)
306+
GenerateCertificates(certDir)
307+
err := InstallCertificate(certDir.Join("ca.cert.cer"))
308+
// if something goes wrong during the cert install we remove them, so the user is able to retry
309+
if err != nil {
310+
log.Errorf("cannot install certificates something went wrong: %s", err)
311+
DeleteCertificates(certDir)
312+
}
313+
}
314+
}
315+
}
316+
}

main.go

+53
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ var (
8686
verbose = iniConf.Bool("v", true, "show debug logging")
8787
crashreport = iniConf.Bool("crashreport", false, "enable crashreport logging")
8888
autostartMacOS = iniConf.Bool("autostartMacOS", true, "the Arduino Create Agent is able to start automatically after login on macOS (launchd agent)")
89+
installCerts = iniConf.Bool("installCerts", false, "")
8990
)
9091

9192
// the ports filter provided by the user via the -regex flag, if any
@@ -220,6 +221,27 @@ func loop() {
220221
configPath = config.GenerateConfig(configDir)
221222
}
222223

224+
// if the default browser is Safari, prompt the user to install HTTPS certificates
225+
// and eventually install them
226+
if runtime.GOOS == "darwin" {
227+
if value, err := valueIni(configPath.String()); err != nil {
228+
log.Panicf("config.ini cannot be parsed: %s", err)
229+
} else if !value && cert.PromptInstallCertsSafari() {
230+
err = modifyIni(configPath.String())
231+
if err != nil {
232+
log.Panicf("config.ini cannot be parsed: %s", err)
233+
}
234+
certDir := config.GetCertificatesDir()
235+
cert.GenerateCertificates(certDir)
236+
err := cert.InstallCertificate(certDir.Join("ca.cert.cer"))
237+
// if something goes wrong during the cert install we remove them, so the user is able to retry
238+
if err != nil {
239+
log.Errorf("cannot install certificates something went wrong: %s", err)
240+
cert.DeleteCertificates(certDir)
241+
}
242+
}
243+
}
244+
223245
// Parse the config.ini
224246
args, err := parseIni(configPath.String())
225247
if err != nil {
@@ -342,6 +364,13 @@ func loop() {
342364
}
343365
}
344366

367+
// check if the HTTPS certificates are expired and prompt the user to update them on macOS
368+
if runtime.GOOS == "darwin" {
369+
if *installCerts && config.CertsExist() {
370+
cert.PromptExpiredCerts(config.GetCertificatesDir())
371+
}
372+
}
373+
345374
// launch the discoveries for the running system
346375
go serialPorts.Run()
347376
// launch the hub routine which is the singleton for the websocket server
@@ -487,3 +516,27 @@ func parseIni(filename string) (args []string, err error) {
487516

488517
return args, nil
489518
}
519+
520+
func modifyIni(filename string) error {
521+
cfg, err := ini.LoadSources(ini.LoadOptions{IgnoreInlineComment: false, AllowPythonMultilineValues: true}, filename)
522+
if err != nil {
523+
return err
524+
}
525+
_, err = cfg.Section("").NewKey("installCerts", "true")
526+
if err != nil {
527+
return err
528+
}
529+
err = cfg.SaveTo(filename)
530+
if err != nil {
531+
return err
532+
}
533+
return nil
534+
}
535+
536+
func valueIni(filename string) (bool, error) {
537+
cfg, err := ini.LoadSources(ini.LoadOptions{IgnoreInlineComment: false, AllowPythonMultilineValues: true}, filename)
538+
if err != nil {
539+
return false, err
540+
}
541+
return cfg.Section("").HasKey("installCerts"), nil
542+
}

0 commit comments

Comments
 (0)