Skip to content

Commit 80f2665

Browse files
committed
Refactored code, added new routes for tags and orgs
1 parent 6b1972f commit 80f2665

File tree

8 files changed

+121
-71
lines changed

8 files changed

+121
-71
lines changed

backend/conduit/articles/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ def add_organization(self, articles):
158158
self.org_articles.append(articles)
159159
return True
160160

161+
def remove_organization(self, articles):
162+
self.needsReview = False
163+
self.org_articles.remove(articles)
164+
return True
165+
161166
def add_needReviewTag(self, tag):
162167
self.needReviewTags.append(tag)
163168
return True

backend/conduit/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import os
2+
from dotenv import load_dotenv
3+
from os.path import dirname, join
4+
5+
dotenv_path = join(dirname(__file__), '.env')
6+
load_dotenv(dotenv_path)
7+
8+
GITHUB_CLIENT = os.environ.get('GITHUB_ID')
9+
GITHUB_SECRET = os.environ.get('GITHUB_SECRET')
10+
ACCESS_TOKEN_URL = os.environ.get('ACCESS_TOKEN_URL')
11+
GITHUB_API = os.environ.get('GITHUB_API')
12+
STATE = os.environ.get('STATE')

backend/conduit/organizations/views.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,32 @@ def submit_article_for_review(org_slug, slug):
164164
organization.save()
165165

166166
return article
167-
168167

169168
@blueprint.route('/api/organizations/<org_slug>/articles/<slug>',
170169
methods=('DELETE',))
171170
@jwt_required
172-
def reviewed_article(slug, org_slug, **kwargs):
171+
def remove_article(slug, org_slug, **kwargs):
172+
profile = current_user.profile
173+
organization = Organization.query.filter_by(slug=org_slug).first()
174+
article = Article.query.filter_by(slug=slug).first()
175+
176+
if not organization.moderator(profile):
177+
raise InvalidUsage.not_admin()
178+
179+
if article not in organization.pending_articles:
180+
raise InvalidUsage.article_not_found()
181+
182+
organization.pending_articles.remove(article)
183+
organization.save()
184+
article.remove_organization(organization)
185+
article.save()
186+
187+
return '', 200
188+
189+
@blueprint.route('/api/organizations/<org_slug>/articles/<slug>',
190+
methods=('PUT',))
191+
@jwt_required
192+
def add_article(slug, org_slug, **kwargs):
173193
profile = current_user.profile
174194
organization = Organization.query.filter_by(slug=org_slug).first()
175195
article = Article.query.filter_by(slug=slug).first()
@@ -186,3 +206,5 @@ def reviewed_article(slug, org_slug, **kwargs):
186206
article.save()
187207

188208
return '', 200
209+
210+

backend/conduit/tags/views.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def invite_moderator(slug, username):
134134
@blueprint.route('/api/tags/<slug>/articles/<articleSlug>', methods=('PUT',))
135135
@jwt_required
136136
@marshal_with(article_schema)
137-
def review_article(slug, articleSlug):
137+
def add_article(slug, articleSlug):
138138
profile = current_user.profile
139139
tag = Tags.query.filter_by(slug=slug).first()
140140
if not tag:
@@ -147,6 +147,29 @@ def review_article(slug, articleSlug):
147147
raise InvalidUsage.article_not_found()
148148
if article.needsReview:
149149
article.remove_needReviewTag(tag)
150+
article.add_tag(tag)
151+
if article.is_allTagReviewed():
152+
article.set_needsReview(False)
153+
article.save()
154+
return article
155+
156+
@blueprint.route('/api/tags/<slug>/articles/<articleSlug>', methods=('DELETE',))
157+
@jwt_required
158+
@marshal_with(article_schema)
159+
def remove_article(slug, articleSlug):
160+
profile = current_user.profile
161+
tag = Tags.query.filter_by(slug=slug).first()
162+
if not tag:
163+
raise InvalidUsage.tag_not_found()
164+
if tag not in profile.moderated_tags:
165+
raise InvalidUsage.not_moderator()
166+
167+
article = Article.query.filter_by(slug=articleSlug).first()
168+
if not article:
169+
raise InvalidUsage.article_not_found()
170+
if article.needsReview:
171+
article.remove_needReviewTag(tag)
172+
article.remove_tag(tag)
150173
if article.is_allTagReviewed():
151174
article.set_needsReview(False)
152175
article.save()

