summaryrefslogtreecommitdiff
path: root/chphoto/chphoto.go
blob: 5ad46eeaaeb17acd29aa7ad083884937bff93fe0 (plain) (blame)
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
 * Copyright (c) 2024 Runxi Yu <https://runxiyu.org>
 * SPDX-License-Identifier: BSD-2-Clause
 *
 */

package main

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
)

func acquireTokenInteractive(app public.Client, username string) (string, error) {
	result, err := app.AcquireTokenInteractive(context.TODO(), []string{"User.ReadWrite"}, public.WithLoginHint(username))
	if err != nil {
		return "", fmt.Errorf("interactive authentication error: %w", err)
	}
	return result.AccessToken, nil
}

func acquireTokenPassword(app public.Client, username, password string) (string, error) {
	result, err := app.AcquireTokenByUsernamePassword(context.TODO(), []string{"User.ReadWrite"}, username, password)
	if err != nil {
		return "", fmt.Errorf("password authentication error: %w", err)
	}
	return result.AccessToken, nil
}

func updateProfilePhoto(token, userID, photoPath string) error {
	graphEndpoint := "https://graph.microsoft.com/v1.0"
	url := fmt.Sprintf("%s/users/%s/photo/$value", graphEndpoint, userID)

	photoData, err := os.ReadFile(photoPath)
	if err != nil {
		return fmt.Errorf("failed reading photo: %w", err)
	}

	var mimetype string
	if strings.HasSuffix(photoPath, ".jpg") || strings.HasSuffix(photoPath, ".jpeg") {
		mimetype = "image/jpeg"
	} else if strings.HasSuffix(photoPath, ".png") {
		mimetype = "image/png"
	}

	req, err := http.NewRequest("PUT", url, bytes.NewReader(photoData))
	if err != nil {
		return fmt.Errorf("failed making request: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", mimetype)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed requesting: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed reading response: %w", err)
	}

	fmt.Println(resp.StatusCode)
	fmt.Printf("%s\n", body)

	return nil
}

func main() {
	var email, photo, passVar string

	flag.StringVar(&email, "email", "", "(required) username@ykpaoschool.cn")
	flag.StringVar(&photo, "photo", "", "(required) path to avatar")
	flag.StringVar(&passVar, "passvar", "", "environment variable containing the password")
	flag.Parse()

	if photo == "" || email == "" {
		flag.Usage()
		return
	}

	app, err := public.New("14f8346d-98c9-4f12-875f-3b2cabe7110a", public.WithAuthority("https://login.microsoftonline.com/organizations"))
	if err != nil {
		log.Fatalf("failed creating msal app: %v", err)
	}

	var token string
	if passVar == "" {
		token, err = acquireTokenInteractive(app, email)
	} else {
		password := os.Getenv(passVar)
		if password == "" {
			log.Fatalf("environment variable %s not found", passVar)
		}
		token, err = acquireTokenPassword(app, email, password)
	}
	if err != nil {
		log.Fatalf("failed to acquire token: %v", err)
	}

	err = updateProfilePhoto(token, email, photo)
	if err != nil {
		log.Fatalf("failed to update profile photo: %v", err)
	}
}