-
Notifications
You must be signed in to change notification settings - Fork 74
/
Copy pathgh-auth.el
169 lines (137 loc) · 5.96 KB
/
gh-auth.el
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
;;; gh-auth.el --- authentication for gh.el -*- lexical-binding: t; -*-
;; Copyright (C) 2011 Yann Hodique
;; Author: Yann Hodique <[email protected]>
;; Keywords:
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;
;;; Code:
(require 'eieio)
(require 'gh-profile)
(require 'gh-common)
(require 'gh-url)
(defgroup gh-auth nil
"Github authentication."
:group 'gh)
(defvar gh-auth-alist nil)
(defun gh-auth-remember (profile key value)
(let ((cell (assoc profile gh-auth-alist)))
(when (not cell)
(setq cell (cons profile nil))
(setq gh-auth-alist (append gh-auth-alist (list cell))))
(setcdr cell (plist-put (cdr cell) key value))))
(defun gh-auth-get-username ()
(let* ((profile (gh-profile-current-profile))
(user (or (plist-get (cdr (assoc profile gh-auth-alist)) :username)
(plist-get (cdr (assoc profile gh-profile-alist)) :username)
(gh-config "user"))))
(when (not user)
(setq user (read-string "GitHub username: "))
(gh-set-config "user" user))
(gh-auth-remember profile :username user)
user))
(defun gh-auth-get-password (&optional remember)
(let* ((profile (gh-profile-current-profile))
(pass (or (plist-get (cdr (assoc profile gh-auth-alist)) :password)
(plist-get (cdr (assoc profile gh-profile-alist)) :password)
(gh-config "password"))))
(when (not pass)
(setq pass (read-passwd "GitHub password: "))
(when remember
(gh-set-config "password" pass)))
(when remember
(gh-auth-remember profile :password pass))
pass))
(declare-function 'gh-oauth-auth-new "gh-oauth")
(defun gh-auth-get-oauth-token ()
(let* ((profile (gh-profile-current-profile))
(token (or (plist-get (cdr (assoc profile gh-auth-alist)) :token)
(plist-get (cdr (assoc profile gh-profile-alist)) :token)
(gh-config "oauth-token"))))
(when (not token)
(let* ((api (make-instance 'gh-oauth-api))
(tok (and (fboundp 'gh-oauth-auth-new)
(oref (oref (funcall 'gh-oauth-auth-new api
'(user repo gist)) :data)
:token))))
(setq token (or tok (read-string "GitHub OAuth token: ")))
(gh-set-config "oauth-token" token)))
(when (functionp token) (setq token (funcall token)))
(gh-auth-remember profile :token token)
token))
(defclass gh-authenticator ()
((username :initarg :username :initform nil))
"Abstract authenticator")
(cl-defmethod initialize-instance ((auth gh-authenticator) &rest args)
(cl-call-next-method)
(or (oref auth :username)
(oset auth :username (gh-auth-get-username))))
(cl-defmethod gh-auth-modify-request ((auth gh-authenticator) req)
req)
(defclass gh-auth-2fa-callback (gh-url-callback)
((req :initarg :req :initform nil))
"2-factor callback")
(cl-defmethod gh-url-callback-run ((cb gh-auth-2fa-callback) resp)
(when (equal (oref resp :http-status) 401)
(let* ((otp-header "X-GitHub-OTP")
(h (assoc otp-header (oref resp :headers))))
(when (and h (string-prefix-p "required;" (cdr h)))
(let ((otp (read-from-minibuffer "Enter dual-factor auth code: "))
(req (oref cb :req)))
;; reset resp
(oset resp :data nil)
(oset resp :data-received nil)
(object-add-to-list req :headers
(cons otp-header otp))
(gh-url-run-request req resp))))))
(defclass gh-password-authenticator (gh-authenticator)
((password :initarg :password :protection :private :initform nil)
(remember :allocation :class :initform t)
(2fa-cls :initform gh-auth-2fa-callback :allocation :class))
"Password-based authenticator")
(cl-defmethod initialize-instance ((auth gh-password-authenticator) &rest args)
(cl-call-next-method)
(or (oref auth :password)
(oset auth :password (gh-auth-get-password (oref auth remember)))))
(cl-defmethod gh-auth-modify-request ((auth gh-password-authenticator) req)
(object-add-to-list req :headers
(cons "Authorization"
(concat "Basic "
(base64-encode-string
(format "%s:%s" (oref auth :username)
(encode-coding-string
(oref auth :password) 'utf-8))))))
(object-add-to-list req :install-callbacks
(make-instance (oref auth 2fa-cls) :req req))
req)
(defclass gh-oauth-authenticator (gh-authenticator)
((token :initarg :token :protection :private :initform nil))
"Oauth-based authenticator")
(cl-defmethod initialize-instance ((auth gh-oauth-authenticator) &rest args)
(cl-call-next-method)
(or (oref auth :token)
(oset auth :token (gh-auth-get-oauth-token))))
(cl-defmethod gh-auth-modify-request ((auth gh-oauth-authenticator) req)
(object-add-to-list req :headers
(cons "Authorization"
(encode-coding-string
(format "token %s" (oref auth :token)) 'utf-8)))
req)
(provide 'gh-auth)
;; to avoid circular dependencies...
(require 'gh-oauth)
;;; gh-auth.el ends here
;; Local Variables:
;; indent-tabs-mode: nil
;; End: