app.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2014-2018 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. Farhaan Bukhsh <farhaan.bukhsh@gmail.com>
  7. """
  8. from __future__ import unicode_literals
  9. import datetime
  10. import logging
  11. from math import ceil
  12. import flask
  13. from sqlalchemy.exc import SQLAlchemyError
  14. import pagure.exceptions
  15. import pagure.lib.git
  16. import pagure.lib.query
  17. import pagure.forms
  18. import pagure.ui.filters
  19. from pagure.config import config as pagure_config
  20. from pagure.flask_app import _get_user, admin_session_timedout
  21. from pagure.ui import UI_NS
  22. from pagure.utils import (
  23. authenticated,
  24. is_safe_url,
  25. login_required,
  26. get_task_redirect_url,
  27. is_true,
  28. )
  29. _log = logging.getLogger(__name__)
  30. def _filter_acls(repos, acl, user):
  31. """ Filter the given list of repositories to return only the ones where
  32. the user has the specified acl.
  33. """
  34. if acl.lower() == "main admin":
  35. repos = [repo for repo in repos if user.username == repo.user.username]
  36. elif acl.lower() == "ticket" or "commit" or "admin":
  37. repos = [
  38. repo for repo in repos if user in repo.contributors[acl.lower()]
  39. ]
  40. return repos
  41. @UI_NS.route("/browse/projects", endpoint="browse_projects")
  42. @UI_NS.route("/browse/projects/", endpoint="browse_projects")
  43. @UI_NS.route("/")
  44. def index():
  45. """ Front page of the application.
  46. """
  47. if (
  48. authenticated()
  49. and flask.request.path == "/"
  50. and not flask.session.get("_requires_fpca", False)
  51. ):
  52. flask.request.from_index = True
  53. return flask.redirect(flask.url_for("ui_ns.userdash_projects"))
  54. sorting = flask.request.args.get("sorting") or None
  55. page = flask.request.args.get("page", 1)
  56. try:
  57. page = int(page)
  58. if page < 1:
  59. page = 1
  60. except ValueError:
  61. page = 1
  62. limit = pagure_config["ITEM_PER_PAGE"]
  63. start = limit * (page - 1)
  64. private = None
  65. if authenticated():
  66. private = flask.g.fas_user.username
  67. repos = pagure.lib.query.search_projects(
  68. flask.g.session,
  69. fork=False,
  70. start=start,
  71. limit=limit,
  72. sort=sorting,
  73. private=private,
  74. )
  75. num_repos = pagure.lib.query.search_projects(
  76. flask.g.session, fork=False, private=private, count=True
  77. )
  78. total_page = int(ceil(num_repos / float(limit)) if num_repos > 0 else 1)
  79. return flask.render_template(
  80. "index.html",
  81. select="projects",
  82. namespace=None,
  83. repos=repos,
  84. repos_length=num_repos,
  85. total_page=total_page,
  86. page=page,
  87. sorting=sorting,
  88. )
  89. def get_userdash_common(user):
  90. userdash_counts = {}
  91. userdash_counts["repos_length"] = pagure.lib.query.list_users_projects(
  92. flask.g.session,
  93. username=flask.g.fas_user.username,
  94. exclude_groups=None,
  95. fork=False,
  96. private=flask.g.fas_user.username,
  97. count=True,
  98. )
  99. userdash_counts["forks_length"] = pagure.lib.query.search_projects(
  100. flask.g.session,
  101. username=flask.g.fas_user.username,
  102. fork=True,
  103. private=flask.g.fas_user.username,
  104. count=True,
  105. )
  106. userdash_counts["watchlist_length"] = len(
  107. pagure.lib.query.user_watch_list(
  108. flask.g.session,
  109. user=flask.g.fas_user.username,
  110. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  111. )
  112. )
  113. userdash_counts["groups_length"] = len(user.groups)
  114. search_data = pagure.lib.query.list_users_projects(
  115. flask.g.session,
  116. username=flask.g.fas_user.username,
  117. private=flask.g.fas_user.username,
  118. )
  119. return userdash_counts, search_data
  120. @UI_NS.route("/dashboard/projects/")
  121. @UI_NS.route("/dashboard/projects")
  122. @login_required
  123. def userdash_projects():
  124. """ User Dashboard page listing projects for the user
  125. """
  126. user = _get_user(username=flask.g.fas_user.username)
  127. userdash_counts, search_data = get_userdash_common(user)
  128. groups = []
  129. for group in user.groups:
  130. groups.append(
  131. pagure.lib.query.search_groups(
  132. flask.g.session, group_name=group, group_type="user"
  133. )
  134. )
  135. acl = flask.request.args.get("acl", "").strip().lower() or None
  136. search_pattern = flask.request.args.get("search_pattern", None)
  137. if search_pattern == "":
  138. search_pattern = None
  139. limit = pagure_config["ITEM_PER_PAGE"]
  140. repopage = flask.request.args.get("repopage", 1)
  141. try:
  142. repopage = int(repopage)
  143. if repopage < 1:
  144. repopage = 1
  145. except ValueError:
  146. repopage = 1
  147. pattern = "*" + search_pattern + "*" if search_pattern else search_pattern
  148. start = limit * (repopage - 1)
  149. repos = pagure.lib.query.list_users_projects(
  150. flask.g.session,
  151. username=flask.g.fas_user.username,
  152. exclude_groups=None,
  153. fork=False,
  154. pattern=pattern,
  155. private=flask.g.fas_user.username,
  156. start=start,
  157. limit=limit,
  158. acls=[acl] if acl else None,
  159. )
  160. filtered_repos_count = pagure.lib.query.list_users_projects(
  161. flask.g.session,
  162. username=flask.g.fas_user.username,
  163. exclude_groups=None,
  164. fork=False,
  165. pattern=pattern,
  166. private=flask.g.fas_user.username,
  167. count=True,
  168. acls=[acl] if acl else None,
  169. )
  170. repo_list = []
  171. for repo in repos:
  172. access = ""
  173. if repo.user.user == user.username:
  174. access = "main admin"
  175. else:
  176. for repoaccess in repo.contributors:
  177. for repouser in repo.contributors[repoaccess]:
  178. if repouser.username == user.username:
  179. access = repoaccess
  180. grouplist = []
  181. for group in groups:
  182. if repo in group.projects:
  183. thegroup = {"group_name": "", "access": ""}
  184. thegroup["group_name"] = group.group_name
  185. for a in repo.contributor_groups:
  186. for gr in repo.contributor_groups[a]:
  187. if group.group_name == gr.group_name:
  188. thegroup["access"] = a
  189. grouplist.append(thegroup)
  190. repo_list.append(
  191. {"repo": repo, "grouplist": grouplist, "access": access}
  192. )
  193. total_repo_page = int(
  194. ceil(filtered_repos_count / float(limit))
  195. if filtered_repos_count > 0
  196. else 1
  197. )
  198. return flask.render_template(
  199. "userdash_projects.html",
  200. username=flask.g.fas_user.username,
  201. user=user,
  202. select="projects",
  203. repo_list=repo_list,
  204. repopage=repopage,
  205. total_repo_page=total_repo_page,
  206. userdash_counts=userdash_counts,
  207. search_data=search_data,
  208. acl=acl,
  209. filtered_repos_count=filtered_repos_count,
  210. search_pattern=search_pattern,
  211. )
  212. @UI_NS.route("/dashboard/activity/")
  213. @UI_NS.route("/dashboard/activity")
  214. @login_required
  215. def userdash_activity():
  216. """ User Dashboard page listing user activity
  217. """
  218. user = _get_user(username=flask.g.fas_user.username)
  219. userdash_counts, search_data = get_userdash_common(user)
  220. messages = pagure.lib.query.get_watchlist_messages(
  221. flask.g.session, user, limit=20
  222. )
  223. return flask.render_template(
  224. "userdash_activity.html",
  225. username=flask.g.fas_user.username,
  226. user=user,
  227. select="activity",
  228. messages=messages,
  229. userdash_counts=userdash_counts,
  230. search_data=search_data,
  231. )
  232. @UI_NS.route("/dashboard/groups/")
  233. @UI_NS.route("/dashboard/groups")
  234. @login_required
  235. def userdash_groups():
  236. """ User Dashboard page listing a user's groups
  237. """
  238. user = _get_user(username=flask.g.fas_user.username)
  239. userdash_counts, search_data = get_userdash_common(user)
  240. groups = []
  241. for group in user.groups:
  242. groups.append(
  243. pagure.lib.query.search_groups(
  244. flask.g.session, group_name=group, group_type="user"
  245. )
  246. )
  247. return flask.render_template(
  248. "userdash_groups.html",
  249. username=flask.g.fas_user.username,
  250. user=user,
  251. select="groups",
  252. groups=groups,
  253. userdash_counts=userdash_counts,
  254. search_data=search_data,
  255. )
  256. @UI_NS.route("/dashboard/forks/")
  257. @UI_NS.route("/dashboard/forks")
  258. @login_required
  259. def userdash_forks():
  260. """ Forks tab of the user dashboard
  261. """
  262. user = _get_user(username=flask.g.fas_user.username)
  263. userdash_counts, search_data = get_userdash_common(user)
  264. limit = pagure_config["ITEM_PER_PAGE"]
  265. # FORKS
  266. forkpage = flask.request.args.get("forkpage", 1)
  267. try:
  268. forkpage = int(forkpage)
  269. if forkpage < 1:
  270. forkpage = 1
  271. except ValueError:
  272. forkpage = 1
  273. start = limit * (forkpage - 1)
  274. forks = pagure.lib.query.search_projects(
  275. flask.g.session,
  276. username=flask.g.fas_user.username,
  277. fork=True,
  278. private=flask.g.fas_user.username,
  279. start=start,
  280. limit=limit,
  281. )
  282. total_fork_page = int(
  283. ceil(userdash_counts["forks_length"] / float(limit))
  284. if userdash_counts["forks_length"] > 0
  285. else 1
  286. )
  287. return flask.render_template(
  288. "userdash_forks.html",
  289. username=flask.g.fas_user.username,
  290. user=user,
  291. select="forks",
  292. forks=forks,
  293. forkpage=forkpage,
  294. total_fork_page=total_fork_page,
  295. userdash_counts=userdash_counts,
  296. search_data=search_data,
  297. )
  298. @UI_NS.route("/dashboard/watchlist/")
  299. @UI_NS.route("/dashboard/watchlist")
  300. @login_required
  301. def userdash_watchlist():
  302. """ User Dashboard page for a user's watchlist
  303. """
  304. watch_list = pagure.lib.query.user_watch_list(
  305. flask.g.session,
  306. user=flask.g.fas_user.username,
  307. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  308. )
  309. user = _get_user(username=flask.g.fas_user.username)
  310. userdash_counts, search_data = get_userdash_common(user)
  311. return flask.render_template(
  312. "userdash_watchlist.html",
  313. username=flask.g.fas_user.username,
  314. user=user,
  315. select="watchlist",
  316. watch_list=watch_list,
  317. userdash_counts=userdash_counts,
  318. search_data=search_data,
  319. )
  320. def index_auth():
  321. """ Front page for authenticated user.
  322. """
  323. user = _get_user(username=flask.g.fas_user.username)
  324. acl = flask.request.args.get("acl", "").strip().lower() or None
  325. repopage = flask.request.args.get("repopage", 1)
  326. try:
  327. repopage = int(repopage)
  328. if repopage < 1:
  329. repopage = 1
  330. except ValueError:
  331. repopage = 1
  332. limit = pagure_config["ITEM_PER_PAGE"]
  333. # PROJECTS
  334. start = limit * (repopage - 1)
  335. repos = pagure.lib.query.search_projects(
  336. flask.g.session,
  337. username=flask.g.fas_user.username,
  338. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  339. fork=False,
  340. private=flask.g.fas_user.username,
  341. start=start,
  342. limit=limit,
  343. )
  344. if repos and acl:
  345. repos = _filter_acls(repos, acl, user)
  346. repos_length = pagure.lib.query.search_projects(
  347. flask.g.session,
  348. username=flask.g.fas_user.username,
  349. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  350. fork=False,
  351. private=flask.g.fas_user.username,
  352. count=True,
  353. )
  354. total_repo_page = int(
  355. ceil(repos_length / float(limit)) if repos_length > 0 else 1
  356. )
  357. # FORKS
  358. forkpage = flask.request.args.get("forkpage", 1)
  359. try:
  360. forkpage = int(forkpage)
  361. if forkpage < 1:
  362. forkpage = 1
  363. except ValueError:
  364. forkpage = 1
  365. start = limit * (forkpage - 1)
  366. forks = pagure.lib.query.search_projects(
  367. flask.g.session,
  368. username=flask.g.fas_user.username,
  369. fork=True,
  370. private=flask.g.fas_user.username,
  371. start=start,
  372. limit=limit,
  373. )
  374. forks_length = pagure.lib.query.search_projects(
  375. flask.g.session,
  376. username=flask.g.fas_user.username,
  377. fork=True,
  378. private=flask.g.fas_user.username,
  379. start=start,
  380. limit=limit,
  381. count=True,
  382. )
  383. total_fork_page = int(
  384. ceil(forks_length / float(limit)) if forks_length > 0 else 1
  385. )
  386. watch_list = pagure.lib.query.user_watch_list(
  387. flask.g.session,
  388. user=flask.g.fas_user.username,
  389. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  390. )
  391. return flask.render_template(
  392. "userdash_projects.html",
  393. username=flask.g.fas_user.username,
  394. user=user,
  395. forks=forks,
  396. repos=repos,
  397. watch_list=watch_list,
  398. repopage=repopage,
  399. repos_length=repos_length,
  400. total_repo_page=total_repo_page,
  401. forkpage=forkpage,
  402. forks_length=forks_length,
  403. total_fork_page=total_fork_page,
  404. )
  405. @UI_NS.route("/search/")
  406. @UI_NS.route("/search")
  407. def search():
  408. """ Search this pagure instance for projects or users.
  409. """
  410. stype = flask.request.args.get("type", "projects")
  411. term = flask.request.args.get("term")
  412. page = flask.request.args.get("page", 1)
  413. direct = is_true(flask.request.values.get("direct", False))
  414. try:
  415. page = int(page)
  416. if page < 1:
  417. page = 1
  418. except ValueError:
  419. page = 1
  420. if direct:
  421. return flask.redirect(flask.url_for("ui_ns.view_repo", repo="") + term)
  422. if stype == "projects":
  423. return flask.redirect(
  424. flask.url_for("ui_ns.view_projects", pattern=term)
  425. )
  426. elif stype == "projects_forks":
  427. return flask.redirect(
  428. flask.url_for("view_projects", pattern=term, forks=True)
  429. )
  430. elif stype == "groups":
  431. return flask.redirect(flask.url_for("ui_ns.view_group", group=term))
  432. else:
  433. return flask.redirect(flask.url_for("ui_ns.view_users", username=term))
  434. @UI_NS.route("/users/")
  435. @UI_NS.route("/users")
  436. @UI_NS.route("/users/<username>")
  437. def view_users(username=None):
  438. """ Present the list of users.
  439. """
  440. page = flask.request.args.get("page", 1)
  441. try:
  442. page = int(page)
  443. if page < 1:
  444. page = 1
  445. except ValueError:
  446. page = 1
  447. users = pagure.lib.query.search_user(flask.g.session, pattern=username)
  448. private = False
  449. # Condition to check non-authorized user should't be able to access private
  450. # project of other users
  451. if authenticated() and username == flask.g.fas_user.username:
  452. private = flask.g.fas_user.username
  453. limit = pagure_config["ITEM_PER_PAGE"]
  454. start = limit * (page - 1)
  455. end = limit * page
  456. users_length = len(users)
  457. users = users[start:end]
  458. total_page = int(ceil(users_length / float(limit)))
  459. for user in users:
  460. repos_length = pagure.lib.query.search_projects(
  461. flask.g.session,
  462. username=user.user,
  463. fork=False,
  464. count=True,
  465. private=private,
  466. )
  467. forks_length = pagure.lib.query.search_projects(
  468. flask.g.session,
  469. username=user.user,
  470. fork=True,
  471. count=True,
  472. private=private,
  473. )
  474. user.repos_length = repos_length
  475. user.forks_length = forks_length
  476. return flask.render_template(
  477. "user_list.html",
  478. users=users,
  479. users_length=users_length,
  480. total_page=total_page,
  481. page=page,
  482. select="users",
  483. )
  484. @UI_NS.route("/projects/")
  485. @UI_NS.route("/projects")
  486. @UI_NS.route("/projects/<pattern>")
  487. @UI_NS.route("/projects/<namespace>/<pattern>")
  488. def view_projects(pattern=None, namespace=None):
  489. """ Present the list of projects.
  490. """
  491. forks = flask.request.args.get("forks")
  492. page = flask.request.args.get("page", 1)
  493. try:
  494. page = int(page)
  495. if page < 1:
  496. page = 1
  497. except ValueError:
  498. page = 1
  499. select = "projects"
  500. # If forks is specified, we want both forks and projects
  501. if is_true(forks):
  502. forks = None
  503. select = "projects_forks"
  504. else:
  505. forks = False
  506. private = False
  507. if authenticated():
  508. private = flask.g.fas_user.username
  509. limit = pagure_config["ITEM_PER_PAGE"]
  510. start = limit * (page - 1)
  511. projects = pagure.lib.query.search_projects(
  512. flask.g.session,
  513. pattern=pattern,
  514. namespace=namespace,
  515. fork=forks,
  516. start=start,
  517. limit=limit,
  518. private=private,
  519. )
  520. if len(projects) == 1:
  521. flask.flash("Only one result found, redirecting you to it")
  522. return flask.redirect(
  523. flask.url_for(
  524. "ui_ns.view_repo",
  525. repo=projects[0].name,
  526. namespace=projects[0].namespace,
  527. username=projects[0].user.username
  528. if projects[0].is_fork
  529. else None,
  530. )
  531. )
  532. projects_length = pagure.lib.query.search_projects(
  533. flask.g.session,
  534. pattern=pattern,
  535. namespace=namespace,
  536. fork=forks,
  537. count=True,
  538. private=private,
  539. )
  540. total_page = int(ceil(projects_length / float(limit)))
  541. if namespace in pagure_config["ALLOWED_PREFIX"]:
  542. namespace = None
  543. return flask.render_template(
  544. "index.html",
  545. namespace=namespace,
  546. repos=projects,
  547. repos_length=projects_length,
  548. total_page=total_page,
  549. page=page,
  550. select=select,
  551. )
  552. def get_userprofile_common(user):
  553. userprofile_counts = {}
  554. userprofile_counts["repos_length"] = pagure.lib.query.search_projects(
  555. flask.g.session,
  556. username=user.username,
  557. fork=False,
  558. exclude_groups=None,
  559. private=False,
  560. count=True,
  561. )
  562. userprofile_counts["forks_length"] = pagure.lib.query.search_projects(
  563. flask.g.session,
  564. username=user.username,
  565. fork=True,
  566. private=False,
  567. count=True,
  568. )
  569. return userprofile_counts
  570. @UI_NS.route("/user/<username>/")
  571. @UI_NS.route("/user/<username>")
  572. def view_user(username):
  573. """ Front page of a specific user.
  574. """
  575. user = _get_user(username=username)
  576. # public profile, so never show private repos,
  577. # even if the user is viewing themself
  578. private = False
  579. owned_repos = pagure.lib.query.list_users_projects(
  580. flask.g.session,
  581. username=username,
  582. exclude_groups=None,
  583. fork=False,
  584. private=private,
  585. limit=6,
  586. acls=["main admin"],
  587. )
  588. userprofile_common = get_userprofile_common(user)
  589. return flask.render_template(
  590. "userprofile_overview.html",
  591. username=username,
  592. user=user,
  593. owned_repos=owned_repos,
  594. repos_length=userprofile_common["repos_length"],
  595. forks_length=userprofile_common["forks_length"],
  596. select="overview",
  597. )
  598. @UI_NS.route("/user/<username>/projects/")
  599. @UI_NS.route("/user/<username>/projects")
  600. def userprofile_projects(username):
  601. """ Public Profile view of a user's projects.
  602. """
  603. user = _get_user(username=username)
  604. repopage = flask.request.args.get("repopage", 1)
  605. try:
  606. repopage = int(repopage)
  607. if repopage < 1:
  608. repopage = 1
  609. except ValueError:
  610. repopage = 1
  611. limit = pagure_config["ITEM_PER_PAGE"]
  612. repo_start = limit * (repopage - 1)
  613. repos = pagure.lib.query.search_projects(
  614. flask.g.session,
  615. username=username,
  616. fork=False,
  617. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  618. start=repo_start,
  619. limit=limit,
  620. private=False,
  621. )
  622. userprofile_common = get_userprofile_common(user)
  623. total_page_repos = int(
  624. ceil(userprofile_common["repos_length"] / float(limit))
  625. )
  626. return flask.render_template(
  627. "userprofile_projects.html",
  628. username=username,
  629. user=user,
  630. repos=repos,
  631. total_page_repos=total_page_repos,
  632. repopage=repopage,
  633. repos_length=userprofile_common["repos_length"],
  634. forks_length=userprofile_common["forks_length"],
  635. select="projects",
  636. )
  637. @UI_NS.route("/user/<username>/forks/")
  638. @UI_NS.route("/user/<username>/forks")
  639. def userprofile_forks(username):
  640. """ Public Profile view of a user's forks.
  641. """
  642. user = _get_user(username=username)
  643. forkpage = flask.request.args.get("forkpage", 1)
  644. try:
  645. forkpage = int(forkpage)
  646. if forkpage < 1:
  647. forkpage = 1
  648. except ValueError:
  649. forkpage = 1
  650. limit = pagure_config["ITEM_PER_PAGE"]
  651. fork_start = limit * (forkpage - 1)
  652. forks = pagure.lib.query.search_projects(
  653. flask.g.session,
  654. username=username,
  655. fork=True,
  656. start=fork_start,
  657. limit=limit,
  658. private=False,
  659. )
  660. userprofile_common = get_userprofile_common(user)
  661. total_page_forks = int(
  662. ceil(userprofile_common["forks_length"] / float(limit))
  663. )
  664. return flask.render_template(
  665. "userprofile_forks.html",
  666. username=username,
  667. user=user,
  668. forks=forks,
  669. total_page_forks=total_page_forks,
  670. forkpage=forkpage,
  671. repos_length=userprofile_common["repos_length"],
  672. forks_length=userprofile_common["forks_length"],
  673. select="forks",
  674. )
  675. # original view_user()
  676. @UI_NS.route("/user2/<username>/")
  677. @UI_NS.route("/user2/<username>")
  678. def view_user2(username):
  679. """ Front page of a specific user.
  680. """
  681. user = _get_user(username=username)
  682. acl = flask.request.args.get("acl", "").strip().lower() or None
  683. repopage = flask.request.args.get("repopage", 1)
  684. try:
  685. repopage = int(repopage)
  686. if repopage < 1:
  687. repopage = 1
  688. except ValueError:
  689. repopage = 1
  690. forkpage = flask.request.args.get("forkpage", 1)
  691. try:
  692. forkpage = int(forkpage)
  693. if forkpage < 1:
  694. forkpage = 1
  695. except ValueError:
  696. forkpage = 1
  697. limit = pagure_config["ITEM_PER_PAGE"]
  698. repo_start = limit * (repopage - 1)
  699. fork_start = limit * (forkpage - 1)
  700. private = False
  701. if authenticated() and username == flask.g.fas_user.username:
  702. private = flask.g.fas_user.username
  703. repos = pagure.lib.query.search_projects(
  704. flask.g.session,
  705. username=username,
  706. fork=False,
  707. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  708. start=repo_start,
  709. limit=limit,
  710. private=private,
  711. )
  712. if repos and acl:
  713. repos = _filter_acls(repos, acl, user)
  714. repos_length = pagure.lib.query.search_projects(
  715. flask.g.session,
  716. username=username,
  717. fork=False,
  718. exclude_groups=pagure_config.get("EXCLUDE_GROUP_INDEX"),
  719. private=private,
  720. count=True,
  721. )
  722. forks = pagure.lib.query.search_projects(
  723. flask.g.session,
  724. username=username,
  725. fork=True,
  726. start=fork_start,
  727. limit=limit,
  728. private=private,
  729. )
  730. forks_length = pagure.lib.query.search_projects(
  731. flask.g.session,
  732. username=username,
  733. fork=True,
  734. private=private,
  735. count=True,
  736. )
  737. total_page_repos = int(ceil(repos_length / float(limit)))
  738. total_page_forks = int(ceil(forks_length / float(limit)))
  739. return flask.render_template(
  740. "userprofile_overview.html",
  741. username=username,
  742. user=user,
  743. repos=repos,
  744. total_page_repos=total_page_repos,
  745. forks=forks,
  746. total_page_forks=total_page_forks,
  747. repopage=repopage,
  748. forkpage=forkpage,
  749. repos_length=repos_length,
  750. forks_length=forks_length,
  751. )
  752. @UI_NS.route("/user/<username>/requests/")
  753. @UI_NS.route("/user/<username>/requests")
  754. def view_user_requests(username):
  755. """ Shows the pull-requests for the specified user.
  756. """
  757. user = _get_user(username=username)
  758. requests = pagure.lib.query.get_pull_request_of_user(
  759. flask.g.session, username=username
  760. )
  761. userprofile_common = get_userprofile_common(user)
  762. return flask.render_template(
  763. "userprofile_pullrequests.html",
  764. username=username,
  765. user=user,
  766. requests=requests,
  767. select="requests",
  768. repos_length=userprofile_common["repos_length"],
  769. forks_length=userprofile_common["forks_length"],
  770. )
  771. @UI_NS.route("/user/<username>/issues/")
  772. @UI_NS.route("/user/<username>/issues")
  773. def view_user_issues(username):
  774. """
  775. Shows the issues created or assigned to the specified user.
  776. :param username: The username to retrieve the issues for
  777. :type username: str
  778. """
  779. if not pagure_config.get("ENABLE_TICKETS", True):
  780. flask.abort(404, "Tickets have been disabled on this pagure instance")
  781. user = _get_user(username=username)
  782. userprofile_common = get_userprofile_common(user)
  783. return flask.render_template(
  784. "userprofile_issues.html",
  785. username=username,
  786. user=user,
  787. repos_length=userprofile_common["repos_length"],
  788. forks_length=userprofile_common["forks_length"],
  789. select="issues",
  790. )
  791. @UI_NS.route("/user/<username>/stars/")
  792. @UI_NS.route("/user/<username>/stars")
  793. def userprofile_starred(username):
  794. """
  795. Shows the starred projects of the specified user.
  796. :arg username: The username whose stars we have to retrieve
  797. """
  798. user = _get_user(username=username)
  799. userprofile_common = get_userprofile_common(user)
  800. return flask.render_template(
  801. "userprofile_starred.html",
  802. username=username,
  803. user=user,
  804. repos=[star.project for star in user.stars],
  805. repos_length=userprofile_common["repos_length"],
  806. forks_length=userprofile_common["forks_length"],
  807. select="starred",
  808. )
  809. @UI_NS.route("/user/<username>/groups/")
  810. @UI_NS.route("/user/<username>/groups")
  811. def userprofile_groups(username):
  812. """
  813. Shows the groups of a user
  814. """
  815. user = _get_user(username=username)
  816. userprofile_common = get_userprofile_common(user)
  817. groups = []
  818. for groupname in user.groups:
  819. groups.append(
  820. pagure.lib.query.search_groups(
  821. flask.g.session, group_name=groupname
  822. )
  823. )
  824. return flask.render_template(
  825. "userprofile_groups.html",
  826. username=username,
  827. user=user,
  828. groups=groups,
  829. repos_length=userprofile_common["repos_length"],
  830. forks_length=userprofile_common["forks_length"],
  831. select="groups",
  832. )
  833. @UI_NS.route("/new/", methods=("GET", "POST"))
  834. @UI_NS.route("/new", methods=("GET", "POST"))
  835. @login_required
  836. def new_project():
  837. """ Form to create a new project.
  838. """
  839. user = pagure.lib.query.search_user(
  840. flask.g.session, username=flask.g.fas_user.username
  841. )
  842. if not pagure_config.get(
  843. "ENABLE_NEW_PROJECTS", True
  844. ) or not pagure_config.get("ENABLE_UI_NEW_PROJECTS", True):
  845. flask.abort(
  846. 404,
  847. "Creation of new project is not allowed on this \
  848. pagure instance",
  849. )
  850. namespaces = pagure_config["ALLOWED_PREFIX"][:]
  851. if user:
  852. namespaces.extend([grp for grp in user.groups])
  853. if pagure_config.get("USER_NAMESPACE", False):
  854. namespaces.insert(0, flask.g.fas_user.username)
  855. form = pagure.forms.ProjectForm(namespaces=namespaces)
  856. if form.validate_on_submit():
  857. name = form.name.data
  858. description = form.description.data
  859. url = form.url.data
  860. avatar_email = form.avatar_email.data
  861. create_readme = form.create_readme.data
  862. private = False
  863. if pagure_config.get("PRIVATE_PROJECTS", False):
  864. private = form.private.data
  865. namespace = form.namespace.data
  866. if namespace:
  867. namespace = namespace.strip()
  868. if form.repospanner_region:
  869. repospanner_region = form.repospanner_region.data
  870. else:
  871. repospanner_region = None
  872. if form.ignore_existing_repos:
  873. ignore_existing_repos = form.ignore_existing_repos.data
  874. else:
  875. ignore_existing_repos = False
  876. mirrored_from = form.mirrored_from.data
  877. try:
  878. task = pagure.lib.query.new_project(
  879. flask.g.session,
  880. name=name,
  881. private=private,
  882. description=description,
  883. namespace=namespace,
  884. repospanner_region=repospanner_region,
  885. url=url,
  886. avatar_email=avatar_email,
  887. user=flask.g.fas_user.username,
  888. blacklist=pagure_config["BLACKLISTED_PROJECTS"],
  889. allowed_prefix=pagure_config["ALLOWED_PREFIX"],
  890. add_readme=create_readme,
  891. mirrored_from=mirrored_from,
  892. userobj=user,
  893. prevent_40_chars=pagure_config.get(
  894. "OLD_VIEW_COMMIT_ENABLED", False
  895. ),
  896. user_ns=pagure_config.get("USER_NAMESPACE", False),
  897. ignore_existing_repo=ignore_existing_repos,
  898. )
  899. flask.g.session.commit()
  900. return pagure.utils.wait_for_task(task)
  901. except pagure.exceptions.PagureException as err:
  902. flask.flash(str(err), "error")
  903. except SQLAlchemyError as err: # pragma: no cover
  904. flask.g.session.rollback()
  905. flask.flash(str(err), "error")
  906. return flask.render_template("new_project.html", form=form)
  907. @UI_NS.route("/wait/<taskid>")
  908. def wait_task(taskid):
  909. """ Shows a wait page until the task finishes. """
  910. task = pagure.lib.tasks.get_result(taskid)
  911. is_js = is_true(flask.request.args.get("js"))
  912. prev = flask.request.args.get("prev")
  913. if not is_safe_url(prev):
  914. prev = flask.url_for("index")
  915. count = flask.request.args.get("count", 0)
  916. try:
  917. count = int(count)
  918. if count < 1:
  919. count = 0
  920. except ValueError:
  921. count = 0
  922. if task.ready():
  923. if is_js:
  924. flask.abort(417)
  925. return flask.redirect(get_task_redirect_url(task, prev))
  926. else:
  927. if is_js:
  928. return flask.jsonify({"count": count + 1, "status": task.status})
  929. return flask.render_template(
  930. "waiting.html", task=task, count=count, prev=prev
  931. )
  932. @UI_NS.route("/settings/")
  933. @UI_NS.route("/settings")
  934. @login_required
  935. def user_settings():
  936. """ Update the user settings.
  937. """
  938. if admin_session_timedout():
  939. return flask.redirect(
  940. flask.url_for("auth_login", next=flask.request.url)
  941. )
  942. user = _get_user(username=flask.g.fas_user.username)
  943. form = pagure.forms.ConfirmationForm()
  944. return flask.render_template("user_settings.html", user=user, form=form)
  945. @UI_NS.route("/settings/usersettings", methods=["POST"])
  946. @login_required
  947. def update_user_settings():
  948. """ Update the user's settings set in the settings page.
  949. """
  950. if admin_session_timedout():
  951. if flask.request.method == "POST":
  952. flask.flash("Action canceled, try it again", "error")
  953. return flask.redirect(
  954. flask.url_for("auth_login", next=flask.request.url)
  955. )
  956. user = _get_user(username=flask.g.fas_user.username)
  957. form = pagure.forms.ConfirmationForm()
  958. if form.validate_on_submit():
  959. settings = {}
  960. for key in flask.request.form:
  961. if key == "csrf_token":
  962. continue
  963. settings[key] = flask.request.form[key]
  964. try:
  965. message = pagure.lib.query.update_user_settings(
  966. flask.g.session, settings=settings, user=user.username
  967. )
  968. flask.g.session.commit()
  969. flask.flash(message)
  970. except pagure.exceptions.PagureException as msg:
  971. flask.g.session.rollback()
  972. flask.flash(msg, "error")
  973. except SQLAlchemyError as err: # pragma: no cover
  974. flask.g.session.rollback()
  975. flask.flash(str(err), "error")
  976. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  977. @UI_NS.route("/settings/usersettings/addkey", methods=["POST"])
  978. @login_required
  979. def add_user_sshkey():
  980. """ Add the specified SSH key to the user.
  981. """
  982. if admin_session_timedout():
  983. if flask.request.method == "POST":
  984. flask.flash("Action canceled, try it again", "error")
  985. return flask.redirect(
  986. flask.url_for("auth_login", next=flask.request.url)
  987. )
  988. form = pagure.forms.AddSSHKeyForm()
  989. if form.validate_on_submit():
  990. user = _get_user(username=flask.g.fas_user.username)
  991. try:
  992. msg = pagure.lib.query.add_sshkey_to_project_or_user(
  993. flask.g.session,
  994. ssh_key=form.ssh_key.data,
  995. pushaccess=True,
  996. creator=user,
  997. user=user,
  998. )
  999. flask.g.session.commit()
  1000. pagure.lib.query.create_user_ssh_keys_on_disk(
  1001. user, pagure_config.get("GITOLITE_KEYDIR", None)
  1002. )
  1003. pagure.lib.tasks.gitolite_post_compile_only.delay()
  1004. flask.flash(msg)
  1005. return flask.redirect(
  1006. flask.url_for("ui_ns.user_settings") + "#nav-ssh-tab"
  1007. )
  1008. except pagure.exceptions.PagureException as msg:
  1009. flask.g.session.rollback()
  1010. _log.debug(msg)
  1011. flask.flash(str(msg), "error")
  1012. except SQLAlchemyError as err: # pragma: no cover
  1013. flask.g.session.rollback()
  1014. _log.exception(err)
  1015. flask.flash("SSH key could not be added", "error")
  1016. return flask.redirect(
  1017. flask.url_for("ui_ns.user_settings") + "#nav-ssh-tab"
  1018. )
  1019. @UI_NS.route("/settings/usersettings/removekey/<int:keyid>", methods=["POST"])
  1020. @login_required
  1021. def remove_user_sshkey(keyid):
  1022. """ Removes an SSH key from the user.
  1023. """
  1024. if admin_session_timedout():
  1025. if flask.request.method == "POST":
  1026. flask.flash("Action canceled, try it again", "error")
  1027. return flask.redirect(
  1028. flask.url_for("auth_login", next=flask.request.url)
  1029. )
  1030. form = pagure.forms.ConfirmationForm()
  1031. if form.validate_on_submit():
  1032. user = _get_user(username=flask.g.fas_user.username)
  1033. found = False
  1034. for key in user.sshkeys:
  1035. if key.id == keyid:
  1036. flask.g.session.delete(key)
  1037. found = True
  1038. break
  1039. if not found:
  1040. flask.flash("SSH key does not exist in user.", "error")
  1041. return flask.redirect(
  1042. flask.url_for("ui_ns.user_settings") + "#nav-ssh-tab"
  1043. )
  1044. try:
  1045. flask.g.session.commit()
  1046. pagure.lib.query.create_user_ssh_keys_on_disk(
  1047. user, pagure_config.get("GITOLITE_KEYDIR", None)
  1048. )
  1049. pagure.lib.tasks.gitolite_post_compile_only.delay()
  1050. flask.flash("SSH key removed")
  1051. except SQLAlchemyError as err: # pragma: no cover
  1052. flask.g.session.rollback()
  1053. _log.exception(err)
  1054. flask.flash("SSH key could not be removed", "error")
  1055. return flask.redirect(
  1056. flask.url_for("ui_ns.user_settings") + "#nav-ssh-tab"
  1057. )
  1058. @UI_NS.route("/markdown/", methods=["POST"])
  1059. def markdown_preview():
  1060. """ Return the provided markdown text in html.
  1061. The text has to be provided via the parameter 'content' of a POST query.
  1062. """
  1063. form = pagure.forms.ConfirmationForm()
  1064. if form.validate_on_submit():
  1065. return pagure.ui.filters.markdown_filter(flask.request.form["content"])
  1066. else:
  1067. flask.abort(400, "Invalid request")
  1068. @UI_NS.route("/settings/email/drop", methods=["POST"])
  1069. @login_required
  1070. def remove_user_email():
  1071. """ Remove the specified email from the logged in user.
  1072. """
  1073. if admin_session_timedout():
  1074. return flask.redirect(
  1075. flask.url_for("auth_login", next=flask.request.url)
  1076. )
  1077. user = _get_user(username=flask.g.fas_user.username)
  1078. if len(user.emails) == 1:
  1079. flask.flash("You must always have at least one email", "error")
  1080. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1081. form = pagure.forms.UserEmailForm()
  1082. if form.validate_on_submit():
  1083. email = form.email.data
  1084. useremails = [mail.email for mail in user.emails]
  1085. if email not in useremails:
  1086. flask.flash(
  1087. "You do not have the email: %s, nothing to remove" % email,
  1088. "error",
  1089. )
  1090. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1091. for mail in user.emails:
  1092. if mail.email == email:
  1093. user.emails.remove(mail)
  1094. break
  1095. try:
  1096. flask.g.session.commit()
  1097. flask.flash("Email removed")
  1098. except SQLAlchemyError as err: # pragma: no cover
  1099. flask.g.session.rollback()
  1100. _log.exception(err)
  1101. flask.flash("Email could not be removed", "error")
  1102. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1103. @UI_NS.route("/settings/email/add/", methods=["GET", "POST"])
  1104. @UI_NS.route("/settings/email/add", methods=["GET", "POST"])
  1105. @login_required
  1106. def add_user_email():
  1107. """ Add a new email for the logged in user.
  1108. """
  1109. if admin_session_timedout():
  1110. return flask.redirect(
  1111. flask.url_for("auth_login", next=flask.request.url)
  1112. )
  1113. user = _get_user(username=flask.g.fas_user.username)
  1114. form = pagure.forms.UserEmailForm(
  1115. emails=[mail.email for mail in user.emails]
  1116. )
  1117. if form.validate_on_submit():
  1118. email = form.email.data
  1119. try:
  1120. pagure.lib.query.add_user_pending_email(
  1121. flask.g.session, user, email
  1122. )
  1123. flask.g.session.commit()
  1124. flask.flash("Email pending validation")
  1125. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1126. except pagure.exceptions.PagureException as err:
  1127. flask.flash(str(err), "error")
  1128. except SQLAlchemyError as err: # pragma: no cover
  1129. flask.g.session.rollback()
  1130. _log.exception(err)
  1131. flask.flash("Email could not be added", "error")
  1132. return flask.render_template("user_emails.html", user=user, form=form)
  1133. @UI_NS.route("/settings/email/default", methods=["POST"])
  1134. @login_required
  1135. def set_default_email():
  1136. """ Set the default email address of the user.
  1137. """
  1138. if admin_session_timedout():
  1139. return flask.redirect(
  1140. flask.url_for("auth_login", next=flask.request.url)
  1141. )
  1142. user = _get_user(username=flask.g.fas_user.username)
  1143. form = pagure.forms.UserEmailForm()
  1144. if form.validate_on_submit():
  1145. email = form.email.data
  1146. useremails = [mail.email for mail in user.emails]
  1147. if email not in useremails:
  1148. flask.flash(
  1149. "You do not have the email: %s, nothing to set" % email,
  1150. "error",
  1151. )
  1152. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1153. user.default_email = email
  1154. try:
  1155. flask.g.session.commit()
  1156. flask.flash("Default email set to: %s" % email)
  1157. except SQLAlchemyError as err: # pragma: no cover
  1158. flask.g.session.rollback()
  1159. _log.exception(err)
  1160. flask.flash("Default email could not be set", "error")
  1161. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1162. @UI_NS.route("/settings/email/resend", methods=["POST"])
  1163. @login_required
  1164. def reconfirm_email():
  1165. """ Re-send the email address of the user.
  1166. """
  1167. if admin_session_timedout():
  1168. return flask.redirect(
  1169. flask.url_for("auth_login", next=flask.request.url)
  1170. )
  1171. user = _get_user(username=flask.g.fas_user.username)
  1172. form = pagure.forms.UserEmailForm()
  1173. if form.validate_on_submit():
  1174. email = form.email.data
  1175. try:
  1176. pagure.lib.query.resend_pending_email(flask.g.session, user, email)
  1177. flask.g.session.commit()
  1178. flask.flash("Confirmation email re-sent")
  1179. except pagure.exceptions.PagureException as err:
  1180. flask.flash(str(err), "error")
  1181. except SQLAlchemyError as err: # pragma: no cover
  1182. flask.g.session.rollback()
  1183. _log.exception(err)
  1184. flask.flash("Confirmation email could not be re-sent", "error")
  1185. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1186. @UI_NS.route("/settings/email/confirm/<token>/")
  1187. @UI_NS.route("/settings/email/confirm/<token>")
  1188. def confirm_email(token):
  1189. """ Confirm a new email.
  1190. """
  1191. if admin_session_timedout():
  1192. return flask.redirect(
  1193. flask.url_for("auth_login", next=flask.request.url)
  1194. )
  1195. email = pagure.lib.query.search_pending_email(flask.g.session, token=token)
  1196. if not email:
  1197. flask.flash("No email associated with this token.", "error")
  1198. else:
  1199. try:
  1200. pagure.lib.query.add_email_to_user(
  1201. flask.g.session, email.user, email.email
  1202. )
  1203. flask.g.session.delete(email)
  1204. flask.g.session.commit()
  1205. flask.flash("Email validated")
  1206. except pagure.exceptions.PagureException as err:
  1207. flask.flash(str(err), "error")
  1208. _log.exception(err)
  1209. except SQLAlchemyError as err: # pragma: no cover
  1210. flask.g.session.rollback()
  1211. flask.flash(
  1212. "Could not set the account as active in the db, "
  1213. "please report this error to an admin",
  1214. "error",
  1215. )
  1216. _log.exception(err)
  1217. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1218. @UI_NS.route("/ssh_info/")
  1219. @UI_NS.route("/ssh_info")
  1220. def ssh_hostkey():
  1221. """ Endpoint returning information about the SSH hostkey and fingerprint
  1222. of the current pagure instance.
  1223. """
  1224. return flask.render_template("doc_ssh_keys.html")
  1225. @UI_NS.route("/settings/token/new/", methods=("GET", "POST"))
  1226. @UI_NS.route("/settings/token/new", methods=("GET", "POST"))
  1227. @login_required
  1228. def add_api_user_token():
  1229. """ Create an user token (not project specific).
  1230. """
  1231. if admin_session_timedout():
  1232. if flask.request.method == "POST":
  1233. flask.flash("Action canceled, try it again", "error")
  1234. return flask.redirect(
  1235. flask.url_for("auth_login", next=flask.request.url)
  1236. )
  1237. # Ensure the user is in the DB at least
  1238. user = _get_user(username=flask.g.fas_user.username)
  1239. acls = pagure.lib.query.get_acls(
  1240. flask.g.session, restrict=pagure_config.get("CROSS_PROJECT_ACLS")
  1241. )
  1242. form = pagure.forms.NewTokenForm(acls=acls)
  1243. if form.validate_on_submit():
  1244. try:
  1245. msg = pagure.lib.query.add_token_to_user(
  1246. flask.g.session,
  1247. project=None,
  1248. description=form.description.data.strip() or None,
  1249. acls=form.acls.data,
  1250. username=user.username,
  1251. )
  1252. flask.g.session.commit()
  1253. flask.flash(msg)
  1254. return flask.redirect(
  1255. flask.url_for("ui_ns.user_settings") + "#nav-api-tab"
  1256. )
  1257. except SQLAlchemyError as err: # pragma: no cover
  1258. flask.g.session.rollback()
  1259. _log.exception(err)
  1260. flask.flash("API key could not be added", "error")
  1261. # When form is displayed after an empty submission, show an error.
  1262. if form.errors.get("acls"):
  1263. flask.flash("You must select at least one permission.", "error")
  1264. return flask.render_template(
  1265. "add_token.html", select="settings", form=form, acls=acls
  1266. )
  1267. @UI_NS.route("/settings/token/revoke/<token_id>/", methods=["POST"])
  1268. @UI_NS.route("/settings/token/revoke/<token_id>", methods=["POST"])
  1269. @login_required
  1270. def revoke_api_user_token(token_id):
  1271. """ Revoke a user token (ie: not project specific).
  1272. """
  1273. if admin_session_timedout():
  1274. flask.flash("Action canceled, try it again", "error")
  1275. url = flask.url_for(".user_settings")
  1276. return flask.redirect(flask.url_for("auth_login", next=url))
  1277. token = pagure.lib.query.get_api_token(flask.g.session, token_id)
  1278. if not token or token.user.username != flask.g.fas_user.username:
  1279. flask.abort(404, "Token not found")
  1280. form = pagure.forms.ConfirmationForm()
  1281. if form.validate_on_submit():
  1282. try:
  1283. if token.expiration >= datetime.datetime.utcnow():
  1284. token.expiration = datetime.datetime.utcnow()
  1285. flask.g.session.add(token)
  1286. flask.g.session.commit()
  1287. flask.flash("Token revoked")
  1288. except SQLAlchemyError as err: # pragma: no cover
  1289. flask.g.session.rollback()
  1290. _log.exception(err)
  1291. flask.flash(
  1292. "Token could not be revoked, please contact an admin", "error"
  1293. )
  1294. return flask.redirect(
  1295. flask.url_for("ui_ns.user_settings") + "#nav-api-token"
  1296. )
  1297. @UI_NS.route("/settings/forcelogout/", methods=("POST",))
  1298. @UI_NS.route("/settings/forcelogout", methods=("POST",))
  1299. @login_required
  1300. def force_logout():
  1301. """ Set refuse_sessions_before, logging the user out everywhere
  1302. """
  1303. if admin_session_timedout():
  1304. flask.flash("Action canceled, try it again", "error")
  1305. return flask.redirect(
  1306. flask.url_for("auth_login", next=flask.request.url)
  1307. )
  1308. # we just need an empty form here to validate that csrf token is present
  1309. form = pagure.forms.PagureForm()
  1310. if form.validate_on_submit():
  1311. # Ensure the user is in the DB at least
  1312. user = _get_user(username=flask.g.fas_user.username)
  1313. user.refuse_sessions_before = datetime.datetime.utcnow()
  1314. flask.g.session.commit()
  1315. flask.flash("All active sessions logged out")
  1316. return flask.redirect(flask.url_for("ui_ns.user_settings"))
  1317. @UI_NS.route("/about")
  1318. @UI_NS.route("/about/")
  1319. def help():
  1320. """ A page to direct users to the appropriate places to get assistance,
  1321. or find basic instance information.
  1322. """
  1323. return flask.render_template("about.html")