Starting to mess with it, this basically works for Google
This commit is contained in:
parent
95d1abd416
commit
9cef15313e
@ -13,6 +13,7 @@
|
|||||||
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editMailAccount()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.MailAccount }">
|
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editMailAccount()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.MailAccount }">
|
||||||
<i-bs name="plus-circle"></i-bs> <ng-container i18n>Add Account</ng-container>
|
<i-bs name="plus-circle"></i-bs> <ng-container i18n>Add Account</ng-container>
|
||||||
</button>
|
</button>
|
||||||
|
<a class="btn btn-sm btn-outline-primary ms-2" [href]="googleOAuthUrl" target="_blank" i18n>Connect with Google</a>
|
||||||
</h4>
|
</h4>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
|
@ -18,6 +18,9 @@ import { MailAccountEditDialogComponent } from '../../common/edit-dialog/mail-ac
|
|||||||
import { MailRuleEditDialogComponent } from '../../common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component'
|
import { MailRuleEditDialogComponent } from '../../common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component'
|
||||||
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
|
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
|
||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
|
import { ActivatedRoute } from '@angular/router'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-mail',
|
selector: 'pngx-mail',
|
||||||
@ -32,13 +35,20 @@ export class MailComponent
|
|||||||
mailRules: MailRule[] = []
|
mailRules: MailRule[] = []
|
||||||
|
|
||||||
unsubscribeNotifier: Subject<any> = new Subject()
|
unsubscribeNotifier: Subject<any> = new Subject()
|
||||||
|
oAuthAccoundId: number
|
||||||
|
|
||||||
|
public get googleOAuthUrl(): string {
|
||||||
|
return this.settingsService.get(SETTINGS_KEYS.GOOGLE_OAUTH_URL)
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public mailAccountService: MailAccountService,
|
public mailAccountService: MailAccountService,
|
||||||
public mailRuleService: MailRuleService,
|
public mailRuleService: MailRuleService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
public permissionsService: PermissionsService
|
public permissionsService: PermissionsService,
|
||||||
|
private settingsService: SettingsService,
|
||||||
|
private route: ActivatedRoute
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@ -50,6 +60,13 @@ export class MailComponent
|
|||||||
.subscribe({
|
.subscribe({
|
||||||
next: (r) => {
|
next: (r) => {
|
||||||
this.mailAccounts = r.results
|
this.mailAccounts = r.results
|
||||||
|
if (this.oAuthAccoundId) {
|
||||||
|
this.editMailAccount(
|
||||||
|
this.mailAccounts.find(
|
||||||
|
(account) => account.id === this.oAuthAccoundId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
this.toastService.showError(
|
this.toastService.showError(
|
||||||
@ -70,6 +87,19 @@ export class MailComponent
|
|||||||
this.toastService.showError($localize`Error retrieving mail rules`, e)
|
this.toastService.showError($localize`Error retrieving mail rules`, e)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.route.queryParamMap.subscribe((params) => {
|
||||||
|
if (params.get('oauth_success')) {
|
||||||
|
this.oAuthAccoundId = parseInt(params.get('account_id'))
|
||||||
|
if (this.mailAccounts.length > 0) {
|
||||||
|
this.editMailAccount(
|
||||||
|
this.mailAccounts.find(
|
||||||
|
(account) => account.id === this.oAuthAccoundId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
@ -64,6 +64,7 @@ export const SETTINGS_KEYS = {
|
|||||||
SEARCH_DB_ONLY: 'general-settings:search:db-only',
|
SEARCH_DB_ONLY: 'general-settings:search:db-only',
|
||||||
SEARCH_FULL_TYPE: 'general-settings:search:more-link',
|
SEARCH_FULL_TYPE: 'general-settings:search:more-link',
|
||||||
EMPTY_TRASH_DELAY: 'trash_delay',
|
EMPTY_TRASH_DELAY: 'trash_delay',
|
||||||
|
GOOGLE_OAUTH_URL: 'google_oauth_url',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SETTINGS: UiSetting[] = [
|
export const SETTINGS: UiSetting[] = [
|
||||||
@ -242,4 +243,9 @@ export const SETTINGS: UiSetting[] = [
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
default: 30,
|
default: 30,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: SETTINGS_KEYS.GOOGLE_OAUTH_URL,
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
@ -14,6 +14,7 @@ from unicodedata import normalize
|
|||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import httpx
|
||||||
import pathvalidate
|
import pathvalidate
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -1554,6 +1555,16 @@ class UiSettingsView(GenericAPIView):
|
|||||||
permission_classes = (IsAuthenticated, PaperlessObjectPermissions)
|
permission_classes = (IsAuthenticated, PaperlessObjectPermissions)
|
||||||
serializer_class = UiSettingsViewSerializer
|
serializer_class = UiSettingsViewSerializer
|
||||||
|
|
||||||
|
def generate_google_oauth_url(self) -> str:
|
||||||
|
token_request_uri = "https://accounts.google.com/o/oauth2/auth"
|
||||||
|
response_type = "code"
|
||||||
|
client_id = settings.GOOGLE_OAUTH_CLIENT_ID
|
||||||
|
redirect_uri = "http://localhost:8000/api/oauth/google/callback/"
|
||||||
|
scope = "https://mail.google.com/"
|
||||||
|
access_type = "offline"
|
||||||
|
url = f"{token_request_uri}?response_type={response_type}&client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}&access_type={access_type}"
|
||||||
|
return url
|
||||||
|
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
@ -1584,6 +1595,9 @@ class UiSettingsView(GenericAPIView):
|
|||||||
|
|
||||||
ui_settings["auditlog_enabled"] = settings.AUDIT_LOG_ENABLED
|
ui_settings["auditlog_enabled"] = settings.AUDIT_LOG_ENABLED
|
||||||
|
|
||||||
|
if settings.GOOGLE_OAUTH_ENABLED:
|
||||||
|
ui_settings["google_oauth_url"] = self.generate_google_oauth_url()
|
||||||
|
|
||||||
user_resp = {
|
user_resp = {
|
||||||
"id": user.id,
|
"id": user.id,
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
@ -2129,3 +2143,58 @@ class TrashView(ListModelMixin, PassUserMixin):
|
|||||||
doc_ids = [doc.id for doc in docs]
|
doc_ids = [doc.id for doc in docs]
|
||||||
empty_trash(doc_ids=doc_ids)
|
empty_trash(doc_ids=doc_ids)
|
||||||
return Response({"result": "OK", "doc_ids": doc_ids})
|
return Response({"result": "OK", "doc_ids": doc_ids})
|
||||||
|
|
||||||
|
|
||||||
|
# Outlook https://stackoverflow.com/questions/73902642/office-365-imap-authentication-via-oauth2-and-python-msal-library
|
||||||
|
class GoogleOauthCallbackView(GenericAPIView):
|
||||||
|
# permission_classes = (AllowAny,)
|
||||||
|
|
||||||
|
def get(self, request, format=None):
|
||||||
|
# Guide: https://postmansmtp.com/how-to-configure-post-smtp-with-gmailgsuite-using-oauth/
|
||||||
|
# http://localhost:4200/api/oauth/google/callback?code=4%2F0AQlEd8yxIwqjz95p82tWMq4ogn4KxRdprtjjGqjEHW4x7X1roEgswzn9EfiAit1cOLfSog&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&hd=michaelshamoon.com&prompt=consent
|
||||||
|
code = request.query_params.get("code")
|
||||||
|
if code is None:
|
||||||
|
return HttpResponseBadRequest("Code required")
|
||||||
|
|
||||||
|
token_request_uri = "https://accounts.google.com/o/oauth2/token"
|
||||||
|
client_id = settings.GOOGLE_OAUTH_CLIENT_ID
|
||||||
|
client_secret = settings.GOOGLE_OAUTH_CLIENT_SECRET
|
||||||
|
redirect_uri = "http://localhost:8000/api/oauth/google/callback/"
|
||||||
|
grant_type = "authorization_code"
|
||||||
|
scope = "https://mail.google.com/"
|
||||||
|
url = f"{token_request_uri}"
|
||||||
|
data = {
|
||||||
|
"code": code,
|
||||||
|
"client_id": client_id,
|
||||||
|
"client_secret": client_secret,
|
||||||
|
"redirect_uri": redirect_uri,
|
||||||
|
"grant_type": grant_type,
|
||||||
|
"scope": scope,
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
}
|
||||||
|
response = httpx.post(url, data=data, headers=headers)
|
||||||
|
data = response.json()
|
||||||
|
if "error" in data:
|
||||||
|
return HttpResponseBadRequest(data["error"])
|
||||||
|
elif "access_token" in data:
|
||||||
|
access_token = data["access_token"]
|
||||||
|
# if "refresh_token" in data:
|
||||||
|
# refresh_token = data["refresh_token"]
|
||||||
|
# expires_in = data["expires_in"]
|
||||||
|
account, _ = MailAccount.objects.update_or_create(
|
||||||
|
password=access_token,
|
||||||
|
is_token=True,
|
||||||
|
imap_server="imap.gmail.com",
|
||||||
|
defaults={
|
||||||
|
"name": f"Gmail {datetime.now()}",
|
||||||
|
"username": "",
|
||||||
|
"imap_security": MailAccount.ImapSecurity.SSL,
|
||||||
|
"imap_port": 993,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
f"http://localhost:4200/mail?oauth_success=1&account_id={account.pk}",
|
||||||
|
)
|
||||||
|
@ -1195,3 +1195,11 @@ EMAIL_ENABLE_GPG_DECRYPTOR: Final[bool] = __get_boolean(
|
|||||||
# Soft Delete #
|
# Soft Delete #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
EMPTY_TRASH_DELAY = max(__get_int("PAPERLESS_EMPTY_TRASH_DELAY", 30), 1)
|
EMPTY_TRASH_DELAY = max(__get_int("PAPERLESS_EMPTY_TRASH_DELAY", 30), 1)
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Oauth Email Providers #
|
||||||
|
###############################################################################
|
||||||
|
GOOGLE_OAUTH_CLIENT_ID = os.getenv("PAPERLESS_GOOGLE_OAUTH_CLIENT_ID")
|
||||||
|
GOOGLE_OAUTH_CLIENT_SECRET = os.getenv("PAPERLESS_GOOGLE_OAUTH_CLIENT_SECRET")
|
||||||
|
GOOGLE_OAUTH_ENABLED = bool(GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET)
|
||||||
|
@ -22,6 +22,7 @@ from documents.views import CorrespondentViewSet
|
|||||||
from documents.views import CustomFieldViewSet
|
from documents.views import CustomFieldViewSet
|
||||||
from documents.views import DocumentTypeViewSet
|
from documents.views import DocumentTypeViewSet
|
||||||
from documents.views import GlobalSearchView
|
from documents.views import GlobalSearchView
|
||||||
|
from documents.views import GoogleOauthCallbackView
|
||||||
from documents.views import IndexView
|
from documents.views import IndexView
|
||||||
from documents.views import LogViewSet
|
from documents.views import LogViewSet
|
||||||
from documents.views import PostDocumentView
|
from documents.views import PostDocumentView
|
||||||
@ -165,6 +166,11 @@ urlpatterns = [
|
|||||||
TrashView.as_view(),
|
TrashView.as_view(),
|
||||||
name="trash",
|
name="trash",
|
||||||
),
|
),
|
||||||
|
re_path(
|
||||||
|
r"^oauth/google/callback/",
|
||||||
|
GoogleOauthCallbackView.as_view(),
|
||||||
|
name="google_oauth_callback",
|
||||||
|
),
|
||||||
*api_router.urls,
|
*api_router.urls,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user