backend/conduit/user/views.py

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
from conduit.profile.models import UserProfile
1212
from .models import User
1313
from .serializers import user_schema
14+
from conduit.config import GITHUB_CLIENT, GITHUB_SECRET, ACCESS_TOKEN_URL, GITHUB_API, STATE
1415
import requests
16+
import os
1517

1618
blueprint = Blueprint('user', __name__)
1719

@@ -65,62 +67,38 @@ def update_user(**kwargs):
6567
user.update(**kwargs)
6668
return user
6769

68-
#TODO:
69-
#1) we have to add the state to make sure no third party access when sending code
70-
#2) change this away from username, only allows me to call the thing username cause of user_schema.
71-
#if bit_token invalid and access_tok still valid, just reauthenticate with new code and stuff
72-
#if access_token invalid but bit_token valid, ignore until bit_token gets invalid
73-
74-
#Note: the parameter is username but it should be changed to github_code
75-
#i just get errors thrown if
76-
77-
@blueprint.route('/api/user/callback', methods = ('POST',))
78-
@use_kwargs(user_schema)
70+
@blueprint.route('/api/user/callback/<github_code>/<state>', methods = ('GET',))
7971
@marshal_with(user_schema)
80-
def github_oauth(username, **kwargs):
81-
#refactor and hide these
82-
83-
#NOTE: use try catch block later
84-
payload = { 'client_id': "98574e099fa640413899",
85-
'client_secret': "272ac3010797de4cc29c5c0caf0bbd9df4d79832",
86-
'code': username,
87-
}
88-
header = {
89-
'Accept': 'application/json',
90-
}
91-
92-
auth_response = requests.post('https://github.com/login/oauth/access_token', params=payload, headers=header).json()
93-
94-
#if it's an error response, the access_token will not work (like if code is invalid)
95-
#it won't have access_token key-value pair
96-
#build in try catch!
97-
access_token = auth_response["access_token"]
98-
99-
auth_header = {"Authorization": "Bearer " + access_token}
100-
data_response = requests.get('https://api.github.com/user', headers=auth_header).json()
101-
email_response = requests.get('https://api.github.com/user/emails', headers=auth_header).json()
102-
103-
username = data_response["login"]
104-
email = email_response[0]["email"]
105-
github_id = data_response["id"]
106-
107-
user = User.query.filter_by(email=email).first()
108-
if user is None:
109-
userprofile = UserProfile(User(username, email, github_access_token = access_token).save()).save()
110-
user = userprofile.user
111-
112-
user.token = create_access_token(identity=user, fresh=True)
113-
return user
114-
115-
# Flask Migrate
116-
117-
# write code
118-
# run flaskdb migrate in the code
119-
# flaskdb upgrade in the code
120-
# Code isn't working because staging db uses staging code
121-
# Code isn't working on local because we don't have db
122-
123-
# When doing github auth, we need to use flask db migrate to be able to add our cols
124-
# to our remote db
125-
72+
def github_oauth(github_code, state):
73+
try:
74+
if (state.strip() != STATE):
75+
raise InvalidUsage.user_not_found()
76+
77+
payload = { 'client_id': GITHUB_CLIENT,
78+
'client_secret': GITHUB_SECRET,
79+
'code': github_code,
80+
}
81+
header = {
82+
'Accept': 'application/json',
83+
}
84+
85+
auth_response = requests.post(ACCESS_TOKEN_URL, params=payload, headers=header).json()
86+
access_token = auth_response["access_token"]
87+
88+
auth_header = {"Authorization": "Bearer " + access_token}
89+
data_response = requests.get(GITHUB_API + 'user', headers=auth_header).json()
90+
email_response = requests.get(GITHUB_API + 'user/emails', headers=auth_header).json()
91+
92+
username = data_response["login"]
93+
email = email_response[0]["email"]
94+
github_id = data_response["id"]
95+
96+
user = User.query.filter_by(email=email).first()
97+
if user is None:
98+
userprofile = UserProfile(User(username, email, github_access_token = access_token).save()).save()
99+
user = userprofile.user
126100

