From 5d962dc5e44b3c893ca814548394619aca681b4f Mon Sep 17 00:00:00 2001 From: Mark LaCroix Date: Tue, 6 Jan 2015 22:57:58 -0500 Subject: [PATCH 01/12] Add option to deny anonymous (i.e. unauthorized) access --- src/main/scala/ScalatraBootstrap.scala | 1 + .../scala/app/AnonymousAccessController.scala | 14 +++++ .../scala/app/SystemSettingsController.scala | 1 + .../scala/service/SystemSettingsService.scala | 4 ++ .../servlet/BasicAuthenticationFilter.scala | 59 ++++++++++--------- src/main/twirl/admin/system.scala.html | 15 +++++ 6 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 src/main/scala/app/AnonymousAccessController.scala diff --git a/src/main/scala/ScalatraBootstrap.scala b/src/main/scala/ScalatraBootstrap.scala index 90fe41548..bdbd89867 100644 --- a/src/main/scala/ScalatraBootstrap.scala +++ b/src/main/scala/ScalatraBootstrap.scala @@ -14,6 +14,7 @@ class ScalatraBootstrap extends LifeCycle { context.getFilterRegistration("basicAuthenticationFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/git/*") // Register controllers + context.mount(new AnonymousAccessController, "/*") context.mount(new IndexController, "/") context.mount(new SearchController, "/") context.mount(new FileUploadController, "/upload") diff --git a/src/main/scala/app/AnonymousAccessController.scala b/src/main/scala/app/AnonymousAccessController.scala new file mode 100644 index 000000000..35481ab58 --- /dev/null +++ b/src/main/scala/app/AnonymousAccessController.scala @@ -0,0 +1,14 @@ +package app + +class AnonymousAccessController extends AnonymousAccessControllerBase + +trait AnonymousAccessControllerBase extends ControllerBase { + get(!context.settings.allowAnonymousAccess, context.loginAccount.isEmpty) { + if(!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") && + !context.currentPath.startsWith("/register")) { + Unauthorized() + } else { + pass() + } + } +} diff --git a/src/main/scala/app/SystemSettingsController.scala b/src/main/scala/app/SystemSettingsController.scala index f323347d1..af1e2c9c8 100644 --- a/src/main/scala/app/SystemSettingsController.scala +++ b/src/main/scala/app/SystemSettingsController.scala @@ -16,6 +16,7 @@ trait SystemSettingsControllerBase extends ControllerBase { "baseUrl" -> trim(label("Base URL", optional(text()))), "information" -> trim(label("Information", optional(text()))), "allowAccountRegistration" -> trim(label("Account registration", boolean())), + "allowAnonymousAccess" -> trim(label("Anonymous access", boolean())), "gravatar" -> trim(label("Gravatar", boolean())), "notification" -> trim(label("Notification", boolean())), "ssh" -> trim(label("SSH access", boolean())), diff --git a/src/main/scala/service/SystemSettingsService.scala b/src/main/scala/service/SystemSettingsService.scala index 14bc5315b..d234b9c1f 100644 --- a/src/main/scala/service/SystemSettingsService.scala +++ b/src/main/scala/service/SystemSettingsService.scala @@ -14,6 +14,7 @@ trait SystemSettingsService { settings.baseUrl.foreach(x => props.setProperty(BaseURL, x.replaceFirst("/\\Z", ""))) settings.information.foreach(x => props.setProperty(Information, x)) props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString) + props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString) props.setProperty(Gravatar, settings.gravatar.toString) props.setProperty(Notification, settings.notification.toString) props.setProperty(Ssh, settings.ssh.toString) @@ -63,6 +64,7 @@ trait SystemSettingsService { getOptionValue[String](props, BaseURL, None).map(x => x.replaceFirst("/\\Z", "")), getOptionValue[String](props, Information, None), getValue(props, AllowAccountRegistration, false), + getValue(props, AllowAnonymousAccess, true), getValue(props, Gravatar, true), getValue(props, Notification, false), getValue(props, Ssh, false), @@ -109,6 +111,7 @@ object SystemSettingsService { baseUrl: Option[String], information: Option[String], allowAccountRegistration: Boolean, + allowAnonymousAccess: Boolean, gravatar: Boolean, notification: Boolean, ssh: Boolean, @@ -152,6 +155,7 @@ object SystemSettingsService { private val BaseURL = "base_url" private val Information = "information" private val AllowAccountRegistration = "allow_account_registration" + private val AllowAnonymousAccess = "allow_anonymous_access" private val Gravatar = "gravatar" private val Notification = "notification" private val Ssh = "ssh" diff --git a/src/main/scala/servlet/BasicAuthenticationFilter.scala b/src/main/scala/servlet/BasicAuthenticationFilter.scala index 8272c7acd..cbecfc1ac 100644 --- a/src/main/scala/servlet/BasicAuthenticationFilter.scala +++ b/src/main/scala/servlet/BasicAuthenticationFilter.scala @@ -28,33 +28,45 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou override def setCharacterEncoding(encoding: String) = {} } + val isUpdating = request.getRequestURI.endsWith("/git-receive-pack") || "service=git-receive-pack".equals(request.getQueryString) + + val settings = loadSystemSettings() + try { - defining(request.paths){ case Array(_, repositoryOwner, repositoryName, _*) => - getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match { - case Some(repository) => { - if(!request.getRequestURI.endsWith("/git-receive-pack") && - !"service=git-receive-pack".equals(request.getQueryString) && !repository.repository.isPrivate){ - chain.doFilter(req, wrappedResponse) - } else { - request.getHeader("Authorization") match { - case null => requireAuth(response) - case auth => decodeAuthHeader(auth).split(":") match { - case Array(username, password) => getWritableUser(username, password, repository) match { - case Some(account) => { - request.setAttribute(Keys.Request.UserName, account.userName) - chain.doFilter(req, wrappedResponse) + defining(request.paths){ + case Array(_, repositoryOwner, repositoryName, _*) => + getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match { + case Some(repository) => { + if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){ + chain.doFilter(req, wrappedResponse) + } else { + request.getHeader("Authorization") match { + case null => requireAuth(response) + case auth => decodeAuthHeader(auth).split(":") match { + case Array(username, password) => { + authenticate(settings, username, password) match { + case Some(account) => { + if(isUpdating && hasWritePermission(repository.owner, repository.name, Some(account))){ + request.setAttribute(Keys.Request.UserName, account.userName) + } + chain.doFilter(req, wrappedResponse) + } + case None => requireAuth(response) + } } - case None => requireAuth(response) + case _ => requireAuth(response) } - case _ => requireAuth(response) } } } + case None => { + logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.") + response.sendError(HttpServletResponse.SC_NOT_FOUND) + } } - case None => { - logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.") - response.sendError(HttpServletResponse.SC_NOT_FOUND) - } + case _ => { + logger.debug(s"Not enough path arguments: ${request.paths}") + response.sendError(HttpServletResponse.SC_NOT_FOUND) } } } catch { @@ -65,13 +77,6 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou } } - private def getWritableUser(username: String, password: String, repository: RepositoryService.RepositoryInfo) - (implicit session: Session): Option[Account] = - authenticate(loadSystemSettings(), username, password) match { - case x @ Some(account) if(hasWritePermission(repository.owner, repository.name, x)) => x - case _ => None - } - private def requireAuth(response: HttpServletResponse): Unit = { response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"") response.sendError(HttpServletResponse.SC_UNAUTHORIZED) diff --git a/src/main/twirl/admin/system.scala.html b/src/main/twirl/admin/system.scala.html index 9379672ef..7b347cdee 100644 --- a/src/main/twirl/admin/system.scala.html +++ b/src/main/twirl/admin/system.scala.html @@ -54,6 +54,21 @@ + + +
+ +
+ + +
+
From 48fe7133f72895ab9fe0a9fccea96c7bbb47664d Mon Sep 17 00:00:00 2001 From: Mark LaCroix Date: Wed, 7 Jan 2015 09:47:36 -0500 Subject: [PATCH 02/12] Add anonymous access option to tests --- src/test/scala/view/AvatarImageProviderSpec.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/scala/view/AvatarImageProviderSpec.scala b/src/test/scala/view/AvatarImageProviderSpec.scala index b8e5b8eeb..262fd502d 100644 --- a/src/test/scala/view/AvatarImageProviderSpec.scala +++ b/src/test/scala/view/AvatarImageProviderSpec.scala @@ -95,6 +95,7 @@ class AvatarImageProviderSpec extends Specification with Mockito { baseUrl = None, information = None, allowAccountRegistration = false, + allowAnonymousAccess = true, gravatar = useGravatar, notification = false, ssh = false, From 5f939c18b4af1a741569d0a77c9ec9d22e8684f2 Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sun, 25 Jan 2015 14:45:37 +0900 Subject: [PATCH 03/12] (refs #609)Convert labelId when rename repository --- src/main/scala/service/RepositoryService.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/scala/service/RepositoryService.scala b/src/main/scala/service/RepositoryService.scala index 6894125ad..f54291b29 100644 --- a/src/main/scala/service/RepositoryService.scala +++ b/src/main/scala/service/RepositoryService.scala @@ -2,7 +2,7 @@ package service import model.Profile._ import profile.simple._ -import model.{Repository, Account, Collaborator} +import model.{Repository, Account, Collaborator, Label} import util.JGitUtil trait RepositoryService { self: AccountService => @@ -94,9 +94,17 @@ trait RepositoryService { self: AccountService => PullRequests .insertAll(pullRequests .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*) IssueComments .insertAll(issueComments .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*) Labels .insertAll(labels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*) - IssueLabels .insertAll(issueLabels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*) CommitComments.insertAll(commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*) + // Convert labelId + val oldLabelMap = labels.map(x => (x.labelId, x.labelName)).toMap + val newLabelMap = Labels.filter(_.byRepository(newUserName, newRepositoryName)).map(x => (x.labelName, x.labelId)).list.toMap + IssueLabels.insertAll(issueLabels.map(x => x.copy( + labelId = newLabelMap(oldLabelMap(x.labelId)), + userName = newUserName, + repositoryName = newRepositoryName + )) :_*) + if(account.isGroupAccount){ Collaborators.insertAll(getGroupMembers(newUserName).map(m => Collaborator(newUserName, newRepositoryName, m.userName)) :_*) } else { From e24684cb2bc65a2f277838e1837309aac2cf2282 Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sun, 25 Jan 2015 20:01:12 +0900 Subject: [PATCH 04/12] Update favicon --- src/main/twirl/main.scala.html | 2 +- .../webapp/assets/common/images/favicon.png | Bin 6964 -> 0 bytes .../webapp/assets/common/images/gitbucket.png | Bin 7813 -> 18903 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/main/webapp/assets/common/images/favicon.png diff --git a/src/main/twirl/main.scala.html b/src/main/twirl/main.scala.html index 154431de7..9b8ae0280 100644 --- a/src/main/twirl/main.scala.html +++ b/src/main/twirl/main.scala.html @@ -6,7 +6,7 @@ @title - + diff --git a/src/main/webapp/assets/common/images/favicon.png b/src/main/webapp/assets/common/images/favicon.png deleted file mode 100644 index 332b6ce4cde2f0b0666bf75bbb8f9636be044e6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6964 zcmZ`-1z1#D*Pekvx)hL72Bc$XknXMl1Ocf51{gX<2_*%Q?vxZHBo(B)>(U`0%}AFt zi2S4X-tY6j&;8ExoSD7fz23dnyY|}qoacl>HI)eQsPO;*0HKPqybk(b`sa;v3;p{Z z;kp0-;K?B5l996MTRmz8DupmuDv;s7478ZW{YC=qZqt@mK+Q+k*zEgE*8`y613~Rb1E9Y^jse-;cAN3-I|H3(7?dPHA_HLSvb8lx>0BOT z;(L=sYHFc!cjf6G#NVo6j^^nl7t0_l$9M~Xr!Xlb?#p}^g^{3J*+s>71L{9c zWaE3q%SVxe`?iE(wX@vewBRd8LtjcV^I8tI;Sg1$KxRY(bRmstIX427W1PaYWRv>> z8{YItB@KW&V;vtqPge)O;f7cDc3_kDG*C3?ND^bVPKhBIX%G7T3Uc1pOLC;+F7MsMy_HYe%x+BO7>2N=Xa ztnh;TCYSvvg;=nud^7CHY@eC|e1b)wwJO-X&JqB*2bhdbQr;Lo+fPV%@p_vZZA)Xz z-J|)uf2?29;TwlJEvw5g=zcvVh_-TLddo?byBOrqhH*Ov8<~wK5CGx9;4~+s_2+lN zTmS=t{FO;ClQ1F57zk<5M;Xb_=n~}p41D&|&IK?L;5`D;3>0#?Wej@UO6Y=5;IAZ& z_o_{e2e=S`zzIAcA%83LK87NXI8}CvgvJQROHKy*kPNd+E;NQDPWobmxsa0m<@+2t zsKoIDBy~NG$;-=Z>=CfQt!to95NQg=IE?T!pxS??LW~wiseNEc-jh5X`)=#pvT7}b zBvDQK-DR2s0B5jSdpwSGFAnCYq5%%gp}cI20V9x<-aeKg52HvSEruwMrY=%B)^!c@ zR!9ykg_5G1YBj_#V%b6;Hf5p8GnrzUqOX4UtB^Gbf51iHJB!H=`35W{QqORONZ&+q zb?Tc(n?I>Y{($)zwj)MM)YD3}LhIsNOKHsKBv=NjA)bzC@ZatrTxLG>Y-C>`+Ycme zb3d?nLEnt$N9%WgF`BNsg(U{p2xK2fOrHd1bYOf$LXESIa~(w5E}$JysD)&BMdHa$Fv3N5o{JekTX#=AOD`%2@B zW9<{Slb-;>h8GVUNt=3^Re7weKNY74@Os zwU>4$9a|Dx@ziP5&D2!Xdeq4R-GWqUPHBBlv64|Gr`*~vFk$L@a)i8nmS6gO*V!?)QK+N!whnC(onH?j2Td1be1SvizH|MgR85!d3gmR%pQ zG?R*XiU!Sw;ZvehhAZYPUTldF&yXgfv(Lz9tPMdvlcd`l%e6y?5v$lN&sjc#v=^b&%Z| z$mNl%??+7T>D_rNJsPXd>TYP=kP`Jn{X{u}@eR{UL;Om+j|(- zv*;AIFrl5TUD0fATe<(WTeePCd%1~g=n0WSmjk)O?!0wnsoh*MgX!pr#lT9~iWbwC zxa~zlpT`&TgQr2eP47 zde5Ntr816PurJFc9mCsix9VGL{LK91ca;xghn0er-u1LcKb;htm=-P+P7rF3jO+fe z>e}Ocy>ue?{7&&Els~ub9Q2T09sZ96Lmw{zfFr!}ptPckp3&=-m*}$4JTg zs+4)5AKqqPeE4?|R9_Hf16vVm=u@O}nqIBd6{+WG0#I_#!qx?K?JY zx>Q~#t(HbLWBIIKnp|-&oYwoGqHZ#1FvD+M?m?QL-p#qHkBImet2zr zZMn1hmIY6XJ@dU(>GkCKv~^L_@lN)%`EccHo9I`ya@;qG7v)+VSi?ob9Ew$nTS}!$ z!x0w|^WGnhyAB2-lE+trTiAY7pCyhKXZd}dUU@hs@*ici$g7yV9& z>!NJv4glca`FR5YY3Y;z0EiLs$N*`ep)P9a>R`4<9!l9~at!%iYTf3G?J~a)0pWB!AB% z4|lh8L%1Lj&Q9Q;^TI5gpCTm~7=AkX&-G`VNQCu&Jvq7mB?~Q(`{xKZFNBBt-)L}8 z#Q&iE9QmI#E6e|=a(U|J@Jo}GB{$pw?g)26x}$l#|LP8%+y4;%Uyz@Gr{m*gx$J{_93k z_-`M-ga0IGxgpR>VL#)N4gbx-@8EBQpQ#sxB0S*^2J#4WLVu|c5)kM9 z*MWZ$<(wUy-E>`Gmhhi}{i6H^{nPrF8^izQ=C^_0h*p-O?oTc3;FieWRtvpmziM9= zX2bm(EYAI}*5c?&5Y=$DLRfpr!;o-EJ|12{Q664V-iKgbfu9ZjhvMA-u=y>5pCfW^ za2V3r?UA#ygXAw2jz7C#eh5DVt^1!Af04zxf9|8-_SK)8_7{rYwUT&f=HDlPBwmc; z@)`g@fKZW_dE^P)ePZLFH$ZAC9MMKLZhyZigeU+!3UZ5)&UwkI$|#z{>Q5p|iedsY zfYwIESaYO2Nu+adM&*0Jq7RW3o0Q`YJl${h=f1c<-K#4wH9Pvg8Yv)ZbG9?IH@G*1 zzCuZ005&+Nw-dwC|HaKp$8FDU%v}F0|NYmbq@=fp0f0zlD>8s0reS6b<{(4bBrMl;BoY@-2QhlA`?l{43)HT3JAos=&v@D;gF!uV`xtJt~D03-H3s4H*3&toerS{mt^KeChTG+{$1$~5)W?VQf zw+9!);cz{yN@Jo$0z7i|W1@44h=QJ;9y7a%&n-ej_z&eDMD7_g+o_ucw~HjbNiDigaB+*-x9^3S`sP z)|L{>bqc;mMU@(g`uHZ$e-B54q$`YEt#$Tl)G_y>%2>ZG5KGuxKjB1xP{?_~m>vRg zdMpX=KT_{BFOzUv!|`DAh{??SQ4@fP?d^m!aRkqx;4*S}rJ4P1iwNr3A3{vG)){(- zDKD8j6@w+}I*=%a-)1P`b6ig#%bD1HJP$1PTF=9`v{Jr}C69`9GVOiYjY=M;#>6c3 zrY8cj0dQ1*A}R>?&RQbJ<6b$TiqeLhvqah8`jVoM_2I06$>PUqkVx;7tzZ*MbUT?w z$RVbJ7Im^cjmlGrXH2NzkcmNj2D*)8%ed{%e!87h*t}h&j0F9lro<#j!3E-KsPbD6 zzIL9kKlzcw^`yE@VC>_yR!TL-xT^WH*Liu2Koxue_G~c_rwp*8*KFz!wOqwWkOQH1 zi|@7niv3gxDY}2#togttbk*9SgjFZ+b?Zkmbjgrc+`^iwu^r0`Z7mGfd8djsVS}On zOqA@>xbX*m%1Wi#xk^A0;KkyP*%HIb76M8h>ErEbt=7R4_!}wnGyIg+m6=K#j7o(@ z@00$UwVE2|MZ67iV7T@Y0l3-s`hDiptqD?|cUac`Y|Bm0&%zo!_SwJoBJ12xprl+L z8cK-(rRe*irB7rO$;^QBr46~>sfbmlwo9Bqh*wtaKCtG?S+}AY&9JL4mTYkjpP6|Dm zfEJ~9S^hvFNu?a8`Fd57bl>!5y<&ZGo`=TxUGModFUaoaSKMaJuWsGcHg0}lm@ej_ zDee54M%W1lqJs?1boKW+_1ZSf@3rLY*<_ z!G|D5&;!@97=t6Xg+?y}-~zB83Nfx7*(RE}?Sqf|Pz(Z)qwv?8Pf4K^@%!;&|MtP8 z!AGmm&O!HaZq4Y~))*MRT|J`r13{9sG!?Z3`rJsSorL-eh2o3RlV9KH^?u1+sdB-| zMvUjX4QEPxMC%61f_Hf>yOmk1pjKe?$-^BPD0qB^H^vk|FK0GEuvL{TzH|HAROyp2 znVt1~SxU(lwLw7gI1)5s!xChGy6R`}9|0A1KY&pO7^>#?r+Qe(j37CkA?f1-#J2 zy>BsMKW;I&zB$H0K*c9-9zNKfib|ZWw?_Ge3=(9cI9uz!=q!s6(vdUm(Q+cD-|MMm zK6Gsljo9=^C+|+-T@x&)W{1Y*XCQMA)Snj&qzgHwu{oA%oCCx;<$1qOO%ka{l$G{wZ}{R3k`30$y68ojw6mFXdjes zmvoxsc9PBxBmYPfWP%u9BdQJG%H!g4M>nU$4os%#+S|dr#0ZIg_DdDs@!> zaIZ>HDT)^1z{-ip1$ZBC&@W@?Rt;xKac>GuUz)#YZ(u|hr2P&yF@4lrqa`pa;bG#! z!F-?C4hLNlHYF69ChQDJ;x>aU$??2X)8cpf7`d3)5o6IB%;ibis;eXF^(~L9t?^Ph z>w!;%zPa1oHo5c%wdB&&tZcv~e+CL}(;el9U$8c;-U-hVkl=$01qey>fg@#_FwBJG zwZ3?)vTTA~lw;fiicL2k$01mTa9LHo%6@t%WnUHGR3mfyrhE#+=i6#`q%OpDf!2p( zWA;5QU3h3hw%jhv<1K#abs)!xlH`_Mq=|4m;wE*i73o#HR+onRE6Cn zdnMI&3bvH&NGzqQmeXSM{D(3HAhk?ldw)}O%B}=3BaTDAq+%o9OXyx6GIuk$sL66J z{`(%*5@yT+;m|PtSCx1O^Z1b61zF(Tu3G0s$vzz7(&is3$&_VvCP!LdmrV;!&allh zr5{5HKjX#_ZHe!FXJU{ve){5UZ(io)L|~I2iTPSno1U*w?F|!Y3Ja9((Pm!du0Tt} zJcE~O`a3I%Ll39Mfv$s6v{hs*s-E zMag-1h0@wPwd6SHqH{^G9toH>fR4;po+NHIlO&XBeJGf__P9p{I23kx9vG+u-e!NE=N-M)UH-3d=T@@6R{X1dVs@ zyw$-H@SI`z1Jr7NY*=aCSEO5@>K$gwQsLN5b>67B_GqU{`EJn$Z$!29dS@{H-ABh$9U;V6hkfxZTNF59+64E-4JMfgxm&YYU(NF}?hU5$ z2f79w(Ac%59vdQoI@%3^P>^(cM7;slPFv8e*Voz*e$(hr&WonfFuaEXdbTu*y5uic zbY$X@?%GhN02?Aojg^rcIgB4thf8gkZ2KtqQGv7&rS;Jp(mC z47MeATqJmKd!?Gb&(7>+XfJC%$(zx~P80T*0%BvRVIC1erK+%gke(BRO+FnDx|HQ( zdKr>}X&boWp4>xXDJ8aPiaSSRFk^IBZ=$Smv+1H*;j(!{ONR+JdkNTMAC&APm3EXB z)9y=nSBaO+y?>wqgkvtsjXDNcFMd_CZ{{c^Zi+TV6 diff --git a/src/main/webapp/assets/common/images/gitbucket.png b/src/main/webapp/assets/common/images/gitbucket.png index fad9a32c48e728125c002f35ea0dd0d1f961d74b..945d93a5fe43f5a972326e35705f19bf5418e3f1 100644 GIT binary patch delta 15519 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 O0000 Date: Wed, 28 Jan 2015 00:09:34 +0900 Subject: [PATCH 05/12] (refs #532) Fix rendering of link over image --- src/main/scala/view/Markdown.scala | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/scala/view/Markdown.scala b/src/main/scala/view/Markdown.scala index 87719b4d6..836e5c91f 100644 --- a/src/main/scala/view/Markdown.scala +++ b/src/main/scala/view/Markdown.scala @@ -195,6 +195,32 @@ class GitBucketHtmlSerializer( printTag(node, "li") } } + + override def visit(node: ExpLinkNode) { + printLink(linkRenderer.render(node, printLinkChildrenToString(node))) + } + + def printLinkChildrenToString(node: SuperNode) = { + val priorPrinter = printer + printer = new Printer() + visitLinkChildren(node) + val result = printer.getString() + printer = priorPrinter + result + } + + def visitLinkChildren(node: SuperNode) { + import scala.collection.JavaConversions._ + node.getChildren.foreach(child => child match { + case node: ExpImageNode => visitLinkChild(node) + case node: SuperNode => visitLinkChildren(node) + case _ => child.accept(this) + }) + } + + def visitLinkChild(node: ExpImageNode) { + printer.print("\"").printEncoded(printChildrenToString(node)).print("\"/") + } } object GitBucketHtmlSerializer { From b79f6a5fa05a7667bd8dc8f1daad9f03828e3df7 Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Thu, 29 Jan 2015 21:47:00 +0900 Subject: [PATCH 06/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a902ad665..5c67abd40 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ GitBucket [![Gitter chat](https://badges.gitter.im/takezoe/gitbucket.png)](https://gitter.im/takezoe/gitbucket) [![Build Status](https://travis-ci.org/takezoe/gitbucket.svg?branch=master)](https://travis-ci.org/takezoe/gitbucket) ========= -GitBucket is the easily installable Github clone written with Scala. +GitBucket is the easily installable GitHub clone based on Scala. Features From b9b6589bd728048a137a546c8ecab4318e4e0fe6 Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Thu, 29 Jan 2015 21:47:54 +0900 Subject: [PATCH 07/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c67abd40..0926318b2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ GitBucket [![Gitter chat](https://badges.gitter.im/takezoe/gitbucket.png)](https://gitter.im/takezoe/gitbucket) [![Build Status](https://travis-ci.org/takezoe/gitbucket.svg?branch=master)](https://travis-ci.org/takezoe/gitbucket) ========= -GitBucket is the easily installable GitHub clone based on Scala. +GitBucket is the easily installable GitHub clone powered by Scala. Features From 06b5b92673e383ad6b0696a31977b612cfc2f864 Mon Sep 17 00:00:00 2001 From: nazoking Date: Fri, 30 Jan 2015 04:14:04 +0900 Subject: [PATCH 08/12] update pullrequest commitId on file edited by online editor --- .../app/RepositoryViewerController.scala | 7 ++++-- .../scala/service/PullRequestService.scala | 13 ++++++++++ .../scala/servlet/GitRepositoryServlet.scala | 24 +------------------ src/main/scala/util/JGitUtil.scala | 20 ++++++++++++++++ 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala index 00f67556f..74fceaefb 100644 --- a/src/main/scala/app/RepositoryViewerController.scala +++ b/src/main/scala/app/RepositoryViewerController.scala @@ -21,7 +21,7 @@ import service.WebHookService.WebHookPayload class RepositoryViewerController extends RepositoryViewerControllerBase with RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService - with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator + with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService /** @@ -29,7 +29,7 @@ class RepositoryViewerController extends RepositoryViewerControllerBase */ trait RepositoryViewerControllerBase extends ControllerBase { self: RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService - with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator => + with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService => ArchiveCommand.registerFormat("zip", new ZipFormat) ArchiveCommand.registerFormat("tar.gz", new TgzFormat) @@ -496,6 +496,9 @@ trait RepositoryViewerControllerBase extends ControllerBase { //refUpdate.setRefLogMessage("merged", true) refUpdate.update() + // update pull request + updatePullRequests(repository.owner, repository.name, branch) + // record activity recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch, List(new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId)))) diff --git a/src/main/scala/service/PullRequestService.scala b/src/main/scala/service/PullRequestService.scala index 9a3239b4c..68121f906 100644 --- a/src/main/scala/service/PullRequestService.scala +++ b/src/main/scala/service/PullRequestService.scala @@ -3,6 +3,7 @@ package service import model.Profile._ import profile.simple._ import model.{PullRequest, Issue} +import util.JGitUtil trait PullRequestService { self: IssuesService => import PullRequestService._ @@ -81,6 +82,18 @@ trait PullRequestService { self: IssuesService => .map { case (t1, t2) => t1 } .list + /** + * Fetch pull request contents into refs/pull/${issueId}/head and update pull request table. + */ + def updatePullRequests(owner: String, repository: String, branch: String)(implicit s: Session): Unit = + getPullRequestsByRequest(owner, repository, branch, false).foreach { pullreq => + if(Repositories.filter(_.byRepository(pullreq.userName, pullreq.repositoryName)).exists.run){ + val (commitIdTo, commitIdFrom) = JGitUtil.updatePullRequest( + pullreq.userName, pullreq.repositoryName, pullreq.branch, pullreq.issueId, + pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.requestBranch) + updateCommitId(pullreq.userName, pullreq.repositoryName, pullreq.issueId, commitIdTo, commitIdFrom) + } + } } object PullRequestService { diff --git a/src/main/scala/servlet/GitRepositoryServlet.scala b/src/main/scala/servlet/GitRepositoryServlet.scala index df55d46fd..7fde407d0 100644 --- a/src/main/scala/servlet/GitRepositoryServlet.scala +++ b/src/main/scala/servlet/GitRepositoryServlet.scala @@ -174,7 +174,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: case ReceiveCommand.Type.CREATE | ReceiveCommand.Type.UPDATE | ReceiveCommand.Type.UPDATE_NONFASTFORWARD => - updatePullRequests(branchName) + updatePullRequests(owner, repository, branchName) case _ => } } @@ -211,26 +211,4 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: } } } - - /** - * Fetch pull request contents into refs/pull/${issueId}/head and update pull request table. - */ - private def updatePullRequests(branch: String) = - getPullRequestsByRequest(owner, repository, branch, false).foreach { pullreq => - if(getRepository(pullreq.userName, pullreq.repositoryName, baseUrl).isDefined){ - using(Git.open(Directory.getRepositoryDir(pullreq.userName, pullreq.repositoryName)), - Git.open(Directory.getRepositoryDir(pullreq.requestUserName, pullreq.requestRepositoryName))){ (oldGit, newGit) => - oldGit.fetch - .setRemote(Directory.getRepositoryDir(owner, repository).toURI.toString) - .setRefSpecs(new RefSpec(s"refs/heads/${branch}:refs/pull/${pullreq.issueId}/head").setForceUpdate(true)) - .call - - val commitIdTo = oldGit.getRepository.resolve(s"refs/pull/${pullreq.issueId}/head").getName - val commitIdFrom = JGitUtil.getForkedCommitId(oldGit, newGit, - pullreq.userName, pullreq.repositoryName, pullreq.branch, - pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.requestBranch) - updateCommitId(pullreq.userName, pullreq.repositoryName, pullreq.issueId, commitIdTo, commitIdFrom) - } - } - } } diff --git a/src/main/scala/util/JGitUtil.scala b/src/main/scala/util/JGitUtil.scala index b5e2e3bef..0f76f1e30 100644 --- a/src/main/scala/util/JGitUtil.scala +++ b/src/main/scala/util/JGitUtil.scala @@ -13,6 +13,7 @@ import org.eclipse.jgit.treewalk._ import org.eclipse.jgit.treewalk.filter._ import org.eclipse.jgit.diff.DiffEntry.ChangeType import org.eclipse.jgit.errors.{ConfigInvalidException, MissingObjectException} +import org.eclipse.jgit.transport.RefSpec import java.util.Date import org.eclipse.jgit.api.errors.{JGitInternalException, InvalidRefNameException, RefAlreadyExistsException, NoHeadException} import service.RepositoryService @@ -674,6 +675,25 @@ object JGitUtil { }.head.id } + /** + * Fetch pull request contents into refs/pull/${issueId}/head and return (commitIdTo, commitIdFrom) + */ + def updatePullRequest(userName: String, repositoryName:String, branch: String, issueId: Int, + requestUserName: String, requestRepositoryName: String, requestBranch: String):(String, String) = + using(Git.open(Directory.getRepositoryDir(userName, repositoryName)), + Git.open(Directory.getRepositoryDir(requestUserName, requestRepositoryName))){ (oldGit, newGit) => + oldGit.fetch + .setRemote(Directory.getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString) + .setRefSpecs(new RefSpec(s"refs/heads/${requestBranch}:refs/pull/${issueId}/head").setForceUpdate(true)) + .call + + val commitIdTo = oldGit.getRepository.resolve(s"refs/pull/${issueId}/head").getName + val commitIdFrom = getForkedCommitId(oldGit, newGit, + userName, repositoryName, branch, + requestUserName, requestRepositoryName, requestBranch) + (commitIdTo, commitIdFrom) + } + /** * Returns the last modified commit of specified path * @param git the Git object From 9ba564c864ea62f871f93ca8c5e1185a8aceadde Mon Sep 17 00:00:00 2001 From: nazoking Date: Fri, 30 Jan 2015 15:32:53 +0900 Subject: [PATCH 09/12] test/html is cause of xss --- src/main/scala/app/AccountController.scala | 5 +++-- src/main/scala/app/ControllerBase.scala | 10 ++++++++++ src/main/scala/app/IssuesController.scala | 3 +-- src/main/scala/app/RepositoryViewerController.scala | 3 +-- src/main/scala/app/WikiController.scala | 3 +-- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/scala/app/AccountController.scala b/src/main/scala/app/AccountController.scala index 4628a3660..9acfd8df7 100644 --- a/src/main/scala/app/AccountController.scala +++ b/src/main/scala/app/AccountController.scala @@ -135,8 +135,9 @@ trait AccountControllerBase extends AccountManagementControllerBase { get("/:userName/_avatar"){ val userName = params("userName") getAccountByUserName(userName).flatMap(_.image).map { image => - contentType = FileUtil.getMimeType(image) - new java.io.File(getUserUploadDir(userName), image) + outputUploadedRawData( + FileUtil.getMimeType(image), + new java.io.File(getUserUploadDir(userName), image)) } getOrElse { contentType = "image/png" Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png") diff --git a/src/main/scala/app/ControllerBase.scala b/src/main/scala/app/ControllerBase.scala index c31b90f7e..be8d37af5 100644 --- a/src/main/scala/app/ControllerBase.scala +++ b/src/main/scala/app/ControllerBase.scala @@ -134,6 +134,16 @@ abstract class ControllerBase extends ScalatraFilter if (path.startsWith("http")) path else baseUrl + super.url(path, params, false, false, false) + /** against XSS */ + def outputUploadedRawData[DATATYPE](contentType: String, rawData: DATATYPE): DATATYPE = { + if(contentType.split(";").head.trim.toLowerCase.startsWith("text/html")){ + this.contentType = "text/plain" + } else { + this.contentType = contentType + } + response.addHeader("X-Content-Type-Options", "nosniff") + rawData + } } /** diff --git a/src/main/scala/app/IssuesController.scala b/src/main/scala/app/IssuesController.scala index 9f6efcf3d..7abab1acb 100644 --- a/src/main/scala/app/IssuesController.scala +++ b/src/main/scala/app/IssuesController.scala @@ -292,8 +292,7 @@ trait IssuesControllerBase extends ControllerBase { (Directory.getAttachedDir(repository.owner, repository.name) match { case dir if(dir.exists && dir.isDirectory) => dir.listFiles.find(_.getName.startsWith(params("file") + ".")).map { file => - contentType = FileUtil.getMimeType(file.getName) - file + outputUploadedRawData(FileUtil.getMimeType(file.getName), file) } case _ => None }) getOrElse NotFound diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala index 00f67556f..192fe0f2f 100644 --- a/src/main/scala/app/RepositoryViewerController.scala +++ b/src/main/scala/app/RepositoryViewerController.scala @@ -214,8 +214,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { if(raw){ // Download defining(JGitUtil.getContentFromId(git, objectId, false).get){ bytes => - contentType = FileUtil.getContentType(path, bytes) - bytes + outputUploadedRawData(FileUtil.getContentType(path, bytes), bytes) } } else { repo.html.blob(id, repository, path.split("/").toList, JGitUtil.getContentInfo(git, path, objectId), diff --git a/src/main/scala/app/WikiController.scala b/src/main/scala/app/WikiController.scala index 5270d0316..ea4349179 100644 --- a/src/main/scala/app/WikiController.scala +++ b/src/main/scala/app/WikiController.scala @@ -164,8 +164,7 @@ trait WikiControllerBase extends ControllerBase { val path = multiParams("splat").head getFileContent(repository.owner, repository.name, path).map { bytes => - contentType = FileUtil.getContentType(path, bytes) - bytes + outputUploadedRawData(FileUtil.getContentType(path, bytes), bytes) } getOrElse NotFound }) From 8161560757436fd0e033efa18480ea795e8f62eb Mon Sep 17 00:00:00 2001 From: HairyFotr Date: Fri, 30 Jan 2015 21:34:25 +0100 Subject: [PATCH 10/12] Fix typo --- src/main/twirl/search/code.scala.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/twirl/search/code.scala.html b/src/main/twirl/search/code.scala.html index 04d0a1922..cc4566537 100644 --- a/src/main/twirl/search/code.scala.html +++ b/src/main/twirl/search/code.scala.html @@ -16,7 +16,7 @@ @files.drop((page - 1) * CodeLimit).take(CodeLimit).map { file => } From 6a758902ef2838e093474cd7428ec035e704b83a Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sun, 1 Feb 2015 12:55:37 +0900 Subject: [PATCH 11/12] Small fix for #615 --- src/main/scala/app/AccountController.scala | 4 +--- src/main/scala/app/ControllerBase.scala | 6 ++++-- src/main/scala/app/IssuesController.scala | 2 +- src/main/scala/app/RepositoryViewerController.scala | 2 +- src/main/scala/app/WikiController.scala | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/scala/app/AccountController.scala b/src/main/scala/app/AccountController.scala index 456026750..83116543d 100644 --- a/src/main/scala/app/AccountController.scala +++ b/src/main/scala/app/AccountController.scala @@ -135,9 +135,7 @@ trait AccountControllerBase extends AccountManagementControllerBase { get("/:userName/_avatar"){ val userName = params("userName") getAccountByUserName(userName).flatMap(_.image).map { image => - outputUploadedRawData( - FileUtil.getMimeType(image), - new java.io.File(getUserUploadDir(userName), image)) + RawData(FileUtil.getMimeType(image), new java.io.File(getUserUploadDir(userName), image)) } getOrElse { contentType = "image/png" Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png") diff --git a/src/main/scala/app/ControllerBase.scala b/src/main/scala/app/ControllerBase.scala index be8d37af5..8a184f08d 100644 --- a/src/main/scala/app/ControllerBase.scala +++ b/src/main/scala/app/ControllerBase.scala @@ -134,8 +134,10 @@ abstract class ControllerBase extends ScalatraFilter if (path.startsWith("http")) path else baseUrl + super.url(path, params, false, false, false) - /** against XSS */ - def outputUploadedRawData[DATATYPE](contentType: String, rawData: DATATYPE): DATATYPE = { + /** + * Use this method to response the raw data against XSS. + */ + protected def RawData[T](contentType: String, rawData: T): T = { if(contentType.split(";").head.trim.toLowerCase.startsWith("text/html")){ this.contentType = "text/plain" } else { diff --git a/src/main/scala/app/IssuesController.scala b/src/main/scala/app/IssuesController.scala index 7abab1acb..063b8b28a 100644 --- a/src/main/scala/app/IssuesController.scala +++ b/src/main/scala/app/IssuesController.scala @@ -292,7 +292,7 @@ trait IssuesControllerBase extends ControllerBase { (Directory.getAttachedDir(repository.owner, repository.name) match { case dir if(dir.exists && dir.isDirectory) => dir.listFiles.find(_.getName.startsWith(params("file") + ".")).map { file => - outputUploadedRawData(FileUtil.getMimeType(file.getName), file) + RawData(FileUtil.getMimeType(file.getName), file) } case _ => None }) getOrElse NotFound diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala index de2a6ad98..57b1027ed 100644 --- a/src/main/scala/app/RepositoryViewerController.scala +++ b/src/main/scala/app/RepositoryViewerController.scala @@ -214,7 +214,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { if(raw){ // Download defining(JGitUtil.getContentFromId(git, objectId, false).get){ bytes => - outputUploadedRawData(FileUtil.getContentType(path, bytes), bytes) + RawData(FileUtil.getContentType(path, bytes), bytes) } } else { repo.html.blob(id, repository, path.split("/").toList, JGitUtil.getContentInfo(git, path, objectId), diff --git a/src/main/scala/app/WikiController.scala b/src/main/scala/app/WikiController.scala index ea4349179..c273018d0 100644 --- a/src/main/scala/app/WikiController.scala +++ b/src/main/scala/app/WikiController.scala @@ -164,7 +164,7 @@ trait WikiControllerBase extends ControllerBase { val path = multiParams("splat").head getFileContent(repository.owner, repository.name, path).map { bytes => - outputUploadedRawData(FileUtil.getContentType(path, bytes), bytes) + RawData(FileUtil.getContentType(path, bytes), bytes) } getOrElse NotFound }) From 0085cb24ad6ed3d9a4d0de6876b2582375eabbe2 Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sun, 1 Feb 2015 13:00:31 +0900 Subject: [PATCH 12/12] Add description about 2.8 --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 0926318b2..e229137fe 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,13 @@ Run the following commands in `Terminal` to Release Notes -------- +### 2.8 - 1 Feb 2015 +- New logo and icons +- New system setting options to control visibility +- Comment on side-by-side diff +- Information message on sign-in page +- Fork repository by group account + ### 2.7 - 29 Dec 2014 - Comment for commit and diff - Fix security issue in markdown rendering
@file.path
-
Last commited @helper.html.datetimeago(file.lastModified)
+
Last committed @helper.html.datetimeago(file.lastModified)
@Html(file.highlightText)