101+
user.token = create_access_token(identity=user, fresh=True)
102+
return user
103+
except:
104+
raise InvalidUsage.user_not_found()

components/profile/LoginForm.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Router from "next/router";
2-
import React from "react";
2+
import React, { useEffect } from "react";
33
import { mutate } from "swr";
4+
import { CODE_URL, STATE, SCOPE, GITHUB_CLIENT } from "../../lib/utils/constant";
45

56
import ListErrors from "../common/ListErrors";
67
import UserAPI from "../../lib/api/user";
@@ -20,16 +21,20 @@ const LoginForm = () => {
2021
[]
2122
);
2223

24+
const authorize_url = CODE_URL + "?client_id=" + GITHUB_CLIENT + "&scope=" + SCOPE
25+
+ "&state=" + STATE;
26+
2327
let logging_in;
2428
if (typeof window !== "undefined"){
2529
const code = new URLSearchParams(window.location.search).get("code");
30+
const state = new URLSearchParams(window.location.search).get("state");
2631
if (code){
2732
logging_in = (<p>Redirecting to home page...</p>);
28-
React.useEffect(() => {
33+
useEffect(() => {
2934

3035
async function post_code(){
3136
try{
32-
const {data, status} = await UserAPI.post_code(code);
37+
const {data, status} = await UserAPI.post_code(code, state);
3338
console.log("begun await");
3439
if (data?.user){
3540
console.log(data.user)
@@ -46,7 +51,6 @@ const LoginForm = () => {
4651
}, [])
4752
}
4853
}
49-
5054

5155
const handleSubmit = async (e) => {
5256
e.preventDefault();
@@ -104,11 +108,12 @@ const LoginForm = () => {
104108
Sign in
105109
</button>
106110
</fieldset>
111+
107112
</form>
108-
<a href="https://github.com/login/oauth/authorize?client_id=98574e099fa640413899&scope=user+repo"
109-
className="btn btn-lg btn-primary pull-xs-left"
113+
<a href={authorize_url}
114+
className="btn btn-lg btn-primary pull-xs-right"
110115
>
111-
Sign in through GitHub REAL</a>
116+
Sign in through Github</a>
112117
{logging_in}
113118
</>
114119
);

lib/api/user.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,10 @@ const UserAPI = {
138138
},
139139
get: async (username) => axios.get(`${SERVER_BASE_URL}/profiles/${username}`),
140140

141-
post_code: async (username) => {
141+
post_code: async (github_code, state) => {
142142
try{
143-
const response = await axios.post(
144-
`${SERVER_BASE_URL}/user/callback`,
145-
JSON.stringify({ user: { username } }),
143+
const response = await axios.get(
144+
`${SERVER_BASE_URL}/user/callback/${encodeURIComponent(github_code)}/${encodeURIComponent(state)}`,
146145
{
147146
headers: {
148147
"Content-Type": "application/json",

lib/utils/constant.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ export const SERVER_BASE_URL = `http://127.0.0.1:5000/api`;
1414

1515
export const APP_NAME = `conduit`;
1616

17+
export const CODE_URL = 'https://github.com/login/oauth/authorize';
18+
export const GITHUB_CLIENT = '98574e099fa640413899';
19+
export const SCOPE = 'user+repo';
20+
//must conceal state later
21+
export const STATE = 'd3Asp0fK03M0Ldnwoi2Pnbh9knB2K335Ln';
22+
1723
export const ARTICLE_QUERY_MAP = {
1824
"tab=feed": `${SERVER_BASE_URL}/articles/feed`,
1925
"tab=tag": `${SERVER_BASE_URL}/articles/tag`

0 commit comments

Comments
 (0)