Nav apraksta

main.go 193KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161
  1. package main
  2. import (
  3. uuid "github.com/satori/go.uuid"
  4. "github.com/shuffle/shuffle-shared"
  5. singul "github.com/shuffle/singul/pkg"
  6. //"net/http/pprof"
  7. "archive/zip"
  8. "bufio"
  9. "bytes"
  10. "context"
  11. "crypto/md5"
  12. "strconv"
  13. "encoding/hex"
  14. "encoding/json"
  15. "errors"
  16. "fmt"
  17. "io"
  18. "io/ioutil"
  19. "log"
  20. "math/rand"
  21. "net/http"
  22. "net/url"
  23. "os"
  24. "os/exec"
  25. "net/http/httptest"
  26. "strings"
  27. "time"
  28. "github.com/frikky/kin-openapi/openapi2"
  29. "github.com/frikky/kin-openapi/openapi2conv"
  30. "github.com/frikky/kin-openapi/openapi3"
  31. "github.com/go-git/go-billy/v5/memfs"
  32. "github.com/go-git/go-git/v5"
  33. "github.com/go-git/go-git/v5/plumbing"
  34. http2 "github.com/go-git/go-git/v5/plumbing/transport/http"
  35. "github.com/go-git/go-git/v5/storage/memory"
  36. // Random
  37. "sort"
  38. xj "github.com/basgys/goxml2json"
  39. newscheduler "github.com/carlescere/scheduler"
  40. "golang.org/x/crypto/bcrypt"
  41. "gopkg.in/yaml.v3"
  42. // Web
  43. "github.com/gorilla/mux"
  44. //http2 "gopkg.in/src-d/go-git.v5/plumbing/transport/http"
  45. //http2 "github.com/go-git/go-git/plumbing/transport/http"
  46. )
  47. // This is used to handle onprem vs offprem databases etc
  48. var gceProject = "shuffle"
  49. var bucketName = "shuffler.appspot.com"
  50. var baseAppPath = "/home/frikky/git/shaffuru/tmp/apps"
  51. var baseDockerName = "frikky/shuffle"
  52. var registryName = "registry.hub.docker.com"
  53. var runningEnvironment = "onprem"
  54. var syncUrl = "https://shuffler.io"
  55. var debug = false
  56. //var syncUrl = "http://localhost:5002"
  57. type retStruct struct {
  58. Success bool `json:"success"`
  59. SyncFeatures shuffle.SyncFeatures `json:"sync_features"`
  60. SessionKey string `json:"session_key"`
  61. IntervalSeconds int64 `json:"interval_seconds"`
  62. Reason string `json:"reason"`
  63. Subscriptions []shuffle.PaymentSubscription `json:"subscriptions"`
  64. Licensed bool `json:"licensed"`
  65. CloudSyncUrl string `json:"cloud_sync_url,omitempty"`
  66. }
  67. type Contact struct {
  68. Firstname string `json:"firstname"`
  69. Lastname string `json:"lastname"`
  70. Title string `json:"title"`
  71. Companyname string `json:"companyname"`
  72. Phone string `json:"phone"`
  73. Email string `json:"email"`
  74. Message string `json:"message"`
  75. }
  76. type Translator struct {
  77. Src struct {
  78. Name string `json:"name" datastore:"name"`
  79. Value string `json:"value" datastore:"value,noindex"`
  80. Description string `json:"description" datastore:"description,noindex"`
  81. Required string `json:"required" datastore:"required"`
  82. Type string `json:"type" datastore:"type"`
  83. Schema struct {
  84. Type string `json:"type" datastore:"type"`
  85. } `json:"schema" datastore:"schema"`
  86. } `json:"src" datastore:"src"`
  87. Dst struct {
  88. Name string `json:"name" datastore:"name"`
  89. Value string `json:"value" datastore:"value,noindex"`
  90. Type string `json:"type" datastore:"type"`
  91. Description string `json:"description" datastore:"description,noindex"`
  92. Required string `json:"required" datastore:"required"`
  93. Schema struct {
  94. Type string `json:"type" datastore:"type"`
  95. } `json:"schema" datastore:"schema"`
  96. } `json:"dst" datastore:"dst"`
  97. }
  98. type Appconfig struct {
  99. Key string `json:"key" datastore:"key"`
  100. Value string `json:"value" datastore:"value,noindex"`
  101. }
  102. type ScheduleApp struct {
  103. Foldername string `json:"foldername" datastore:"foldername,noindex"`
  104. Name string `json:"name" datastore:"name,noindex"`
  105. Id string `json:"id" datastore:"id,noindex"`
  106. Description string `json:"description" datastore:"description,noindex"`
  107. Action string `json:"action" datastore:"action,noindex"`
  108. Config []Appconfig `json:"config,omitempty" datastore:"config,noindex"`
  109. }
  110. type AppInfo struct {
  111. SourceApp ScheduleApp `json:"sourceapp,omitempty" datastore:"sourceapp,noindex"`
  112. DestinationApp ScheduleApp `json:"destinationapp,omitempty" datastore:"destinationapp,noindex"`
  113. }
  114. // May 2020: Reused for onprem schedules - Id, Seconds, WorkflowId and argument
  115. type ScheduleOld struct {
  116. Id string `json:"id" datastore:"id"`
  117. StartNode string `json:"start_node" datastore:"start_node"`
  118. Seconds int `json:"seconds" datastore:"seconds"`
  119. WorkflowId string `json:"workflow_id" datastore:"workflow_id", `
  120. Argument string `json:"argument" datastore:"argument"`
  121. WrappedArgument string `json:"wrapped_argument" datastore:"wrapped_argument"`
  122. AppInfo AppInfo `json:"appinfo" datastore:"appinfo,noindex"`
  123. Finished bool `json:"finished" finished:"id"`
  124. BaseAppLocation string `json:"base_app_location" datastore:"baseapplocation,noindex"`
  125. Translator []Translator `json:"translator,omitempty" datastore:"translator"`
  126. Org string `json:"org" datastore:"org"`
  127. CreatedBy string `json:"createdby" datastore:"createdby"`
  128. Availability string `json:"availability" datastore:"availability"`
  129. CreationTime int64 `json:"creationtime" datastore:"creationtime,noindex"`
  130. LastModificationtime int64 `json:"lastmodificationtime" datastore:"lastmodificationtime,noindex"`
  131. LastRuntime int64 `json:"lastruntime" datastore:"lastruntime,noindex"`
  132. Frequency string `json:"frequency" datastore:"frequency,noindex"`
  133. Environment string `json:"environment" datastore:"environment"`
  134. }
  135. // Returned from /GET /schedules
  136. type Schedules struct {
  137. Schedules []ScheduleOld `json:"schedules"`
  138. Success bool `json:"success"`
  139. }
  140. type ScheduleApps struct {
  141. Apps []ApiYaml `json:"apps"`
  142. Success bool `json:"success"`
  143. }
  144. // The yaml that is uploaded
  145. type ApiYaml struct {
  146. Name string `json:"name" yaml:"name" required:"true datastore:"name"`
  147. Foldername string `json:"foldername" yaml:"foldername" required:"true datastore:"foldername"`
  148. Id string `json:"id" yaml:"id",required:"true, datastore:"id"`
  149. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  150. AppVersion string `json:"app_version" yaml:"app_version",datastore:"app_version"`
  151. ContactInfo struct {
  152. Name string `json:"name" datastore:"name" yaml:"name"`
  153. Url string `json:"url" datastore:"url" yaml:"url"`
  154. } `json:"contact_info" datastore:"contact_info" yaml:"contact_info"`
  155. Types []string `json:"types" datastore:"types" yaml:"types"`
  156. Input []struct {
  157. Name string `json:"name" datastore:"name" yaml:"name"`
  158. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  159. InputParameters []struct {
  160. Name string `json:"name" datastore:"name" yaml:"name"`
  161. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  162. Required string `json:"required" datastore:"required" yaml:"required"`
  163. Schema struct {
  164. Type string `json:"type" datastore:"type" yaml:"type"`
  165. } `json:"schema" datastore:"schema" yaml:"schema"`
  166. } `json:"inputparameters" datastore:"inputparameters" yaml:"inputparameters"`
  167. OutputParameters []struct {
  168. Name string `json:"name" datastore:"name" yaml:"name"`
  169. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  170. Required string `json:"required" datastore:"required" yaml:"required"`
  171. Schema struct {
  172. Type string `json:"type" datastore:"type" yaml:"type"`
  173. } `json:"schema" datastore:"schema" yaml:"schema"`
  174. } `json:"outputparameters" datastore:"outputparameters" yaml:"outputparameters"`
  175. Config []struct {
  176. Name string `json:"name" datastore:"name" yaml:"name"`
  177. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  178. Required string `json:"required" datastore:"required" yaml:"required"`
  179. Schema struct {
  180. Type string `json:"type" datastore:"type" yaml:"type"`
  181. } `json:"schema" datastore:"schema" yaml:"schema"`
  182. } `json:"config" datastore:"config" yaml:"config"`
  183. } `json:"input" datastore:"input" yaml:"input"`
  184. Output []struct {
  185. Name string `json:"name" datastore:"name" yaml:"name"`
  186. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  187. Config []struct {
  188. Name string `json:"name" datastore:"name" yaml:"name"`
  189. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  190. Required string `json:"required" datastore:"required" yaml:"required"`
  191. Schema struct {
  192. Type string `json:"type" datastore:"type" yaml:"type"`
  193. } `json:"schema" datastore:"schema" yaml:"schema"`
  194. } `json:"config" datastore:"config" yaml:"config"`
  195. InputParameters []struct {
  196. Name string `json:"name" datastore:"name" yaml:"name"`
  197. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  198. Required string `json:"required" datastore:"required" yaml:"required"`
  199. Schema struct {
  200. Type string `json:"type" datastore:"type" yaml:"type"`
  201. } `json:"schema" datastore:"schema" yaml:"schema"`
  202. } `json:"inputparameters" datastore:"inputparameters" yaml:"inputparameters"`
  203. OutputParameters []struct {
  204. Name string `json:"name" datastore:"name" yaml:"name"`
  205. Description string `json:"description" datastore:"description,noindex" yaml:"description"`
  206. Required string `json:"required" datastore:"required" yaml:"required"`
  207. Schema struct {
  208. Type string `json:"type" datastore:"type" yaml:"type"`
  209. } `json:"schema" datastore:"schema" yaml:"schema"`
  210. } `json:"outputparameters" datastore:"outputparameters" yaml:"outputparameters"`
  211. } `json:"output" datastore:"output" yaml:"output"`
  212. }
  213. type Hooks struct {
  214. Hooks []Hook `json:"hooks"`
  215. Success bool `json:"-"`
  216. }
  217. type Info struct {
  218. Url string `json:"url" datastore:"url"`
  219. Name string `json:"name" datastore:"name"`
  220. Description string `json:"description" datastore:"description,noindex"`
  221. }
  222. // Actions to be done by webhooks etc
  223. // Field is the actual field to use from json
  224. type HookAction struct {
  225. Type string `json:"type" datastore:"type"`
  226. Name string `json:"name" datastore:"name"`
  227. Id string `json:"id" datastore:"id"`
  228. Field string `json:"field" datastore:"field"`
  229. }
  230. type Hook struct {
  231. Id string `json:"id" datastore:"id"`
  232. Start string `json:"start" datastore:"start"`
  233. Info Info `json:"info" datastore:"info"`
  234. Actions []HookAction `json:"actions" datastore:"actions,noindex"`
  235. Type string `json:"type" datastore:"type"`
  236. Owner string `json:"owner" datastore:"owner"`
  237. Status string `json:"status" datastore:"status"`
  238. Workflows []string `json:"workflows" datastore:"workflows"`
  239. Running bool `json:"running" datastore:"running"`
  240. OrgId string `json:"org_id" datastore:"org_id"`
  241. Environment string `json:"environment" datastore:"environment"`
  242. }
  243. func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
  244. data := map[string]interface{}{
  245. "id": "12345",
  246. "ts": time.Now().Format(time.RFC3339),
  247. }
  248. b, err := json.Marshal(data)
  249. if err != nil {
  250. http.Error(w, err.Error(), 400)
  251. return
  252. }
  253. w.Write(b)
  254. }
  255. func jsonPrettyPrint(in string) string {
  256. var out bytes.Buffer
  257. err := json.Indent(&out, []byte(in), "", "\t")
  258. if err != nil {
  259. return in
  260. }
  261. return out.String()
  262. }
  263. // Does User exist?
  264. // Does User have permission to view / run this?
  265. // Encoding: /json?
  266. // General authentication
  267. func authenticate(request *http.Request) bool {
  268. authField := "authorization"
  269. authenticationKey := "topkek"
  270. //authFound := false
  271. // This should work right?
  272. for name, headers := range request.Header {
  273. name = strings.ToLower(name)
  274. for _, h := range headers {
  275. if name == authField && h == authenticationKey {
  276. //log.Printf("%v: %v", name, h)
  277. return true
  278. }
  279. }
  280. }
  281. return false
  282. }
  283. func checkError(cmdName string, cmdArgs []string) error {
  284. cmd := exec.Command(cmdName, cmdArgs...)
  285. cmdReader, err := cmd.StdoutPipe()
  286. if err != nil {
  287. fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err)
  288. return err
  289. }
  290. scanner := bufio.NewScanner(cmdReader)
  291. go func() {
  292. for scanner.Scan() {
  293. log.Printf("Out: %s\n", scanner.Text())
  294. }
  295. }()
  296. err = cmd.Start()
  297. if err != nil {
  298. fmt.Fprintln(os.Stderr, "Error starting Cmd", err)
  299. return err
  300. }
  301. err = cmd.Wait()
  302. if err != nil {
  303. fmt.Fprintln(os.Stderr, "Error waiting for Cmd", err)
  304. return err
  305. }
  306. return nil
  307. }
  308. func md5sum(data []byte) string {
  309. hasher := md5.New()
  310. hasher.Write(data)
  311. newmd5 := hex.EncodeToString(hasher.Sum(nil))
  312. return newmd5
  313. }
  314. func md5sumfile(filepath string) string {
  315. dat, err := ioutil.ReadFile(filepath)
  316. if err != nil {
  317. log.Printf("Error in dat: %s", err)
  318. }
  319. hasher := md5.New()
  320. hasher.Write(dat)
  321. newmd5 := hex.EncodeToString(hasher.Sum(nil))
  322. log.Printf("%s: %s", filepath, newmd5)
  323. return newmd5
  324. }
  325. func checkFileExistsLocal(basepath string, filepath string) bool {
  326. User := "test"
  327. // md5sum
  328. // get tmp/results/md5sum/folder/results.json
  329. // parse /tmp/results/md5sum/results.json
  330. path := fmt.Sprintf("%s/%s", basepath, md5sumfile(filepath))
  331. if _, err := os.Stat(path); os.IsNotExist(err) {
  332. //log.Printf("File error for %s: %s", filepath, err)
  333. return false
  334. }
  335. log.Printf("File %s exists. Getting for User %s.", filepath, User)
  336. return true
  337. }
  338. func redirect(w http.ResponseWriter, req *http.Request) {
  339. // remove/add not default ports from req.Host
  340. target := "https://" + req.Host + req.URL.Path
  341. if len(req.URL.RawQuery) > 0 {
  342. target += "?" + req.URL.RawQuery
  343. }
  344. log.Printf("redirect to: %s", target)
  345. http.Redirect(w, req, target,
  346. // see @andreiavrammsd comment: often 307 > 301
  347. http.StatusTemporaryRedirect)
  348. }
  349. // No more emails :)
  350. func checkUsername(Username string) error {
  351. // Stupid first check of email loool
  352. //if !strings.Contains(Username, "@") || !strings.Contains(Username, ".") {
  353. // return errors.New("Invalid Username")
  354. //}
  355. if len(Username) < 3 {
  356. return errors.New("Minimum Username length is 3")
  357. }
  358. return nil
  359. }
  360. func createNewUser(username, password, role, apikey string, org shuffle.OrgMini) error {
  361. // Returns false if there is an issue
  362. // Use this for register
  363. err := shuffle.CheckPasswordStrength(username, password)
  364. if err != nil {
  365. log.Printf("[WARNING] Bad password strength: %s", err)
  366. return err
  367. }
  368. err = checkUsername(username)
  369. if err != nil {
  370. log.Printf("[WARNING] Bad Username strength: %s", err)
  371. return err
  372. }
  373. ctx := context.Background()
  374. users, err := shuffle.FindUser(ctx, strings.ToLower(strings.TrimSpace(username)))
  375. if err != nil && len(users) == 0 {
  376. log.Printf("[WARNING] Failed getting user %s: %s", username, err)
  377. return err
  378. }
  379. if len(users) > 0 {
  380. return errors.New(fmt.Sprintf("Username %s already exists", username))
  381. }
  382. hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 8)
  383. if err != nil {
  384. log.Printf("Wrong password for %s: %s", username, err)
  385. return err
  386. }
  387. newUser := new(shuffle.User)
  388. newUser.Username = username
  389. newUser.Password = string(hashedPassword)
  390. newUser.Verified = false
  391. newUser.CreationTime = time.Now().Unix()
  392. newUser.Active = true
  393. newUser.Orgs = []string{org.Id}
  394. if role == "admin" {
  395. newUser.Role = "admin"
  396. newUser.Roles = []string{"admin"}
  397. } else {
  398. newUser.Role = "user"
  399. newUser.Roles = []string{"user"}
  400. }
  401. newUser.ActiveOrg = shuffle.OrgMini{
  402. Id: org.Id,
  403. Name: org.Name,
  404. Role: newUser.Role,
  405. }
  406. if len(apikey) > 0 {
  407. newUser.ApiKey = apikey
  408. }
  409. // set limits
  410. newUser.Limits.DailyApiUsage = 100
  411. newUser.Limits.DailyWorkflowExecutions = 1000
  412. newUser.Limits.DailyCloudExecutions = 100
  413. newUser.Limits.DailyTriggers = 20
  414. newUser.Limits.DailyMailUsage = 100
  415. newUser.Limits.MaxTriggers = 10
  416. newUser.Limits.MaxWorkflows = 10
  417. // Set base info for the user
  418. newUser.Executions.TotalApiUsage = 0
  419. newUser.Executions.TotalWorkflowExecutions = 0
  420. newUser.Executions.TotalAppExecutions = 0
  421. newUser.Executions.TotalCloudExecutions = 0
  422. newUser.Executions.TotalOnpremExecutions = 0
  423. newUser.Executions.DailyApiUsage = 0
  424. newUser.Executions.DailyWorkflowExecutions = 0
  425. newUser.Executions.DailyAppExecutions = 0
  426. newUser.Executions.DailyCloudExecutions = 0
  427. newUser.Executions.DailyOnpremExecutions = 0
  428. verifyToken := uuid.NewV4()
  429. ID := uuid.NewV4()
  430. newUser.Id = ID.String()
  431. newUser.VerificationToken = verifyToken.String()
  432. err = shuffle.SetUser(ctx, newUser, true)
  433. if err != nil {
  434. log.Printf("[ERROR] Problem adding User %s: %s", username, err)
  435. return err
  436. }
  437. neworg, err := shuffle.GetOrg(ctx, org.Id)
  438. if err == nil {
  439. //neworg.Users = append(neworg.Users, *newUser)
  440. for tutorialIndex, tutorial := range neworg.Tutorials {
  441. if tutorial.Name == "Invite teammates" {
  442. neworg.Tutorials[tutorialIndex].Description = fmt.Sprintf("%d users are in your org. Org name and Image change next.", len(neworg.Users))
  443. if len(neworg.Users) > 1 {
  444. neworg.Tutorials[tutorialIndex].Done = true
  445. neworg.Tutorials[tutorialIndex].Link = "/admin?tab=users"
  446. }
  447. break
  448. }
  449. }
  450. err = shuffle.SetOrg(ctx, *neworg, neworg.Id)
  451. if err != nil {
  452. log.Printf("Failed updating org with user %s", newUser.Username)
  453. } else {
  454. log.Printf("[INFO] Successfully updated org with user %s!", newUser.Username)
  455. }
  456. }
  457. return nil
  458. }
  459. func handleRegister(resp http.ResponseWriter, request *http.Request) {
  460. cors := shuffle.HandleCors(resp, request)
  461. if cors {
  462. return
  463. }
  464. // Only admin can CREATE users, but if there are no users, anyone can make (first)
  465. ctx := context.Background()
  466. users, countErr := shuffle.GetAllUsers(ctx)
  467. count := len(users)
  468. user, err := shuffle.HandleApiAuthentication(resp, request)
  469. if err != nil {
  470. if (countErr == nil && count > 0) || countErr != nil {
  471. resp.WriteHeader(401)
  472. resp.Write([]byte(`{"success": false, "reason": "Users already exist. Please go to /login to log into your admin user."}`))
  473. return
  474. }
  475. }
  476. apikey := ""
  477. if count != 0 {
  478. if user.Role != "admin" {
  479. resp.WriteHeader(401)
  480. resp.Write([]byte(`{"success": false, "reason": "Can't register without being admin (2)"}`))
  481. return
  482. }
  483. } else {
  484. apikey = uuid.NewV4().String()
  485. }
  486. // Gets a struct of Username, password
  487. data, err := shuffle.ParseLoginParameters(resp, request)
  488. if err != nil {
  489. log.Printf("Invalid params: %s", err)
  490. resp.WriteHeader(401)
  491. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  492. return
  493. }
  494. role := "user"
  495. if count == 0 {
  496. role = "admin"
  497. }
  498. currentOrg := user.ActiveOrg
  499. if user.ActiveOrg.Id == "" {
  500. log.Printf("[WARNING] There's no active org for the user %s. Checking if there's a single one to assign it to.", user.Username)
  501. orgs, err := shuffle.GetAllOrgs(ctx)
  502. if err == nil && len(orgs) > 0 {
  503. log.Printf("[WARNING] No org exists for user %s. Setting to default (first one)", user.Username)
  504. currentOrg = shuffle.OrgMini{
  505. Id: orgs[0].Id,
  506. Name: orgs[0].Name,
  507. }
  508. } else {
  509. log.Printf("[WARNING] Couldn't find an org to attach to. Create?")
  510. orgSetupName := "default"
  511. orgId := uuid.NewV4().String()
  512. newOrg := shuffle.Org{
  513. Name: orgSetupName,
  514. Id: orgId,
  515. Org: orgSetupName,
  516. Users: []shuffle.User{user},
  517. Roles: []string{"admin", "user"},
  518. CloudSync: false,
  519. }
  520. err = shuffle.SetOrg(ctx, newOrg, newOrg.Id)
  521. if err != nil {
  522. log.Printf("[WARNING] Failed setting init organization: %s", err)
  523. } else {
  524. log.Printf("[DEBUG] Successfully created the default org!")
  525. defaultEnv := os.Getenv("ENVIRONMENT_NAME")
  526. if len(defaultEnv) == 0 {
  527. defaultEnv = "Shuffle"
  528. log.Printf("[DEBUG] Setting default environment for org to %s", defaultEnv)
  529. }
  530. item := shuffle.Environment{
  531. Name: defaultEnv,
  532. Type: "onprem",
  533. OrgId: orgId,
  534. Default: true,
  535. Id: uuid.NewV4().String(),
  536. }
  537. err = shuffle.SetEnvironment(ctx, &item)
  538. if err != nil {
  539. log.Printf("[WARNING] Failed setting up new environment for new org: %s", err)
  540. }
  541. currentOrg = shuffle.OrgMini{
  542. Id: newOrg.Id,
  543. Name: newOrg.Name,
  544. }
  545. user.ActiveOrg = currentOrg
  546. }
  547. }
  548. }
  549. err = createNewUser(data.Username, data.Password, role, apikey, currentOrg)
  550. if err != nil {
  551. if strings.Contains(err.Error(), "already exists") {
  552. // Assign it to the org
  553. log.Printf("[WARNING] User %s already exists. Assigning to org %s", data.Username, currentOrg.Name)
  554. // Get the user
  555. users, err := shuffle.FindUser(ctx, data.Username)
  556. if err != nil || len(users) == 0 {
  557. log.Printf("[WARNING] Failed finding user %s: %s", data.Username, err)
  558. resp.WriteHeader(400)
  559. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  560. return
  561. }
  562. newUser := users[0]
  563. if !shuffle.ArrayContains(newUser.Orgs, currentOrg.Id) {
  564. newUser.Orgs = append(newUser.Orgs, currentOrg.Id)
  565. }
  566. if newUser.ActiveOrg.Id == "" || newUser.ActiveOrg.Name == "" {
  567. newUser.ActiveOrg = currentOrg
  568. }
  569. err = shuffle.SetUser(ctx, &newUser, true)
  570. if err != nil {
  571. log.Printf("[WARNING] Failed updating the user %s: %s", data.Username, err)
  572. resp.WriteHeader(400)
  573. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  574. return
  575. }
  576. resp.WriteHeader(200)
  577. resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
  578. log.Printf("[INFO] %s Successfully re-added to org %s (%s)", data.Username, currentOrg.Name, currentOrg.Id)
  579. return
  580. } else {
  581. log.Printf("[WARNING] Failed registering user: %s", err)
  582. resp.WriteHeader(401)
  583. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  584. return
  585. }
  586. }
  587. resp.WriteHeader(200)
  588. resp.Write([]byte(fmt.Sprintf(`{"success": true, "apikey": "%s"}`, apikey)))
  589. log.Printf("[INFO] %s Successfully registered.", data.Username)
  590. }
  591. func handleCookie(request *http.Request) bool {
  592. c, err := request.Cookie("session_token")
  593. if err != nil {
  594. return false
  595. }
  596. if len(c.Value) == 0 {
  597. return false
  598. }
  599. return true
  600. }
  601. // Returns whether the user is logged in or not etc.
  602. // Also has more data about the user and org
  603. func handleInfo(resp http.ResponseWriter, request *http.Request) {
  604. cors := shuffle.HandleCors(resp, request)
  605. if cors {
  606. return
  607. }
  608. userInfo, err := shuffle.HandleApiAuthentication(resp, request)
  609. if err != nil {
  610. log.Printf("[WARNING] Api authentication failed in handleInfo: %s", err)
  611. resp.WriteHeader(401)
  612. resp.Write([]byte(`{"success": false}`))
  613. return
  614. }
  615. ctx := context.Background()
  616. // This is a long check to see if an inactive admin can access the site
  617. parsedAdmin := "false"
  618. if userInfo.Role == "admin" {
  619. parsedAdmin = "true"
  620. }
  621. if !userInfo.Active {
  622. if userInfo.Role == "admin" {
  623. parsedAdmin = "true"
  624. ctx := context.Background()
  625. users, err := shuffle.GetAllUsers(ctx)
  626. if err != nil {
  627. resp.WriteHeader(401)
  628. resp.Write([]byte(`{"success": false, "reason": "Failed to get other users when verifying admin user"}`))
  629. return
  630. }
  631. activeFound := false
  632. adminFound := false
  633. for _, user := range users {
  634. if user.Id == userInfo.Id {
  635. continue
  636. }
  637. if user.Role != "admin" {
  638. continue
  639. }
  640. if user.Active {
  641. activeFound = true
  642. }
  643. adminFound = true
  644. }
  645. // Must ALWAYS be an active admin
  646. // Will return no access if another admin is active
  647. if !adminFound {
  648. log.Printf("NO OTHER ADMINS FOUND - CONTINUE!")
  649. } else {
  650. //
  651. if activeFound {
  652. log.Printf("OTHER ACTIVE ADMINS FOUND - CAN'T PASS")
  653. resp.WriteHeader(401)
  654. resp.Write([]byte(`{"success": false, "reason": "This user is locked"}`))
  655. return
  656. } else {
  657. log.Printf("NO OTHER ADMINS FOUND - CONTINUE!")
  658. }
  659. }
  660. } else {
  661. resp.WriteHeader(401)
  662. resp.Write([]byte(`{"success": false, "reason": "This user is locked"}`))
  663. return
  664. }
  665. }
  666. go shuffle.CheckSessionOrgs(ctx, userInfo)
  667. //log.Printf("%s %s", session.Session, UserInfo.Session)
  668. //if session.Session != userInfo.Session {
  669. // log.Printf("Session %s is not the same as %s for %s. %s", userInfo.Session, session.Session, userInfo.Username, err)
  670. // resp.WriteHeader(401)
  671. // resp.Write([]byte(`{"success": false, "reason": ""}`))
  672. // return
  673. //}
  674. expiration := time.Now().Add(3600 * time.Second)
  675. sessionCookie := shuffle.ConstructSessionCookie(userInfo.Session, expiration)
  676. http.SetCookie(resp, sessionCookie)
  677. // Updating user info if there's something wrong
  678. if len(userInfo.ActiveOrg.Name) == 0 || len(userInfo.ActiveOrg.Id) == 0 {
  679. if len(userInfo.Orgs) == 0 || (len(userInfo.Orgs) > 0 && userInfo.Orgs[0] == "") {
  680. orgs, err := shuffle.GetAllOrgs(ctx)
  681. log.Printf("[INFO] Fixing organization for user %s (%s). Found orgs: %d", userInfo.Username, userInfo.Id, len(orgs))
  682. if err == nil && len(orgs) > 0 {
  683. for _, org := range orgs {
  684. if len(org.Id) == 0 {
  685. continue
  686. }
  687. // Prolly some way here to jump into another org
  688. // when you have access to the DB
  689. userInfo.ActiveOrg = shuffle.OrgMini{
  690. Name: org.Name,
  691. Id: org.Id,
  692. Role: "admin",
  693. }
  694. userInfo.Orgs = []string{org.Id}
  695. break
  696. }
  697. }
  698. // Make a new one in case we couldn't find one
  699. if len(userInfo.ActiveOrg.Id) == 0 {
  700. orgSetupName := "default"
  701. orgId := uuid.NewV4().String()
  702. newOrg := shuffle.Org{
  703. Name: orgSetupName,
  704. Id: orgId,
  705. Org: orgSetupName,
  706. Users: []shuffle.User{},
  707. Roles: []string{"admin", "user"},
  708. CloudSync: false,
  709. }
  710. err = shuffle.SetOrg(ctx, newOrg, newOrg.Id)
  711. if err == nil {
  712. userInfo.ActiveOrg = shuffle.OrgMini{
  713. Name: newOrg.Name,
  714. Id: newOrg.Id,
  715. Role: "admin",
  716. }
  717. userInfo.Orgs = []string{newOrg.Id}
  718. } else {
  719. log.Printf("[WARNING] Failed to set new org: %s", err)
  720. }
  721. }
  722. // Set user
  723. err = shuffle.SetUser(ctx, &userInfo, true)
  724. if err != nil {
  725. log.Printf("[WARNING] Failed fixing org info for user %s (%s)", userInfo.Username, userInfo.Id)
  726. } else {
  727. log.Printf("[INFO] Set organization for %s (%s) to be %s (%s)", userInfo.Username, userInfo.Id, userInfo.ActiveOrg.Name, userInfo.ActiveOrg.Id)
  728. }
  729. } else if len(userInfo.Orgs) > 0 && userInfo.Orgs[0] != "" {
  730. _, err := shuffle.GetOrg(ctx, userInfo.Orgs[0])
  731. if err != nil {
  732. orgs, err := shuffle.GetAllOrgs(ctx)
  733. if err == nil {
  734. newStringOrgs := []string{}
  735. newOrgs := []shuffle.Org{}
  736. for _, org := range orgs {
  737. if strings.ToLower(org.Name) == strings.ToLower(userInfo.Orgs[0]) {
  738. newOrgs = append(newOrgs, org)
  739. newStringOrgs = append(newStringOrgs, org.Id)
  740. }
  741. }
  742. if len(newOrgs) > 0 {
  743. userInfo.ActiveOrg = shuffle.OrgMini{
  744. Id: newOrgs[0].Id,
  745. Name: newOrgs[0].Name,
  746. }
  747. userInfo.Orgs = newStringOrgs
  748. err = shuffle.SetUser(ctx, &userInfo, true)
  749. if err != nil {
  750. log.Printf("Error patching User for activeOrg: %s", err)
  751. } else {
  752. log.Printf("Updated the users' org")
  753. }
  754. }
  755. } else {
  756. log.Printf("Failed getting orgs for user. Major issue.: %s", err)
  757. }
  758. } else {
  759. // 1. Check if the org exists by ID
  760. // 2. if it does, overwrite user
  761. userInfo.ActiveOrg = shuffle.OrgMini{
  762. Id: userInfo.Orgs[0],
  763. }
  764. err = shuffle.SetUser(ctx, &userInfo, true)
  765. if err != nil {
  766. log.Printf("[INFO] Error patching User for activeOrg: %s", err)
  767. }
  768. }
  769. }
  770. }
  771. org, err := shuffle.GetOrg(ctx, userInfo.ActiveOrg.Id)
  772. if err != nil {
  773. log.Printf("[DEBUG] Failed to get org during getinfo: %s", err)
  774. }
  775. childOrgs := []shuffle.Org{}
  776. if len(org.CreatorOrg) > 0 {
  777. childOrgs, err = shuffle.GetAllChildOrgs(ctx, org.CreatorOrg)
  778. if err != nil {
  779. log.Printf("[ERROR] Failed to get child orgs during getinfo: %s", err)
  780. childOrgs = []shuffle.Org{}
  781. }
  782. }
  783. failToLoadOrgs := []string{}
  784. sort.Slice(childOrgs, func(i, j int) bool {
  785. return childOrgs[i].Created < childOrgs[j].Created
  786. })
  787. parentOrg := &shuffle.Org{}
  788. if len(org.CreatorOrg) > 0 {
  789. parentOrg, err = shuffle.GetOrg(ctx, org.CreatorOrg)
  790. if err != nil {
  791. log.Printf("[ERROR] Failed to get parent org during getinfo: %s", err)
  792. parentOrg = &shuffle.Org{}
  793. }
  794. licenseOrg := shuffle.HandleCheckLicense(ctx, *parentOrg)
  795. parentOrg = &licenseOrg
  796. }
  797. limit := 3
  798. if parentOrg.Licensed && parentOrg.SyncFeatures.MultiTenant.Active {
  799. limit = int(parentOrg.SyncFeatures.MultiTenant.Limit)
  800. }
  801. for index, org := range childOrgs {
  802. if index < limit {
  803. continue
  804. }
  805. failToLoadOrgs = append(failToLoadOrgs, org.Id)
  806. }
  807. if len(org.CreatorOrg) > 0 && len(childOrgs) > limit && shuffle.ArrayContains(failToLoadOrgs, userInfo.ActiveOrg.Id) {
  808. userInfo.ActiveOrg = shuffle.OrgMini{
  809. Id: parentOrg.Id,
  810. Name: parentOrg.Name,
  811. Role: userInfo.Role,
  812. Branding: parentOrg.Branding,
  813. Image: parentOrg.Image,
  814. }
  815. if parentOrg.Licensed {
  816. log.Printf("[INFO] Parent org %s is licensed. But Multi-tenant feature is not active. Moving user %s to parent org %s", parentOrg.Name, userInfo.Username)
  817. } else {
  818. log.Printf("[INFO] Parent org %s has more than 3 child orgs and is not licensed. Moving user %s to parent org %s", parentOrg.Name, userInfo.Username)
  819. }
  820. err = shuffle.SetUser(ctx, &userInfo, false)
  821. if err != nil {
  822. log.Printf("[WARNING] Failed setting user to parent org: %s", err)
  823. }
  824. reason := "Parent org has more than 3 child orgs and is not licensed. Moving user to parent org. Contact support@shuffler.io for more information"
  825. if parentOrg.Licensed {
  826. reason = fmt.Sprintf("Parent organization is licensed, but the maximum number of sub-organizations (%d) has been reached. You have been moved to the parent organization. Please contact support@shuffler.io for further assistance.", parentOrg.SyncFeatures.MultiTenant.Limit)
  827. }
  828. resp.WriteHeader(200)
  829. resp.Write([]byte(`{"success": true, "reason": "` + reason + `", "switch_parent": true}`))
  830. return
  831. }
  832. //if err == nil {
  833. if len(org.Id) > 0 {
  834. if userInfo.Role == "" {
  835. //err = shuffle.SetUser(ctx, &userInfo, false)
  836. for _, user := range org.Users {
  837. if user.Id != userInfo.Id {
  838. continue
  839. }
  840. userInfo.ActiveOrg.Role = user.Role
  841. }
  842. }
  843. userInfo.ActiveOrg = shuffle.OrgMini{
  844. Id: org.Id,
  845. Name: org.Name,
  846. CreatorOrg: org.CreatorOrg,
  847. ChildOrgs: org.ChildOrgs,
  848. Role: userInfo.ActiveOrg.Role,
  849. Image: org.Image,
  850. }
  851. if parsedAdmin == "false" {
  852. // Validating admin user again just to make sure
  853. // This is to avoid issues for the first org ever
  854. for _, user := range org.Users {
  855. if user.Id != userInfo.Id {
  856. continue
  857. }
  858. if user.Role == "admin" {
  859. break
  860. }
  861. }
  862. }
  863. }
  864. orgPriorities := org.Priorities
  865. if len(org.Priorities) < 10 {
  866. //log.Printf("[WARNING] Should find and add priorities as length is less than 10 for org %s", userInfo.ActiveOrg.Id)
  867. newPriorities, err := shuffle.GetPriorities(ctx, userInfo, org)
  868. if err != nil {
  869. log.Printf("[WARNING] Failed getting new priorities for org %s: %s", org.Id, err)
  870. //orgPriorities = []shuffle.Priority{}
  871. } else {
  872. orgPriorities = newPriorities
  873. // A way to manage them over time
  874. }
  875. }
  876. orgInterests := org.Interests
  877. userInfo.ActiveOrg.Users = []shuffle.UserMini{}
  878. userOrgs := []shuffle.OrgMini{}
  879. for _, item := range userInfo.Orgs {
  880. if item == userInfo.ActiveOrg.Id {
  881. userOrgs = append(userOrgs, userInfo.ActiveOrg)
  882. continue
  883. }
  884. org, err := shuffle.GetOrg(ctx, item)
  885. if len(org.Id) > 0 {
  886. userOrgs = append(userOrgs, shuffle.OrgMini{
  887. Id: org.Id,
  888. Name: org.Name,
  889. CreatorOrg: org.CreatorOrg,
  890. Image: org.Image,
  891. })
  892. } else {
  893. log.Printf("[WARNING] Failed to get org %s (%s) for user %s. Error: %#v", org.Name, item, userInfo.Username, err)
  894. }
  895. }
  896. // FIXME: This is bad, but we've had a lot of bugs with edit users, and this is the quick fix.
  897. if userInfo.Role == "" && userInfo.ActiveOrg.Role == "" && parsedAdmin == "false" {
  898. userInfo.Role = "admin"
  899. userInfo.ActiveOrg.Role = "admin"
  900. parsedAdmin = "true"
  901. err = shuffle.SetUser(ctx, &userInfo, true)
  902. if err != nil {
  903. log.Printf("[WARNING] Automatically asigning user as admin to their org because they don't have a role at all failed: %s", err)
  904. resp.WriteHeader(500)
  905. resp.Write([]byte(`{"success": false}`))
  906. return
  907. } else {
  908. log.Printf("[DEBUG] Made user %s org-admin as they didn't have any role specified", err)
  909. }
  910. }
  911. if parsedAdmin == "true" {
  912. userInfo.Role = "admin"
  913. userInfo.ActiveOrg.Role = "admin"
  914. }
  915. chatDisabled := false
  916. if os.Getenv("SHUFFLE_CHAT_DISABLED") == "true" {
  917. chatDisabled = true
  918. }
  919. userOrgs = shuffle.SortOrgList(userOrgs)
  920. tutorialsFinished := []shuffle.Tutorial{}
  921. for _, tutorial := range userInfo.PersonalInfo.Tutorials {
  922. tutorialsFinished = append(tutorialsFinished, shuffle.Tutorial{
  923. Name: tutorial,
  924. })
  925. }
  926. if len(org.SecurityFramework.SIEM.Name) > 0 || len(org.SecurityFramework.Network.Name) > 0 || len(org.SecurityFramework.EDR.Name) > 0 || len(org.SecurityFramework.Cases.Name) > 0 || len(org.SecurityFramework.IAM.Name) > 0 || len(org.SecurityFramework.Assets.Name) > 0 || len(org.SecurityFramework.Intel.Name) > 0 || len(org.SecurityFramework.Communication.Name) > 0 {
  927. tutorialsFinished = append(tutorialsFinished, shuffle.Tutorial{
  928. Name: "find_integrations",
  929. })
  930. }
  931. for _, tutorial := range org.Tutorials {
  932. tutorialsFinished = append(tutorialsFinished, tutorial)
  933. }
  934. licensed := shuffle.IsLicensed(ctx, *org)
  935. workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 1000, 0)
  936. if err != nil {
  937. log.Printf("{WARNING] Failed getting apps (getworkflowapps): %s", err)
  938. resp.WriteHeader(401)
  939. resp.Write([]byte(`{"success": false}`))
  940. return
  941. }
  942. orgApps := workflowapps
  943. activatedAppIds := []string{}
  944. for _, app := range orgApps {
  945. activatedAppIds = append(activatedAppIds, app.ID)
  946. }
  947. parsedStatus := []string{}
  948. if len(org.ManagerOrgs) > 0 || userInfo.ActiveOrg.CreatorOrg != "" {
  949. parsedStatus = append(parsedStatus, "sub_org")
  950. parentOrgId := userInfo.ActiveOrg.CreatorOrg
  951. if len(userInfo.ActiveOrg.CreatorOrg) == 0 {
  952. parentOrgId = org.ManagerOrgs[0].Id
  953. }
  954. // Check for licensing/branding of parent and override
  955. parentOrg, err := shuffle.GetOrg(ctx, parentOrgId)
  956. if err == nil {
  957. parent := shuffle.HandleCheckLicense(ctx, *parentOrg)
  958. if parent.SyncFeatures.Branding.Active {
  959. parsedStatus = append(parsedStatus, "integration_partner")
  960. // except theme take from parent org
  961. org.Branding.EnableChat = parentOrg.Branding.EnableChat
  962. org.Branding.HomeUrl = parentOrg.Branding.HomeUrl
  963. org.Branding.DocumentationLink = parentOrg.Defaults.DocumentationReference
  964. org.Branding.SupportEmail = parentOrg.Branding.SupportEmail
  965. org.Branding.LogoutUrl = parentOrg.Branding.LogoutUrl
  966. org.Branding.BrandColor = parentOrg.Branding.BrandColor
  967. org.Branding.BrandName = parentOrg.Branding.BrandName
  968. org.Branding.GlobalUser = parentOrg.Branding.GlobalUser
  969. if len(org.Branding.Theme) == 0 {
  970. org.Branding.Theme = parentOrg.Branding.Theme
  971. }
  972. userInfo.ActiveOrg.Branding = parentOrg.Branding
  973. userInfo.ActiveOrg.Image = parentOrg.Image
  974. userInfo.ActiveOrg.Branding.DocumentationLink = parentOrg.Defaults.DocumentationReference
  975. userInfo.ActiveOrg.Branding.BrandColor = parentOrg.Branding.BrandColor
  976. userInfo.ActiveOrg.Branding.SupportEmail = org.Branding.SupportEmail
  977. userInfo.ActiveOrg.Branding.LogoutUrl = org.Branding.LogoutUrl
  978. userInfo.ActiveOrg.Branding.BrandName = parentOrg.Branding.BrandName
  979. if len(org.Branding.Theme) == 0 {
  980. userInfo.ActiveOrg.Branding.Theme = parentOrg.Branding.Theme
  981. } else {
  982. userInfo.ActiveOrg.Branding.Theme = org.Branding.Theme
  983. }
  984. // check whether current is global user or not? means is user part of parent org or not
  985. for _, user := range parentOrg.Users {
  986. if user.Id == userInfo.Id && user.Role == "admin" {
  987. userInfo.ActiveOrg.Branding.GlobalUser = true
  988. break
  989. }
  990. }
  991. } else {
  992. userInfo.ActiveOrg.Branding = shuffle.OrgBranding{}
  993. }
  994. }
  995. } else {
  996. // for parent org branding
  997. licenseOrg := shuffle.HandleCheckLicense(ctx, *org)
  998. org = &licenseOrg
  999. if org.SyncFeatures.Branding.Active {
  1000. userInfo.ActiveOrg.Branding.Theme = org.Branding.Theme
  1001. userInfo.ActiveOrg.Branding.DocumentationLink = org.Defaults.DocumentationReference
  1002. userInfo.ActiveOrg.Branding.SupportEmail = org.Branding.SupportEmail
  1003. userInfo.ActiveOrg.Branding.LogoutUrl = org.Branding.LogoutUrl
  1004. userInfo.ActiveOrg.Branding.BrandColor = org.Branding.BrandColor
  1005. userInfo.ActiveOrg.Branding.BrandName = org.Branding.BrandName
  1006. parsedStatus = append(parsedStatus, "integration_partner")
  1007. } else {
  1008. userInfo.ActiveOrg.Branding = shuffle.OrgBranding{}
  1009. }
  1010. }
  1011. aiEnabled := os.Getenv("OPENAI_API_URL") != "" && os.Getenv("AI_MODEL") != ""
  1012. returnValue := shuffle.HandleInfo{
  1013. Success: true,
  1014. Username: userInfo.Username,
  1015. Admin: parsedAdmin,
  1016. Id: userInfo.Id,
  1017. Orgs: userOrgs,
  1018. ActiveOrg: userInfo.ActiveOrg,
  1019. Cookies: []shuffle.SessionCookie{
  1020. shuffle.SessionCookie{
  1021. Key: "session_token",
  1022. Value: userInfo.Session,
  1023. Expiration: expiration.Unix(),
  1024. },
  1025. },
  1026. EthInfo: userInfo.EthInfo,
  1027. ChatDisabled: chatDisabled,
  1028. Tutorials: tutorialsFinished,
  1029. Interests: orgInterests,
  1030. Priorities: orgPriorities,
  1031. Licensed: licensed,
  1032. ActiveApps: activatedAppIds,
  1033. Theme: userInfo.Theme,
  1034. OrgStatus: parsedStatus,
  1035. AIEnabled: aiEnabled,
  1036. }
  1037. returnData, err := json.Marshal(returnValue)
  1038. if err != nil {
  1039. log.Printf("[WARNING] Failed marshalling info in handleinfo: %s", err)
  1040. resp.WriteHeader(401)
  1041. resp.Write([]byte(`{"success": false}`))
  1042. return
  1043. }
  1044. resp.WriteHeader(200)
  1045. resp.Write([]byte(returnData))
  1046. }
  1047. type passwordReset struct {
  1048. Password1 string `json:"newpassword"`
  1049. Password2 string `json:"newpassword2"`
  1050. Reference string `json:"reference"`
  1051. }
  1052. func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
  1053. cors := shuffle.HandleCors(resp, request)
  1054. if cors {
  1055. return
  1056. }
  1057. ctx := context.Background()
  1058. users, err := shuffle.GetAllUsers(ctx)
  1059. if err != nil {
  1060. resp.WriteHeader(401)
  1061. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  1062. return
  1063. }
  1064. count := len(users)
  1065. if count == 0 {
  1066. log.Printf("[WARNING] No users - redirecting for management user")
  1067. resp.WriteHeader(200)
  1068. resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "stay"}`)))
  1069. return
  1070. }
  1071. baseSSOUrl := ""
  1072. handled := []string{}
  1073. for _, user := range users {
  1074. if shuffle.ArrayContains(handled, user.ActiveOrg.Id) {
  1075. continue
  1076. }
  1077. handled = append(handled, user.ActiveOrg.Id)
  1078. org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
  1079. if err != nil {
  1080. log.Printf("[WARNING] Error getting org in admin check: %s", err)
  1081. continue
  1082. }
  1083. // No childorg setup, only parent org
  1084. // if len(org.ManagerOrgs) > 0 || len(org.CreatorOrg) > 0 {
  1085. // continue
  1086. // }
  1087. // Should run calculations
  1088. if len(org.SSOConfig.OpenIdAuthorization) > 0 {
  1089. baseSSOUrl, err = shuffle.GetOpenIdUrl(request, *org, user, "")
  1090. if err != nil {
  1091. log.Printf("[ERROR] Failed getting OpenID URL for org %s: %s", org.Name, err)
  1092. }
  1093. break
  1094. }
  1095. if len(org.SSOConfig.SSOEntrypoint) > 0 {
  1096. log.Printf("[DEBUG] Found SAML SSO url: %s", org.SSOConfig.SSOEntrypoint)
  1097. baseSSOUrl = org.SSOConfig.SSOEntrypoint
  1098. break
  1099. }
  1100. }
  1101. //log.Printf("[DEBUG] OpenID URL: %s", baseSSOUrl)
  1102. resp.WriteHeader(200)
  1103. resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "redirect", "sso_url": "%s"}`, baseSSOUrl)))
  1104. }
  1105. func fixOrgUser(ctx context.Context, org *shuffle.Org) *shuffle.Org {
  1106. //found := false
  1107. //for _, id := range user.Orgs {
  1108. // if user.ActiveOrg.Id == id {
  1109. // found = true
  1110. // break
  1111. // }
  1112. //}
  1113. //if !found {
  1114. // user.Orgs = append(user.Orgs, user.ActiveOrg.Id)
  1115. //}
  1116. //// Might be vulnerable to timing attacks.
  1117. //for _, orgId := range user.Orgs {
  1118. // if len(orgId) == 0 {
  1119. // continue
  1120. // }
  1121. // org, err := shuffle.GetOrg(ctx, orgId)
  1122. // if err != nil {
  1123. // log.Printf("Error getting org %s", orgId)
  1124. // continue
  1125. // }
  1126. // orgIndex := 0
  1127. // userFound := false
  1128. // for index, orgUser := range org.Users {
  1129. // if orgUser.Id == user.Id {
  1130. // orgIndex = index
  1131. // userFound = true
  1132. // break
  1133. // }
  1134. // }
  1135. // if userFound {
  1136. // user.PrivateApps = []WorkflowApp{}
  1137. // user.Executions = ExecutionInfo{}
  1138. // user.Limits = UserLimits{}
  1139. // user.Authentication = []UserAuth{}
  1140. // org.Users[orgIndex] = *user
  1141. // } else {
  1142. // org.Users = append(org.Users, *user)
  1143. // }
  1144. // err = shuffle.SetOrg(ctx, *org, orgId)
  1145. // if err != nil {
  1146. // log.Printf("Failed setting org %s", orgId)
  1147. // }
  1148. //}
  1149. return org
  1150. }
  1151. func fixUserOrg(ctx context.Context, user *shuffle.User) *shuffle.User {
  1152. found := false
  1153. for _, id := range user.Orgs {
  1154. if user.ActiveOrg.Id == id {
  1155. found = true
  1156. break
  1157. }
  1158. }
  1159. if !found {
  1160. user.Orgs = append(user.Orgs, user.ActiveOrg.Id)
  1161. }
  1162. // Might be vulnerable to timing attacks.
  1163. for _, orgId := range user.Orgs {
  1164. if len(orgId) == 0 {
  1165. continue
  1166. }
  1167. org, err := shuffle.GetOrg(ctx, orgId)
  1168. if err != nil {
  1169. log.Printf("Error getting org %s", orgId)
  1170. continue
  1171. }
  1172. orgIndex := 0
  1173. userFound := false
  1174. for index, orgUser := range org.Users {
  1175. if orgUser.Id == user.Id {
  1176. orgIndex = index
  1177. userFound = true
  1178. break
  1179. }
  1180. }
  1181. if userFound {
  1182. user.PrivateApps = []shuffle.WorkflowApp{}
  1183. user.Executions = shuffle.ExecutionInfo{}
  1184. user.Limits = shuffle.UserLimits{}
  1185. user.Authentication = []shuffle.UserAuth{}
  1186. org.Users[orgIndex] = *user
  1187. } else {
  1188. org.Users = append(org.Users, *user)
  1189. }
  1190. err = shuffle.SetOrg(ctx, *org, org.Id)
  1191. if err != nil {
  1192. log.Printf("Failed setting org %s", orgId)
  1193. }
  1194. }
  1195. return user
  1196. }
  1197. // Used for testing only. Shouldn't impact production.
  1198. /*
  1199. func shuffle.HandleCors(resp http.ResponseWriter, request *http.Request) bool {
  1200. // Used for Codespace dev
  1201. allowedOrigins := "https://frikky-shuffle-5gvr4xx62w64-3000.githubpreview.dev"
  1202. //origin := request.Header["Origin"]
  1203. //log.Printf("Origin: %s", origin)
  1204. //allowedOrigins := "http://localhost:3002"
  1205. resp.Header().Set("Vary", "Origin")
  1206. resp.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me, Authorization")
  1207. resp.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, PATCH")
  1208. resp.Header().Set("Access-Control-Allow-Credentials", "true")
  1209. resp.Header().Set("Access-Control-Allow-Origin", allowedOrigins)
  1210. if request.Method == "OPTIONS" {
  1211. resp.WriteHeader(200)
  1212. resp.Write([]byte("OK"))
  1213. return true
  1214. }
  1215. return false
  1216. }
  1217. */
  1218. func parseWorkflowParameters(resp http.ResponseWriter, request *http.Request) (map[string]interface{}, error) {
  1219. body, err := ioutil.ReadAll(request.Body)
  1220. if err != nil {
  1221. return nil, err
  1222. }
  1223. log.Printf("Parsing data: %s", string(body))
  1224. var t map[string]interface{}
  1225. err = json.Unmarshal(body, &t)
  1226. if err == nil {
  1227. log.Printf("PARSED!! :)")
  1228. return t, nil
  1229. }
  1230. // Translate XML to json in case of an XML blob.
  1231. // FIXME - use Content-Type and Accept headers
  1232. xml := strings.NewReader(string(body))
  1233. curjson, err := xj.Convert(xml)
  1234. if err != nil {
  1235. return t, err
  1236. }
  1237. //log.Println(curjson.String())
  1238. //log.Printf("Parsing json a second time: %s", string(curjson.String()))
  1239. err = json.Unmarshal(curjson.Bytes(), &t)
  1240. if err != nil {
  1241. return t, nil
  1242. }
  1243. envelope := t["Envelope"].(map[string]interface{})
  1244. curbody := envelope["Body"].(map[string]interface{})
  1245. //log.Println(curbody)
  1246. // ALWAYS handle strings only
  1247. // FIXME - remove this and get it from config or something
  1248. requiredField := "symptomDescription"
  1249. _, found := SearchNested(curbody, requiredField)
  1250. // Maxdepth
  1251. maxiter := 5
  1252. // Need to look for parent of the item, as that is most likely root
  1253. if found {
  1254. cnt := 0
  1255. var previousDifferentItem map[string]interface{}
  1256. var previousItem map[string]interface{}
  1257. _ = previousItem
  1258. for {
  1259. if cnt == maxiter {
  1260. break
  1261. }
  1262. // Already know it exists
  1263. key, realItem, _ := SearchNestedParent(curbody, requiredField)
  1264. // First should ALWAYS work since we already have recursion checked
  1265. if len(previousDifferentItem) == 0 {
  1266. previousDifferentItem = realItem.(map[string]interface{})
  1267. }
  1268. switch t := realItem.(type) {
  1269. case map[string]interface{}:
  1270. previousItem = realItem.(map[string]interface{})
  1271. curbody = realItem.(map[string]interface{})
  1272. default:
  1273. // Gets here if it's not an object
  1274. _ = t
  1275. //log.Printf("hi %#v", previousItem)
  1276. return previousItem, nil
  1277. }
  1278. _ = key
  1279. cnt += 1
  1280. }
  1281. }
  1282. //key, realItem, found = SearchNestedParent(newbody, requiredField)
  1283. //if !found {
  1284. // log.Println("NOT FOUND!")
  1285. //}
  1286. ////log.Println(realItem[requiredField].(map[string]interface{}))
  1287. //log.Println(realItem[requiredField])
  1288. //log.Printf("FOUND PARENT :): %s", key)
  1289. return t, nil
  1290. }
  1291. // SearchNested searches a nested structure consisting of map[string]interface{}
  1292. // and []interface{} looking for a map with a specific key name.
  1293. // If found SearchNested returns the value associated with that key, true
  1294. func SearchNestedParent(obj interface{}, key string) (string, interface{}, bool) {
  1295. switch t := obj.(type) {
  1296. case map[string]interface{}:
  1297. if v, ok := t[key]; ok {
  1298. return "", v, ok
  1299. }
  1300. for k, v := range t {
  1301. if _, ok := SearchNested(v, key); ok {
  1302. return k, v, ok
  1303. }
  1304. }
  1305. case []interface{}:
  1306. for _, v := range t {
  1307. if _, ok := SearchNested(v, key); ok {
  1308. return "", v, ok
  1309. }
  1310. }
  1311. }
  1312. return "", nil, false
  1313. }
  1314. // SearchNested searches a nested structure consisting of map[string]interface{}
  1315. // and []interface{} looking for a map with a specific key name.
  1316. // If found SearchNested returns the value associated with that key, true
  1317. // If the key is not found SearchNested returns nil, false
  1318. func SearchNested(obj interface{}, key string) (interface{}, bool) {
  1319. switch t := obj.(type) {
  1320. case map[string]interface{}:
  1321. if v, ok := t[key]; ok {
  1322. return v, ok
  1323. }
  1324. for _, v := range t {
  1325. if result, ok := SearchNested(v, key); ok {
  1326. return result, ok
  1327. }
  1328. }
  1329. case []interface{}:
  1330. for _, v := range t {
  1331. if result, ok := SearchNested(v, key); ok {
  1332. return result, ok
  1333. }
  1334. }
  1335. }
  1336. return nil, false
  1337. }
  1338. func handleSetHook(resp http.ResponseWriter, request *http.Request) {
  1339. cors := shuffle.HandleCors(resp, request)
  1340. if cors {
  1341. return
  1342. }
  1343. user, err := shuffle.HandleApiAuthentication(resp, request)
  1344. if err != nil {
  1345. log.Printf("[INFO] Api authentication failed in set new workflowhandler: %s", err)
  1346. resp.WriteHeader(401)
  1347. resp.Write([]byte(`{"success": false}`))
  1348. return
  1349. }
  1350. location := strings.Split(request.URL.String(), "/")
  1351. var workflowId string
  1352. if location[1] == "api" {
  1353. if len(location) <= 4 {
  1354. resp.WriteHeader(401)
  1355. resp.Write([]byte(`{"success": false}`))
  1356. return
  1357. }
  1358. workflowId = location[4]
  1359. }
  1360. if len(workflowId) != 32 {
  1361. resp.WriteHeader(401)
  1362. resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
  1363. return
  1364. }
  1365. // FIXME - check basic authentication
  1366. body, err := ioutil.ReadAll(request.Body)
  1367. if err != nil {
  1368. log.Printf("Error with body read: %s", err)
  1369. resp.WriteHeader(401)
  1370. resp.Write([]byte(`{"success": false}`))
  1371. return
  1372. }
  1373. log.Println(jsonPrettyPrint(string(body)))
  1374. var hook shuffle.Hook
  1375. err = json.Unmarshal(body, &hook)
  1376. if err != nil {
  1377. log.Printf("Failed unmarshaling: %s", err)
  1378. resp.WriteHeader(401)
  1379. resp.Write([]byte(`{"success": false}`))
  1380. return
  1381. }
  1382. if user.Id != hook.Owner && user.Role != "admin" && user.Role != "scheduler" {
  1383. log.Printf("Wrong user (%s) for hook %s", user.Username, hook.Id)
  1384. resp.WriteHeader(401)
  1385. resp.Write([]byte(`{"success": false}`))
  1386. return
  1387. }
  1388. if hook.Id != workflowId {
  1389. errorstring := fmt.Sprintf(`Id %s != %s`, hook.Id, workflowId)
  1390. log.Printf("Ids not matching: %s", errorstring)
  1391. resp.WriteHeader(401)
  1392. resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "%s"}`, errorstring)))
  1393. return
  1394. }
  1395. // Verifies the hook JSON. Bad verification :^)
  1396. finished, errorstring := verifyHook(hook)
  1397. if !finished {
  1398. log.Printf("Error with hook: %s", errorstring)
  1399. resp.WriteHeader(401)
  1400. resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "%s"}`, errorstring)))
  1401. return
  1402. }
  1403. // Get the ID to see whether it exists
  1404. // FIXME - use return and set READONLY fields (don't allow change from User)
  1405. ctx := context.Background()
  1406. _, err = shuffle.GetHook(ctx, workflowId)
  1407. if err != nil {
  1408. log.Printf("[WARNING] Failed getting hook %s (set): %s", workflowId, err)
  1409. resp.WriteHeader(401)
  1410. resp.Write([]byte(`{"success": false, "message": "Invalid ID"}`))
  1411. return
  1412. }
  1413. // Update the fields
  1414. err = shuffle.SetHook(ctx, hook)
  1415. if err != nil {
  1416. log.Printf("Failed setting hook: %s", err)
  1417. resp.WriteHeader(401)
  1418. resp.Write([]byte(`{"success": false}`))
  1419. return
  1420. }
  1421. resp.WriteHeader(200)
  1422. resp.Write([]byte(`{"success": true}`))
  1423. }
  1424. // FIXME - some fields (e.g. status) shouldn't be writeable.. Meh
  1425. func verifyHook(hook shuffle.Hook) (bool, string) {
  1426. // required fields: Id, info.name, type, status, running
  1427. if hook.Id == "" {
  1428. return false, "Missing required field id"
  1429. }
  1430. if hook.Info.Name == "" {
  1431. return false, "Missing required field info.name"
  1432. }
  1433. // Validate type stuff
  1434. validTypes := []string{"webhook"}
  1435. found := false
  1436. for _, key := range validTypes {
  1437. if hook.Type == key {
  1438. found = true
  1439. break
  1440. }
  1441. }
  1442. if !found {
  1443. return false, fmt.Sprintf("Field type is invalid. Allowed: %s", strings.Join(validTypes, ", "))
  1444. }
  1445. // WEbhook specific
  1446. if hook.Type == "webhook" {
  1447. if hook.Info.Url == "" {
  1448. return false, "Missing required field info.url"
  1449. }
  1450. }
  1451. if hook.Status == "" {
  1452. return false, "Missing required field status"
  1453. }
  1454. validStatusFields := []string{"running", "stopped", "uninitialized"}
  1455. found = false
  1456. for _, key := range validStatusFields {
  1457. if hook.Status == key {
  1458. found = true
  1459. break
  1460. }
  1461. }
  1462. if !found {
  1463. return false, fmt.Sprintf("Field status is invalid. Allowed: %s", strings.Join(validStatusFields, ", "))
  1464. }
  1465. // Verify actions
  1466. if len(hook.Actions) > 0 {
  1467. existingIds := []string{}
  1468. for index, action := range hook.Actions {
  1469. if action.Type == "" {
  1470. return false, fmt.Sprintf("Missing required field actions.type at index %d", index)
  1471. }
  1472. if action.Name == "" {
  1473. return false, fmt.Sprintf("Missing required field actions.name at index %d", index)
  1474. }
  1475. if action.Id == "" {
  1476. return false, fmt.Sprintf("Missing required field actions.id at index %d", index)
  1477. }
  1478. // Check for duplicate IDs
  1479. for _, actionId := range existingIds {
  1480. if action.Id == actionId {
  1481. return false, fmt.Sprintf("actions.id %s at index %d already exists", actionId, index)
  1482. }
  1483. }
  1484. existingIds = append(existingIds, action.Id)
  1485. }
  1486. }
  1487. return true, "All items set"
  1488. //log.Printf("%#v", hook)
  1489. //Id string `json:"id" datastore:"id"`
  1490. //Info Info `json:"info" datastore:"info"`
  1491. //Transforms struct{} `json:"transforms" datastore:"transforms"`
  1492. //Actions []HookAction `json:"actions" datastore:"actions"`
  1493. //Type string `json:"type" datastore:"type"`
  1494. //Status string `json:"status" datastore:"status"`
  1495. //Running bool `json:"running" datastore:"running"`
  1496. }
  1497. func setSpecificSchedule(resp http.ResponseWriter, request *http.Request) {
  1498. cors := shuffle.HandleCors(resp, request)
  1499. if cors {
  1500. return
  1501. }
  1502. location := strings.Split(request.URL.String(), "/")
  1503. var workflowId string
  1504. if location[1] == "api" {
  1505. if len(location) <= 4 {
  1506. resp.WriteHeader(401)
  1507. resp.Write([]byte(`{"success": false}`))
  1508. return
  1509. }
  1510. workflowId = location[4]
  1511. }
  1512. if len(workflowId) != 32 {
  1513. resp.WriteHeader(401)
  1514. resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
  1515. return
  1516. }
  1517. // FIXME - check basic authentication
  1518. body, err := ioutil.ReadAll(request.Body)
  1519. if err != nil {
  1520. log.Printf("Error with body read: %s", err)
  1521. resp.WriteHeader(401)
  1522. resp.Write([]byte(`{"success": false}`))
  1523. return
  1524. }
  1525. jsonPrettyPrint(string(body))
  1526. var schedule shuffle.ScheduleOld
  1527. err = json.Unmarshal(body, &schedule)
  1528. if err != nil {
  1529. log.Printf("Failed unmarshaling: %s", err)
  1530. resp.WriteHeader(401)
  1531. resp.Write([]byte(`{"success": false}`))
  1532. return
  1533. }
  1534. // FIXME - check access etc
  1535. ctx := context.Background()
  1536. err = shuffle.SetSchedule(ctx, schedule)
  1537. if err != nil {
  1538. log.Printf("Failed setting schedule: %s", err)
  1539. resp.WriteHeader(401)
  1540. resp.Write([]byte(`{"success": false}`))
  1541. return
  1542. }
  1543. // FIXME - get some real data?
  1544. resp.WriteHeader(200)
  1545. resp.Write([]byte(`{"success": true}`))
  1546. return
  1547. }
  1548. func getSpecificWebhook(resp http.ResponseWriter, request *http.Request) {
  1549. cors := shuffle.HandleCors(resp, request)
  1550. if cors {
  1551. return
  1552. }
  1553. location := strings.Split(request.URL.String(), "/")
  1554. var workflowId string
  1555. if location[1] == "api" {
  1556. if len(location) <= 4 {
  1557. resp.WriteHeader(401)
  1558. resp.Write([]byte(`{"success": false}`))
  1559. return
  1560. }
  1561. workflowId = location[4]
  1562. }
  1563. if len(workflowId) != 32 {
  1564. resp.WriteHeader(401)
  1565. resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
  1566. return
  1567. }
  1568. ctx := context.Background()
  1569. // FIXME: Schedule = trigger?
  1570. schedule, err := shuffle.GetSchedule(ctx, workflowId)
  1571. if err != nil {
  1572. log.Printf("Failed setting schedule: %s", err)
  1573. resp.WriteHeader(401)
  1574. resp.Write([]byte(`{"success": false}`))
  1575. return
  1576. }
  1577. //log.Printf("%#v", schedule.Translator[0])
  1578. b, err := json.Marshal(schedule)
  1579. if err != nil {
  1580. log.Printf("Failed marshalling: %s", err)
  1581. resp.WriteHeader(401)
  1582. resp.Write([]byte(`{"success": false}`))
  1583. return
  1584. }
  1585. // FIXME - get some real data?
  1586. resp.WriteHeader(200)
  1587. resp.Write([]byte(b))
  1588. return
  1589. }
  1590. // Starts a new webhook
  1591. func handleDeleteSchedule(resp http.ResponseWriter, request *http.Request) {
  1592. cors := shuffle.HandleCors(resp, request)
  1593. if cors {
  1594. return
  1595. }
  1596. user, err := shuffle.HandleApiAuthentication(resp, request)
  1597. if err != nil {
  1598. log.Printf("[WARNING] Api authentication failed in set new workflowhandler: %s", err)
  1599. resp.WriteHeader(401)
  1600. resp.Write([]byte(`{"success": false}`))
  1601. return
  1602. }
  1603. // FIXME: IAM - Get workflow and check owner
  1604. if user.Role != "admin" {
  1605. resp.WriteHeader(401)
  1606. resp.Write([]byte(`{"success": false, "reason": "Admin required"}`))
  1607. return
  1608. }
  1609. location := strings.Split(request.URL.String(), "/")
  1610. var workflowId string
  1611. if location[1] == "api" {
  1612. if len(location) <= 4 {
  1613. resp.WriteHeader(401)
  1614. resp.Write([]byte(`{"success": false}`))
  1615. return
  1616. }
  1617. workflowId = location[4]
  1618. }
  1619. if len(workflowId) != 32 {
  1620. resp.WriteHeader(401)
  1621. resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
  1622. return
  1623. }
  1624. ctx := context.Background()
  1625. err = shuffle.DeleteKey(ctx, "schedules", workflowId)
  1626. if err != nil {
  1627. resp.WriteHeader(401)
  1628. resp.Write([]byte(`{"success": false, "message": "Can't delete"}`))
  1629. return
  1630. }
  1631. // FIXME - remove schedule too
  1632. resp.WriteHeader(200)
  1633. resp.Write([]byte(`{"success": true, "message": "Deleted webhook"}`))
  1634. }
  1635. // Starts a new webhook
  1636. func handleNewSchedule(resp http.ResponseWriter, request *http.Request) {
  1637. cors := shuffle.HandleCors(resp, request)
  1638. if cors {
  1639. return
  1640. }
  1641. randomValue := uuid.NewV4()
  1642. h := md5.New()
  1643. io.WriteString(h, randomValue.String())
  1644. newId := strings.ToLower(fmt.Sprintf("%X", h.Sum(nil)))
  1645. // FIXME - timestamp!
  1646. // FIXME - applocation - cloud function?
  1647. timeNow := int64(time.Now().Unix())
  1648. schedule := shuffle.ScheduleOld{
  1649. Id: newId,
  1650. AppInfo: shuffle.AppInfo{},
  1651. BaseAppLocation: "/home/frikky/git/shaffuru/tmp/apps",
  1652. CreationTime: timeNow,
  1653. LastModificationtime: timeNow,
  1654. LastRuntime: timeNow,
  1655. }
  1656. ctx := context.Background()
  1657. err := shuffle.SetSchedule(ctx, schedule)
  1658. if err != nil {
  1659. log.Printf("Failed setting hook: %s", err)
  1660. resp.WriteHeader(401)
  1661. resp.Write([]byte(`{"success": false}`))
  1662. return
  1663. }
  1664. log.Println("Generating new schedule")
  1665. resp.WriteHeader(200)
  1666. resp.Write([]byte(`{"success": true, "message": "Created new service"}`))
  1667. }
  1668. // Does the webhook
  1669. func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) {
  1670. // 1. Get callback data
  1671. // 2. Load the configuration
  1672. // 3. Execute the workflow
  1673. //cors := shuffle.HandleCors(resp, request)
  1674. //if cors {
  1675. // return
  1676. //}
  1677. log.Printf("[DEBUG] HOOKS: webhook callback: %s", request.URL.String())
  1678. if request.Method != "POST" {
  1679. request.Method = "POST"
  1680. }
  1681. if request.Body == nil {
  1682. stringReader := strings.NewReader("")
  1683. request.Body = ioutil.NopCloser(stringReader)
  1684. }
  1685. path := strings.Split(request.URL.String(), "/")
  1686. if len(path) < 4 {
  1687. log.Printf("[DEBUG] HOOKS: Invalid webhook path: %s", request.URL.String())
  1688. resp.WriteHeader(403)
  1689. resp.Write([]byte(`{"success": false}`))
  1690. return
  1691. }
  1692. // 1. Get config with hookId
  1693. //fmt.Sprintf("%s/api/v1/hooks/%s", callbackUrl, hookId)
  1694. ctx := context.Background()
  1695. location := strings.Split(request.URL.String(), "/")
  1696. var hookId string
  1697. var queries string
  1698. if location[1] == "api" {
  1699. if len(location) <= 4 {
  1700. log.Printf("[INFO] Couldn't handle location. Too short in webhook: %d", len(location))
  1701. resp.WriteHeader(400)
  1702. resp.Write([]byte(`{"success": false}`))
  1703. return
  1704. }
  1705. hookId = location[4]
  1706. }
  1707. if strings.Contains(hookId, "?") {
  1708. splitter := strings.Split(hookId, "?")
  1709. hookId = splitter[0]
  1710. if len(splitter) > 1 {
  1711. queries = splitter[1]
  1712. }
  1713. }
  1714. log.Printf("[DEBUG] HOOKS: Pre user agent check")
  1715. // Find user agent header
  1716. userAgent := request.Header.Get("User-Agent")
  1717. if strings.Contains(strings.ToLower(userAgent), "microsoftpreview") || strings.Contains(strings.ToLower(userAgent), "googlebot") {
  1718. log.Printf("[AUDIT] Blocking googlebot and microsoftbot for webhooks. UA: '%s'", userAgent)
  1719. resp.WriteHeader(400)
  1720. resp.Write([]byte(`{"success": false, "reason": "Google/Microsoft preview bots not allowed. Please change the useragent."}`))
  1721. return
  1722. }
  1723. // ID: webhook_<UID>
  1724. if len(hookId) != 44 {
  1725. log.Printf("[INFO] Couldn't handle hookId. Too short in webhook: %d", len(hookId))
  1726. resp.WriteHeader(401)
  1727. resp.Write([]byte(`{"success": false, "reason": "Hook ID not valid"}`))
  1728. return
  1729. }
  1730. hookId = hookId[8:len(hookId)]
  1731. //log.Printf("HookID: %s", hookId)
  1732. hook, err := shuffle.GetHook(ctx, hookId)
  1733. if err != nil {
  1734. log.Printf("[WARNING] HOOKS: Failed getting hook %s (callback): %s", hookId, err)
  1735. resp.WriteHeader(400)
  1736. resp.Write([]byte(`{"success": false}`))
  1737. return
  1738. }
  1739. //log.Printf("HOOK FOUND: %#v", hook)
  1740. // Execute the workflow
  1741. //executeWorkflow(resp, request)
  1742. //resp.WriteHeader(200)
  1743. //resp.Write([]byte(`{"success": true}`))
  1744. if hook.Status == "stopped" {
  1745. log.Printf("[WARNING] HOOKS: Not running %s because hook status is stopped", hook.Id)
  1746. resp.WriteHeader(400)
  1747. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The webhook isn't running. Is it running?"}`)))
  1748. return
  1749. }
  1750. if len(hook.Workflows) == 0 {
  1751. log.Printf("[DEBUG] HOOKS: Not running because hook isn't connected to any workflows")
  1752. resp.WriteHeader(400)
  1753. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflows are defined"}`)))
  1754. return
  1755. }
  1756. if hook.Environment == "cloud" {
  1757. log.Printf("[DEBUG] HOOKS: This should trigger in the cloud. Duplicate action allowed onprem.")
  1758. }
  1759. // Check auth
  1760. if len(hook.Auth) > 0 {
  1761. err = shuffle.CheckHookAuth(request, hook.Auth)
  1762. if err != nil {
  1763. log.Printf("[WARNING] Failed auth for hook %s: %s", hook.Id, err)
  1764. resp.WriteHeader(401)
  1765. resp.Write([]byte(`{"success": false, "reason": "Bad authentication headers"}`))
  1766. return
  1767. }
  1768. }
  1769. body, err := ioutil.ReadAll(request.Body)
  1770. if err != nil {
  1771. log.Printf("[DEBUG] HOOKS: data read error: %s", err)
  1772. resp.WriteHeader(401)
  1773. resp.Write([]byte(`{"success": false}`))
  1774. return
  1775. }
  1776. if len(queries) > 0 && len(body) == 0 {
  1777. body = []byte(queries)
  1778. }
  1779. //log.Printf("BODY: %s", parsedBody)
  1780. // This is a specific fix for MSteams and may fix other things as well
  1781. // Scared whether it may stop other things though, but that's a future problem
  1782. // (famous last words)
  1783. //log.Printf("\n\nPARSEDBODY: %s", parsedBody)
  1784. parsedBody := shuffle.GetExecutionbody(body)
  1785. newBody := shuffle.ExecutionStruct{
  1786. Start: hook.Start,
  1787. ExecutionSource: "webhook",
  1788. ExecutionArgument: parsedBody,
  1789. }
  1790. if len(hook.Workflows) == 1 {
  1791. workflow, err := shuffle.GetWorkflow(ctx, hook.Workflows[0], true)
  1792. if err == nil {
  1793. for _, branch := range workflow.Branches {
  1794. if branch.SourceID == hook.Id {
  1795. log.Printf("[DEBUG] Found ID %s for hook", hook.Id)
  1796. if branch.DestinationID != hook.Start {
  1797. newBody.Start = branch.DestinationID
  1798. break
  1799. }
  1800. }
  1801. }
  1802. }
  1803. }
  1804. b, err := json.Marshal(newBody)
  1805. if err != nil {
  1806. log.Printf("[ERROR] HOOKS: Failed newBody marshaling for webhook: %s", err)
  1807. resp.WriteHeader(500)
  1808. resp.Write([]byte(`{"success": false}`))
  1809. return
  1810. }
  1811. // Should wrap the response input Body as well?
  1812. for _, item := range hook.Workflows {
  1813. log.Printf("[INFO] Running webhook for workflow %s with startnode %s", item, hook.Start)
  1814. // This ID is empty to force it to get the webhook within the execution
  1815. workflow := shuffle.Workflow{
  1816. ID: "",
  1817. }
  1818. if len(hook.Start) == 0 {
  1819. log.Printf("[ERROR] HOOKS: No start node for hook %s - running with workflow default.", hook.Id)
  1820. //bodyWrapper = string(parsedBody)
  1821. }
  1822. newRequest := &http.Request{
  1823. URL: &url.URL{},
  1824. Method: "POST",
  1825. Body: ioutil.NopCloser(bytes.NewReader(b)),
  1826. }
  1827. // OrgId: activeOrgs[0].Id,
  1828. workflowExecution, executionResp, err := handleExecution(item, workflow, newRequest, hook.OrgId)
  1829. if err == nil {
  1830. if hook.Version == "v2" {
  1831. timeout := 15
  1832. //if hook.VersionTimeout != 0 {
  1833. // timeout = hook.VersionTimeout
  1834. //}
  1835. log.Printf("[DEBUG] Waiting for Webhook response from %s for max %d seconds! Checking every 1 second. Hook ID: %s", workflowExecution.ExecutionId, timeout, hook.Id)
  1836. // Try every second for 15 seconds
  1837. for i := 0; i < timeout; i++ {
  1838. time.Sleep(1 * time.Second)
  1839. newExec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId)
  1840. if err != nil {
  1841. log.Printf("[ERROR] Failed to get workflow execution: %s", err)
  1842. break
  1843. }
  1844. if newExec.Status != "EXECUTING" {
  1845. log.Printf("[INFO] Got response from webhook v2 of length '%d' <- %s", len(newExec.Result), newExec.ExecutionId)
  1846. resp.WriteHeader(200)
  1847. resp.Write([]byte(newExec.Result))
  1848. return
  1849. }
  1850. }
  1851. }
  1852. // Fallback
  1853. resp.WriteHeader(200)
  1854. if len(hook.CustomResponse) > 0 {
  1855. resp.Write([]byte(hook.CustomResponse))
  1856. } else {
  1857. resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s"}`, workflowExecution.ExecutionId)))
  1858. }
  1859. return
  1860. }
  1861. resp.WriteHeader(500)
  1862. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp)))
  1863. }
  1864. log.Printf("[ERROR] HOOKS: END OF FUNCTION FOR '%s'. IF this is reached, something went wrong.", hook.Id)
  1865. resp.WriteHeader(500)
  1866. resp.Write([]byte(`{"success": false, "reason": "Failed to run workflow. Check logs."}`))
  1867. }
  1868. func handlePipelineCallback(resp http.ResponseWriter, request *http.Request) {
  1869. if request.Method != "POST" {
  1870. request.Method = "POST"
  1871. }
  1872. if request.Body == nil {
  1873. stringReader := strings.NewReader("")
  1874. request.Body = ioutil.NopCloser(stringReader)
  1875. }
  1876. path := strings.Split(request.URL.String(), "/")
  1877. if len(path) < 4 {
  1878. resp.WriteHeader(403)
  1879. resp.Write([]byte(`{"success": false}`))
  1880. return
  1881. }
  1882. ctx := context.Background()
  1883. location := strings.Split(request.URL.String(), "/")
  1884. var pipelineId string
  1885. if location[1] == "api" {
  1886. if len(location) <= 4 {
  1887. log.Printf("[INFO] Couldn't handle location. Too short in pipeline: %d", len(location))
  1888. resp.WriteHeader(401)
  1889. resp.Write([]byte(`{"success": false}`))
  1890. return
  1891. }
  1892. pipelineId = location[4]
  1893. }
  1894. userAgent := request.Header.Get("User-Agent")
  1895. if strings.Contains(strings.ToLower(userAgent), "microsoftpreview") || strings.Contains(strings.ToLower(userAgent), "googlebot") {
  1896. log.Printf("[AUDIT] Blocking googlebot and microsoftbot for pipelines. UA: '%s'", userAgent)
  1897. resp.WriteHeader(400)
  1898. resp.Write([]byte(`{"success": false, "reason": "Google/Microsoft preview bots not allowed. Please change the useragent."}`))
  1899. return
  1900. }
  1901. if len(pipelineId) != 45 {
  1902. log.Printf("[INFO] Couldn't handle pipeline. Too short in pipeline: %d", len(pipelineId))
  1903. resp.WriteHeader(401)
  1904. resp.Write([]byte(`{"success": false, "reason": "pipeline ID not valid"}`))
  1905. return
  1906. }
  1907. pipelineId = pipelineId[9:]
  1908. pipeline, err := shuffle.GetPipeline(ctx, pipelineId)
  1909. if err != nil {
  1910. log.Printf("[WARNING] Failed getting pipeline %s (callback): %s", pipelineId, err)
  1911. resp.WriteHeader(401)
  1912. resp.Write([]byte(`{"success": false}`))
  1913. return
  1914. }
  1915. if pipeline.Status != "running" {
  1916. log.Printf("[WARNING] Not running %s because pipeline status is %s", pipeline.TriggerId, pipeline.Status)
  1917. resp.WriteHeader(401)
  1918. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The pipeline isn't running"}`)))
  1919. return
  1920. }
  1921. if pipeline.WorkflowId == "" {
  1922. log.Printf("[DEBUG] Not running because pipeline isn't connected to any workflows")
  1923. resp.WriteHeader(401)
  1924. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflows are defined"}`)))
  1925. return
  1926. }
  1927. body, err := ioutil.ReadAll(request.Body)
  1928. if err != nil {
  1929. log.Printf("[DEBUG] Body data error: %s", err)
  1930. resp.WriteHeader(401)
  1931. resp.Write([]byte(`{"success": false}`))
  1932. return
  1933. }
  1934. // Parse concatenated JSON logs
  1935. jsonList, err := parseConcatenatedJSONLogs(string(body))
  1936. if err != nil {
  1937. log.Printf("[DEBUG] JSON parsing error: %s", err)
  1938. resp.WriteHeader(401)
  1939. resp.Write([]byte(`{"success": false}`))
  1940. return
  1941. }
  1942. parsedBody, err := json.Marshal(jsonList)
  1943. if err != nil {
  1944. log.Printf("[ERROR] Failed to marshal jsonList: %s", err)
  1945. resp.WriteHeader(500)
  1946. resp.Write([]byte(`{"success": false}`))
  1947. return
  1948. }
  1949. newBody := shuffle.ExecutionStruct{
  1950. Start: pipeline.StartNode,
  1951. ExecutionSource: "pipeline",
  1952. ExecutionArgument: string(parsedBody),
  1953. }
  1954. workflow, err := shuffle.GetWorkflow(ctx, pipeline.WorkflowId)
  1955. if err == nil {
  1956. for _, branch := range workflow.Branches {
  1957. if branch.SourceID == pipeline.TriggerId {
  1958. log.Printf("[DEBUG] Found ID %s for pipeline", pipeline.TriggerId)
  1959. if branch.DestinationID != pipeline.StartNode {
  1960. newBody.Start = branch.DestinationID
  1961. break
  1962. }
  1963. }
  1964. }
  1965. }
  1966. b, err := json.Marshal(newBody)
  1967. if err != nil {
  1968. log.Printf("[ERROR] Failed newBody marshaling for pipeline: %s", err)
  1969. resp.WriteHeader(500)
  1970. resp.Write([]byte(`{"success": false}`))
  1971. return
  1972. }
  1973. log.Printf("[INFO] Running pipeline for workflow %s with startnode %s", pipeline.WorkflowId, pipeline.StartNode)
  1974. newWorkflow := shuffle.Workflow{
  1975. ID: "",
  1976. }
  1977. if len(pipeline.StartNode) == 0 {
  1978. log.Printf("[WARNING] No start node for pipeline %s - running with workflow default.")
  1979. }
  1980. newRequest := &http.Request{
  1981. URL: &url.URL{},
  1982. Method: "POST",
  1983. Body: ioutil.NopCloser(bytes.NewReader(b)),
  1984. }
  1985. workflowExecution, executionResp, err := handleExecution(pipeline.WorkflowId, newWorkflow, newRequest, pipeline.OrgId)
  1986. if err == nil {
  1987. resp.WriteHeader(200)
  1988. resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s"}`, workflowExecution.ExecutionId)))
  1989. // Track Sigma rules
  1990. trackSigmaRules(ctx, pipeline.OrgId, jsonList)
  1991. return
  1992. }
  1993. resp.WriteHeader(500)
  1994. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp)))
  1995. }
  1996. func parseConcatenatedJSONLogs(logs string) ([]map[string]interface{}, error) {
  1997. var jsonList []map[string]interface{}
  1998. decoder := json.NewDecoder(strings.NewReader(logs))
  1999. for decoder.More() {
  2000. var jsonObject map[string]interface{}
  2001. if err := decoder.Decode(&jsonObject); err != nil {
  2002. log.Printf("[WARNING] JSON decoding error: %s. Skipping this object.", err)
  2003. continue
  2004. }
  2005. jsonList = append(jsonList, jsonObject)
  2006. }
  2007. if err := decoder.Decode(&struct{}{}); err != io.EOF {
  2008. return nil, fmt.Errorf("error after decoding all JSON objects: %v", err)
  2009. }
  2010. return jsonList, nil
  2011. }
  2012. func trackSigmaRules(ctx context.Context, orgId string, jsonList []map[string]interface{}) {
  2013. ruleCount := make(map[string]int)
  2014. for _, logEntry := range jsonList {
  2015. if rule, ok := logEntry["rule"].(map[string]interface{}); ok {
  2016. if ruleName, ok := rule["title"].(string); ok {
  2017. ruleCount[ruleName]++
  2018. }
  2019. }
  2020. }
  2021. for ruleName, count := range ruleCount {
  2022. shuffle.IncrementCache(ctx, orgId, ruleName, count)
  2023. log.Printf("[INFO] Rule %s incremented by %d", ruleName, count)
  2024. }
  2025. }
  2026. func executeCloudAction(action shuffle.CloudSyncJob, apikey string) error {
  2027. data, err := json.Marshal(action)
  2028. if err != nil {
  2029. log.Printf("Failed cloud webhook action marshalling: %s", err)
  2030. return err
  2031. }
  2032. syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync/handle_action", syncUrl)
  2033. client := shuffle.GetExternalClient(syncUrl)
  2034. req, err := http.NewRequest(
  2035. "POST",
  2036. syncUrl,
  2037. bytes.NewBuffer(data),
  2038. )
  2039. req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
  2040. newresp, err := client.Do(req)
  2041. if err != nil {
  2042. return err
  2043. }
  2044. defer newresp.Body.Close()
  2045. respBody, err := ioutil.ReadAll(newresp.Body)
  2046. if err != nil {
  2047. return err
  2048. }
  2049. type Result struct {
  2050. Success bool `json:"success"`
  2051. Reason string `json:"reason"`
  2052. }
  2053. //log.Printf("Data: %s", string(respBody))
  2054. responseData := Result{}
  2055. err = json.Unmarshal(respBody, &responseData)
  2056. if err != nil {
  2057. return err
  2058. }
  2059. if !responseData.Success {
  2060. return errors.New(fmt.Sprintf("Cloud error from Shuffler: %s", responseData.Reason))
  2061. }
  2062. log.Printf("[INFO] Cloud action executed successfully for '%s'", action.Action)
  2063. return nil
  2064. }
  2065. func getSpecificSchedule(resp http.ResponseWriter, request *http.Request) {
  2066. if request.Method != "GET" {
  2067. setSpecificSchedule(resp, request)
  2068. return
  2069. }
  2070. cors := shuffle.HandleCors(resp, request)
  2071. if cors {
  2072. return
  2073. }
  2074. location := strings.Split(request.URL.String(), "/")
  2075. var workflowId string
  2076. if location[1] == "api" {
  2077. if len(location) <= 4 {
  2078. resp.WriteHeader(401)
  2079. resp.Write([]byte(`{"success": false}`))
  2080. return
  2081. }
  2082. workflowId = location[4]
  2083. }
  2084. if len(workflowId) != 32 {
  2085. resp.WriteHeader(401)
  2086. resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
  2087. return
  2088. }
  2089. ctx := context.Background()
  2090. schedule, err := shuffle.GetSchedule(ctx, workflowId)
  2091. if err != nil {
  2092. log.Printf("Failed getting schedule: %s", err)
  2093. resp.WriteHeader(401)
  2094. resp.Write([]byte(`{"success": false}`))
  2095. return
  2096. }
  2097. //log.Printf("%#v", schedule.Translator[0])
  2098. b, err := json.Marshal(schedule)
  2099. if err != nil {
  2100. log.Printf("Failed marshalling: %s", err)
  2101. resp.WriteHeader(401)
  2102. resp.Write([]byte(`{"success": false}`))
  2103. return
  2104. }
  2105. resp.WriteHeader(200)
  2106. resp.Write([]byte(b))
  2107. }
  2108. func loadYaml(fileLocation string) (ApiYaml, error) {
  2109. apiYaml := ApiYaml{}
  2110. yamlFile, err := ioutil.ReadFile(fileLocation)
  2111. if err != nil {
  2112. log.Printf("yamlFile.Get err: %s", err)
  2113. return ApiYaml{}, err
  2114. }
  2115. err = yaml.Unmarshal([]byte(yamlFile), &apiYaml)
  2116. if err != nil {
  2117. return ApiYaml{}, err
  2118. }
  2119. return apiYaml, nil
  2120. }
  2121. // This should ALWAYS come from an OUTPUT
  2122. func executeSchedule(resp http.ResponseWriter, request *http.Request) {
  2123. cors := shuffle.HandleCors(resp, request)
  2124. if cors {
  2125. return
  2126. }
  2127. location := strings.Split(request.URL.String(), "/")
  2128. var workflowId string
  2129. if location[1] == "api" {
  2130. if len(location) <= 4 {
  2131. resp.WriteHeader(401)
  2132. resp.Write([]byte(`{"success": false}`))
  2133. return
  2134. }
  2135. workflowId = location[4]
  2136. }
  2137. if len(workflowId) != 32 {
  2138. resp.WriteHeader(401)
  2139. resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
  2140. return
  2141. }
  2142. ctx := context.Background()
  2143. log.Printf("[INFO] EXECUTING %s!", workflowId)
  2144. idConfig, err := shuffle.GetSchedule(ctx, workflowId)
  2145. if err != nil {
  2146. log.Printf("Error getting schedule: %s", err)
  2147. resp.WriteHeader(401)
  2148. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  2149. return
  2150. }
  2151. // Basically the src app
  2152. inputStrings := map[string]string{}
  2153. for _, item := range idConfig.Translator {
  2154. if item.Dst.Required == "false" {
  2155. log.Println("Skipping not required")
  2156. continue
  2157. }
  2158. if item.Src.Name == "" {
  2159. errorMsg := fmt.Sprintf("Required field %s has no source", item.Dst.Name)
  2160. log.Println(errorMsg)
  2161. resp.WriteHeader(401)
  2162. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, errorMsg)))
  2163. return
  2164. }
  2165. inputStrings[item.Dst.Name] = item.Src.Name
  2166. }
  2167. configmap := map[string]string{}
  2168. for _, config := range idConfig.AppInfo.SourceApp.Config {
  2169. configmap[config.Key] = config.Value
  2170. }
  2171. // FIXME - this wont work for everything lmao
  2172. functionName := strings.ToLower(idConfig.AppInfo.SourceApp.Action)
  2173. functionName = strings.Replace(functionName, " ", "_", 10)
  2174. cmdArgs := []string{
  2175. fmt.Sprintf("%s/%s/app.py", baseAppPath, "thehive"),
  2176. fmt.Sprintf("--referenceid=%s", workflowId),
  2177. fmt.Sprintf("--function=%s", functionName),
  2178. }
  2179. for key, value := range configmap {
  2180. cmdArgs = append(cmdArgs, fmt.Sprintf("--%s=%s", key, value))
  2181. }
  2182. // FIXME - processname
  2183. baseProcess := "python3"
  2184. log.Printf("Executing: %s %s", baseProcess, strings.Join(cmdArgs, " "))
  2185. execSubprocess(baseProcess, cmdArgs)
  2186. resp.WriteHeader(200)
  2187. resp.Write([]byte(`{"success": true}`))
  2188. }
  2189. func execSubprocess(cmdName string, cmdArgs []string) error {
  2190. cmd := exec.Command(cmdName, cmdArgs...)
  2191. cmdReader, err := cmd.StdoutPipe()
  2192. if err != nil {
  2193. fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err)
  2194. return err
  2195. }
  2196. scanner := bufio.NewScanner(cmdReader)
  2197. go func() {
  2198. for scanner.Scan() {
  2199. log.Printf("Out: %s\n", scanner.Text())
  2200. }
  2201. }()
  2202. err = cmd.Start()
  2203. if err != nil {
  2204. fmt.Fprintln(os.Stderr, "Error starting Cmd", err)
  2205. return err
  2206. }
  2207. err = cmd.Wait()
  2208. if err != nil {
  2209. fmt.Fprintln(os.Stderr, "Error waiting for Cmd", err)
  2210. return err
  2211. }
  2212. return nil
  2213. }
  2214. // This should ALWAYS come from an OUTPUT
  2215. func uploadWorkflowResult(resp http.ResponseWriter, request *http.Request) {
  2216. // Post to a key with random data?
  2217. location := strings.Split(request.URL.String(), "/")
  2218. var workflowId string
  2219. if location[1] == "api" {
  2220. if len(location) <= 4 {
  2221. resp.WriteHeader(401)
  2222. resp.Write([]byte(`{"success": false}`))
  2223. return
  2224. }
  2225. workflowId = location[4]
  2226. }
  2227. if len(workflowId) != 32 {
  2228. resp.WriteHeader(401)
  2229. resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
  2230. return
  2231. }
  2232. // FIXME - check if permission AND whether it exists
  2233. // FIXME - validate ID as well
  2234. ctx := context.Background()
  2235. schedule, err := shuffle.GetSchedule(ctx, workflowId)
  2236. if err != nil {
  2237. log.Printf("Failed setting schedule %s: %s", workflowId, err)
  2238. resp.WriteHeader(401)
  2239. resp.Write([]byte(`{"success": false}`))
  2240. return
  2241. }
  2242. // Should use generic interfaces and parse fields OR
  2243. // build temporary struct based on api.yaml of the app
  2244. data, err := parseWorkflowParameters(resp, request)
  2245. if err != nil {
  2246. log.Printf("Invalid params: %s", err)
  2247. resp.WriteHeader(401)
  2248. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  2249. return
  2250. }
  2251. // Get the actual fields
  2252. foldername := schedule.AppInfo.SourceApp.Foldername
  2253. curOutputType := schedule.AppInfo.SourceApp.Name
  2254. curOutputAppOutput := schedule.AppInfo.SourceApp.Action
  2255. curInputType := schedule.AppInfo.DestinationApp.Name
  2256. translatormap := schedule.Translator
  2257. if len(curOutputType) <= 0 {
  2258. log.Printf("Id %s is invalid. Missing sourceapp name", workflowId)
  2259. resp.WriteHeader(401)
  2260. resp.Write([]byte(fmt.Sprintf(`{"success": false}`)))
  2261. return
  2262. }
  2263. if len(foldername) == 0 {
  2264. foldername = strings.ToLower(curOutputType)
  2265. }
  2266. if len(curOutputAppOutput) <= 0 {
  2267. log.Printf("Id %s is invalid. Missing source output ", workflowId)
  2268. resp.WriteHeader(401)
  2269. resp.Write([]byte(fmt.Sprintf(`{"success": false}`)))
  2270. return
  2271. }
  2272. if len(curInputType) <= 0 {
  2273. log.Printf("Id %s is invalid. Missing destination name", workflowId)
  2274. resp.WriteHeader(401)
  2275. resp.Write([]byte(fmt.Sprintf(`{"success": false}`)))
  2276. return
  2277. }
  2278. // Needs to be used for parsing properly
  2279. // Might be dumb to have the yaml as a file too
  2280. yamlpath := fmt.Sprintf("%s/%s/api.yaml", baseAppPath, foldername)
  2281. curyaml, err := loadYaml(yamlpath)
  2282. if err != nil {
  2283. resp.WriteHeader(401)
  2284. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  2285. return
  2286. }
  2287. //validFields := []string{}
  2288. requiredFields := []string{}
  2289. optionalFields := []string{}
  2290. for _, output := range curyaml.Output {
  2291. if output.Name != curOutputAppOutput {
  2292. continue
  2293. }
  2294. for _, outputparam := range output.OutputParameters {
  2295. if outputparam.Required == "true" {
  2296. if outputparam.Schema.Type == "string" {
  2297. requiredFields = append(requiredFields, outputparam.Name)
  2298. } else {
  2299. log.Printf("Outputparam schematype %s is not implemented.", outputparam.Schema.Type)
  2300. }
  2301. } else {
  2302. optionalFields = append(optionalFields, outputparam.Name)
  2303. }
  2304. }
  2305. // Wont reach here unless it's the right one
  2306. break
  2307. }
  2308. // Checks whether ALL required fields are filled
  2309. for _, fieldname := range requiredFields {
  2310. if data[fieldname] == nil {
  2311. resp.WriteHeader(401)
  2312. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Field %s is required"}`, fieldname)))
  2313. return
  2314. } else {
  2315. log.Printf("%s: %s", fieldname, data[fieldname])
  2316. }
  2317. }
  2318. // FIXME
  2319. // Verify whether it can be sent from the source to destination here
  2320. // Save to DB or send it straight? Idk
  2321. // Use e.g. google pubsub if cloud and maybe kafka locally
  2322. // FIXME - add more types :)
  2323. sourcedatamap := map[string]string{}
  2324. for key, value := range data {
  2325. switch v := value.(type) {
  2326. case string:
  2327. sourcedatamap[key] = value.(string)
  2328. default:
  2329. log.Printf("unexpected type %T", v)
  2330. }
  2331. }
  2332. log.Println(data)
  2333. log.Println(requiredFields)
  2334. log.Println(translatormap)
  2335. log.Println(sourcedatamap)
  2336. outputmap := map[string]string{}
  2337. for _, translator := range translatormap {
  2338. if translator.Src.Type == "static" {
  2339. log.Printf("%s = %s", translator.Dst.Name, translator.Src.Value)
  2340. outputmap[translator.Dst.Name] = translator.Src.Value
  2341. } else {
  2342. log.Printf("%s = %s", translator.Dst.Name, translator.Src.Name)
  2343. outputmap[translator.Dst.Name] = sourcedatamap[translator.Src.Name]
  2344. }
  2345. }
  2346. configmap := map[string]string{}
  2347. for _, config := range schedule.AppInfo.DestinationApp.Config {
  2348. configmap[config.Key] = config.Value
  2349. }
  2350. // FIXME - add function to run
  2351. // FIXME - add reference somehow
  2352. // FIXME - add apikey somehow
  2353. // Just package and run really?
  2354. // FIXME - generate from sourceapp
  2355. outputmap["function"] = "create_alert"
  2356. cmdArgs := []string{
  2357. fmt.Sprintf("%s/%s/app.py", baseAppPath, foldername),
  2358. }
  2359. for key, value := range outputmap {
  2360. cmdArgs = append(cmdArgs, fmt.Sprintf("--%s=%s", key, value))
  2361. }
  2362. // COnfig map!
  2363. for key, value := range configmap {
  2364. cmdArgs = append(cmdArgs, fmt.Sprintf("--%s=%s", key, value))
  2365. }
  2366. outputmap["referenceid"] = workflowId
  2367. baseProcess := "python3"
  2368. log.Printf("Executing: %s %s", baseProcess, strings.Join(cmdArgs, " "))
  2369. execSubprocess(baseProcess, cmdArgs)
  2370. resp.WriteHeader(200)
  2371. resp.Write([]byte(`{"success": true}`))
  2372. }
  2373. //dst: {name: "title", required: "true", type: "string"}
  2374. //
  2375. //"title": "symptomDescription",
  2376. //"description": "detailedDescription",
  2377. //"type": "ticketType",
  2378. //"sourceRef": "ticketId"
  2379. //"name": "secureworks",
  2380. //"id": "e07910a06a086c83ba41827aa00b26ed",
  2381. //"description": "I AM SECUREWORKS DESC",
  2382. //"action": "Get Tickets",
  2383. //"config": {}
  2384. //"name": "thehive",
  2385. // "id": "e07910a06a086c83ba41827aa00b26ef",
  2386. // "description": "I AM thehive DESC",
  2387. // "action": "Add ticket",
  2388. // "config": [{
  2389. // "key": "http://localhost:9000",
  2390. // "value": "kZJmmn05j8wndOGDGvKg/D9eKub1itwO"
  2391. // }]
  2392. func findValidScheduleAppFolders(rootAppFolder string) ([]string, error) {
  2393. rootFiles, err := ioutil.ReadDir(rootAppFolder)
  2394. if err != nil {
  2395. return []string{}, err
  2396. }
  2397. invalidRootFiles := []string{}
  2398. invalidRootFolders := []string{}
  2399. invalidAppFolders := []string{}
  2400. validAppFolders := []string{}
  2401. // This is dumb
  2402. allowedLanguages := []string{"py", "go"}
  2403. for _, rootfile := range rootFiles {
  2404. if !rootfile.IsDir() {
  2405. invalidRootFiles = append(invalidRootFiles, rootfile.Name())
  2406. continue
  2407. }
  2408. appFolderLocation := fmt.Sprintf("%s/%s", rootAppFolder, rootfile.Name())
  2409. appFiles, err := ioutil.ReadDir(appFolderLocation)
  2410. if err != nil {
  2411. // Invalid app folder (deleted within a few MS lol)
  2412. invalidRootFolders = append(invalidRootFolders, rootfile.Name())
  2413. continue
  2414. }
  2415. yamlFileDone := false
  2416. appFileExists := false
  2417. for _, appfile := range appFiles {
  2418. if appfile.Name() == "api.yaml" {
  2419. err := validateAppYaml(
  2420. fmt.Sprintf("%s/%s", appFolderLocation, appfile.Name()),
  2421. )
  2422. if err != nil {
  2423. log.Printf("Error in %s: %s", fmt.Sprintf("%s/%s", rootfile.Name(), appfile.Name()), err)
  2424. break
  2425. }
  2426. log.Printf("YAML FOR %s: %s IS VALID!!", rootfile.Name(), appfile.Name())
  2427. yamlFileDone = true
  2428. }
  2429. for _, language := range allowedLanguages {
  2430. if appfile.Name() == fmt.Sprintf("app.%s", language) {
  2431. log.Printf("Appfile found for %s", rootfile.Name())
  2432. appFileExists = true
  2433. break
  2434. }
  2435. }
  2436. }
  2437. if !yamlFileDone || !appFileExists {
  2438. invalidAppFolders = append(invalidAppFolders, rootfile.Name())
  2439. } else {
  2440. validAppFolders = append(validAppFolders, rootfile.Name())
  2441. }
  2442. }
  2443. log.Printf("Invalid rootfiles: %s", strings.Join(invalidRootFiles, ", "))
  2444. log.Printf("Invalid rootfolders: %s", strings.Join(invalidRootFolders, ", "))
  2445. log.Printf("Invalid appfolders: %s", strings.Join(invalidAppFolders, ", "))
  2446. log.Printf("\n=== VALID appfolders ===\n* %s", strings.Join(validAppFolders, "\n"))
  2447. return validAppFolders, err
  2448. }
  2449. func validateInputOutputYaml(appType string, apiYaml ApiYaml) error {
  2450. if appType == "input" {
  2451. for index, input := range apiYaml.Input {
  2452. if input.Name == "" {
  2453. return errors.New(fmt.Sprintf("YAML field name doesn't exist in index %d of Input", index))
  2454. }
  2455. if input.Description == "" {
  2456. return errors.New(fmt.Sprintf("YAML field description doesn't exist in index %d of Input", index))
  2457. }
  2458. for paramindex, param := range input.InputParameters {
  2459. if param.Name == "" {
  2460. return errors.New(fmt.Sprintf("YAML field name doesn't exist in Input %s with index %d", input.Name, paramindex))
  2461. }
  2462. if param.Description == "" {
  2463. return errors.New(fmt.Sprintf("YAML field description doesn't exist in Input %s with index %d", input.Name, index))
  2464. }
  2465. if param.Schema.Type == "" {
  2466. return errors.New(fmt.Sprintf("YAML field schema.type doesn't exist in Input %s with index %d", input.Name, index))
  2467. }
  2468. }
  2469. }
  2470. }
  2471. return nil
  2472. }
  2473. func validateAppYaml(fileLocation string) error {
  2474. /*
  2475. Requires:
  2476. name, description, app_version, contact_info (name), types
  2477. */
  2478. apiYaml, err := loadYaml(fileLocation)
  2479. if err != nil {
  2480. return err
  2481. }
  2482. // Validate fields
  2483. if apiYaml.Name == "" {
  2484. return errors.New("YAML field name doesn't exist")
  2485. }
  2486. if apiYaml.Description == "" {
  2487. return errors.New("YAML field description doesn't exist")
  2488. }
  2489. if apiYaml.AppVersion == "" {
  2490. return errors.New("YAML field app_version doesn't exist")
  2491. }
  2492. if apiYaml.ContactInfo.Name == "" {
  2493. return errors.New("YAML field contact_info.name doesn't exist")
  2494. }
  2495. if len(apiYaml.Types) == 0 {
  2496. return errors.New("YAML field types doesn't exist")
  2497. }
  2498. // Validate types (input/ouput)
  2499. validTypes := []string{"input", "output"}
  2500. for _, appType := range apiYaml.Types {
  2501. // Validate in here lul
  2502. for _, validType := range validTypes {
  2503. if appType == validType {
  2504. err = validateInputOutputYaml(appType, apiYaml)
  2505. if err != nil {
  2506. return err
  2507. }
  2508. break
  2509. }
  2510. }
  2511. }
  2512. return nil
  2513. }
  2514. func setBadMemcache(ctx context.Context, path string) {
  2515. // Add to cache if it doesn't exist
  2516. //item := &memcache.Item{
  2517. // Key: path,
  2518. // Value: []byte(`{"success": false}`),
  2519. // Expiration: time.Minute * 60,
  2520. //}
  2521. //if err := memcache.Add(ctx, item); err == memcache.ErrNotStored {
  2522. // if err := memcache.Set(ctx, item); err != nil {
  2523. // log.Printf("Error setting item: %v", err)
  2524. // }
  2525. //} else if err != nil {
  2526. // log.Printf("error adding item: %v", err)
  2527. //} else {
  2528. // log.Printf("Set cache for %s", item.Key)
  2529. //}
  2530. }
  2531. type Result struct {
  2532. Success bool `json:"success"`
  2533. Reason string `json:"reason"`
  2534. List []string `json:"list"`
  2535. }
  2536. // r.HandleFunc("/api/v1/docs/{key}", getDocs).Methods("GET", "OPTIONS")
  2537. func getOpenapi(resp http.ResponseWriter, request *http.Request) {
  2538. cors := shuffle.HandleCors(resp, request)
  2539. if cors {
  2540. return
  2541. }
  2542. // Just here to verify that the user is logged in
  2543. _, err := shuffle.HandleApiAuthentication(resp, request)
  2544. if err != nil {
  2545. log.Printf("Api authentication failed in validate swagger: %s", err)
  2546. resp.WriteHeader(401)
  2547. resp.Write([]byte(`{"success": false}`))
  2548. return
  2549. }
  2550. location := strings.Split(request.URL.String(), "/")
  2551. var id string
  2552. if location[1] == "api" {
  2553. if len(location) <= 4 {
  2554. resp.WriteHeader(401)
  2555. resp.Write([]byte(`{"success": false}`))
  2556. return
  2557. }
  2558. id = location[4]
  2559. }
  2560. if len(id) != 32 {
  2561. resp.WriteHeader(401)
  2562. resp.Write([]byte(`{"success": false}`))
  2563. return
  2564. }
  2565. // FIXME - FIX AUTH WITH APP
  2566. ctx := context.Background()
  2567. //_, err = shuffle.GetApp(ctx, id)
  2568. //if err == nil {
  2569. // log.Println("You're supposed to be able to continue now.")
  2570. //}
  2571. parsedApi, err := shuffle.GetOpenApiDatastore(ctx, id)
  2572. if err != nil {
  2573. resp.WriteHeader(401)
  2574. resp.Write([]byte(`{"success": false}`))
  2575. return
  2576. }
  2577. log.Printf("[INFO] API LENGTH GET FOR OPENAPI %s: %d, ID: %s", id, len(parsedApi.Body), id)
  2578. parsedApi.Success = true
  2579. data, err := json.Marshal(parsedApi)
  2580. if err != nil {
  2581. resp.WriteHeader(422)
  2582. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed marshalling parsed swagger: %s"}`, err)))
  2583. return
  2584. }
  2585. resp.WriteHeader(200)
  2586. resp.Write(data)
  2587. }
  2588. func handleSwaggerValidation(body []byte) (shuffle.ParsedOpenApi, error) {
  2589. type versionCheck struct {
  2590. Swagger string `datastore:"swagger" json:"swagger" yaml:"swagger"`
  2591. SwaggerVersion string `datastore:"swaggerVersion" json:"swaggerVersion" yaml:"swaggerVersion"`
  2592. OpenAPI string `datastore:"openapi" json:"openapi" yaml:"openapi"`
  2593. }
  2594. //body = []byte(`swagger: "2.0"`)
  2595. //body = []byte(`swagger: '1.0'`)
  2596. //newbody := string(body)
  2597. //newbody = strings.TrimSpace(newbody)
  2598. //body = []byte(newbody)
  2599. //log.Println(string(body))
  2600. //tmpbody, err := yaml.YAMLToJSON(body)
  2601. //log.Println(err)
  2602. //log.Println(string(tmpbody))
  2603. // This has to be done in a weird way because Datastore doesn't
  2604. // support map[string]interface and similar (openapi3.Swagger)
  2605. var version versionCheck
  2606. parsed := shuffle.ParsedOpenApi{}
  2607. swaggerdata := []byte{}
  2608. idstring := ""
  2609. isJson := false
  2610. err := json.Unmarshal(body, &version)
  2611. if err != nil {
  2612. //log.Printf("Json err: %s", err)
  2613. err = yaml.Unmarshal(body, &version)
  2614. if err != nil {
  2615. log.Printf("[WARNING] Yaml error (1): %s", err)
  2616. } else {
  2617. //log.Printf("Successfully parsed YAML!")
  2618. }
  2619. } else {
  2620. isJson = true
  2621. //log.Printf("[DEBUG] Successfully parsed JSON!")
  2622. }
  2623. if len(version.SwaggerVersion) > 0 && len(version.Swagger) == 0 {
  2624. version.Swagger = version.SwaggerVersion
  2625. }
  2626. if strings.HasPrefix(version.Swagger, "3.") || strings.HasPrefix(version.OpenAPI, "3.") {
  2627. //log.Println("Handling v3 API")
  2628. swaggerLoader := openapi3.NewSwaggerLoader()
  2629. swaggerLoader.IsExternalRefsAllowed = true
  2630. swaggerv3, err := swaggerLoader.LoadSwaggerFromData(body)
  2631. if err != nil {
  2632. log.Printf("Failed parsing OpenAPI: %s", err)
  2633. return shuffle.ParsedOpenApi{}, err
  2634. }
  2635. swaggerdata, err = json.Marshal(swaggerv3)
  2636. if err != nil {
  2637. log.Printf("Failed unmarshaling v3 data: %s", err)
  2638. return shuffle.ParsedOpenApi{}, err
  2639. }
  2640. hasher := md5.New()
  2641. hasher.Write(swaggerdata)
  2642. idstring = hex.EncodeToString(hasher.Sum(nil))
  2643. } else { //strings.HasPrefix(version.Swagger, "2.") || strings.HasPrefix(version.OpenAPI, "2.") {
  2644. // Convert
  2645. //log.Println("Handling v2 API")
  2646. var swagger openapi2.Swagger
  2647. //log.Println(string(body))
  2648. err = json.Unmarshal(body, &swagger)
  2649. if err != nil {
  2650. //log.Printf("Json error? %s", err)
  2651. err = yaml.Unmarshal(body, &swagger)
  2652. if err != nil {
  2653. log.Printf("[WARNING] Yaml error (2): %s", err)
  2654. return shuffle.ParsedOpenApi{}, err
  2655. } else {
  2656. //log.Printf("Valid yaml!")
  2657. }
  2658. }
  2659. swaggerv3, err := openapi2conv.ToV3Swagger(&swagger)
  2660. if err != nil {
  2661. log.Printf("Failed converting from openapi2 to 3: %s", err)
  2662. return shuffle.ParsedOpenApi{}, err
  2663. }
  2664. swaggerdata, err = json.Marshal(swaggerv3)
  2665. if err != nil {
  2666. log.Printf("Failed unmarshaling v3 data: %s", err)
  2667. return shuffle.ParsedOpenApi{}, err
  2668. }
  2669. hasher := md5.New()
  2670. hasher.Write(swaggerdata)
  2671. idstring = hex.EncodeToString(hasher.Sum(nil))
  2672. }
  2673. if len(swaggerdata) > 0 {
  2674. body = swaggerdata
  2675. }
  2676. // Overwrite with new json data
  2677. _ = isJson
  2678. body = swaggerdata
  2679. // Parsing it to swagger 3
  2680. parsed = shuffle.ParsedOpenApi{
  2681. ID: idstring,
  2682. Body: string(body),
  2683. Success: true,
  2684. }
  2685. return parsed, err
  2686. }
  2687. func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, skipEdit bool) {
  2688. type Test struct {
  2689. Editing bool `json:"editing" datastore:"editing"`
  2690. Id string `json:"id" datastore:"id"`
  2691. Image string `json:"image" datastore:"image"`
  2692. Body string `json:"body" datastore:"body"`
  2693. }
  2694. var test Test
  2695. err := json.Unmarshal(body, &test)
  2696. if err != nil {
  2697. log.Printf("[ERROR] Failed unmarshalling in swagger build: %s", err)
  2698. resp.WriteHeader(400)
  2699. resp.Write([]byte(`{"success": false}`))
  2700. return
  2701. }
  2702. // Get an identifier
  2703. hasher := md5.New()
  2704. hasher.Write(body)
  2705. newmd5 := hex.EncodeToString(hasher.Sum(nil))
  2706. if test.Editing && len(user.Id) > 0 && skipEdit != true {
  2707. // Quick verification test
  2708. ctx := context.Background()
  2709. app, err := shuffle.GetApp(ctx, test.Id, user, false)
  2710. if err != nil {
  2711. log.Printf("[ERROR] Error getting app when editing: %s", app.Name)
  2712. resp.WriteHeader(400)
  2713. resp.Write([]byte(`{"success": false}`))
  2714. return
  2715. }
  2716. if user.Id == app.Owner || (user.Role == "admin" && user.ActiveOrg.Id == app.ReferenceOrg) || shuffle.ArrayContains(app.Contributors, user.Id) {
  2717. log.Printf("[DEBUG] Editing app %s with user %s (%s) in org %s", test.Id, user.Username, user.Id, user.ActiveOrg.Id)
  2718. } else {
  2719. log.Printf("[WARNING] Wrong user (%s) for app %s when verifying swagger", user.Username, app.Name)
  2720. resp.WriteHeader(403)
  2721. resp.Write([]byte(`{"success": false, "reason": "You don't have permissions to edit this app. Contact support@shuffler.io if this persists."}`))
  2722. return
  2723. }
  2724. log.Printf("[INFO] %s is EDITING APP WITH ID %s and md5 %s", user.Id, app.ID, newmd5)
  2725. newmd5 = app.ID
  2726. }
  2727. // Generate new app integration (bump version)
  2728. // Test = client side with fetch?
  2729. ctx := context.Background()
  2730. swaggerLoader := openapi3.NewSwaggerLoader()
  2731. swaggerLoader.IsExternalRefsAllowed = true
  2732. swagger, err := swaggerLoader.LoadSwaggerFromData(body)
  2733. if err != nil {
  2734. log.Printf("[ERROR] Swagger validation error: %s", err)
  2735. resp.WriteHeader(500)
  2736. resp.Write([]byte(`{"success": false, "reason": "Failed verifying openapi"}`))
  2737. return
  2738. }
  2739. if swagger.Info == nil {
  2740. log.Printf("[ERORR] Info is nil in swagger?")
  2741. resp.WriteHeader(500)
  2742. resp.Write([]byte(`{"success": false, "reason": "Info not parsed"}`))
  2743. return
  2744. }
  2745. swagger.Info.Title = shuffle.FixFunctionName(swagger.Info.Title, swagger.Info.Title, false)
  2746. if strings.Contains(swagger.Info.Title, " ") {
  2747. swagger.Info.Title = strings.Replace(swagger.Info.Title, " ", "_", -1)
  2748. }
  2749. basePath, err := shuffle.BuildStructure(swagger, newmd5)
  2750. if err != nil {
  2751. log.Printf("[WARNING] Failed to build base structure: %s", err)
  2752. resp.WriteHeader(500)
  2753. resp.Write([]byte(`{"success": false, "reason": "Failed building baseline structure"}`))
  2754. return
  2755. }
  2756. //log.Printf("Should generate yaml")
  2757. swagger, api, pythonfunctions, err := shuffle.GenerateYaml(swagger, newmd5)
  2758. if err != nil {
  2759. log.Printf("[WARNING] Failed building and generating yaml (buildapp): %s", err)
  2760. resp.WriteHeader(500)
  2761. resp.Write([]byte(`{"success": false, "reason": "Failed building and parsing yaml"}`))
  2762. return
  2763. }
  2764. // FIXME: CHECK IF SAME NAME AS NORMAL APP
  2765. // Can't overwrite existing normal app
  2766. workflowApps, err := shuffle.GetPrioritizedApps(ctx, user)
  2767. if err != nil {
  2768. log.Printf("[WARNING] Failed getting all workflow apps from database to verify: %s", err)
  2769. resp.WriteHeader(401)
  2770. resp.Write([]byte(`{"success": false, "reason": "Failed to verify existence"}`))
  2771. return
  2772. }
  2773. // Same name only?
  2774. lowerName := strings.ToLower(swagger.Info.Title)
  2775. for _, app := range workflowApps {
  2776. if app.Downloaded && !app.Generated && strings.ToLower(app.Name) == lowerName {
  2777. resp.WriteHeader(401)
  2778. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Normal app with name %s already exists. Delete it first."}`, swagger.Info.Title)))
  2779. return
  2780. }
  2781. }
  2782. if api.Owner == "" {
  2783. api.Owner = user.Id
  2784. }
  2785. if len(api.ReferenceOrg) == 0 {
  2786. api.ReferenceOrg = user.ActiveOrg.Id
  2787. }
  2788. if len(test.Image) > 0 {
  2789. api.SmallImage = test.Image
  2790. api.LargeImage = test.Image
  2791. }
  2792. err = shuffle.DumpApi(basePath, api)
  2793. if err != nil {
  2794. log.Printf("[WARNING] Failed dumping yaml: %s", err)
  2795. resp.WriteHeader(500)
  2796. resp.Write([]byte(`{"success": false, "reason": "Failed dumping yaml"}`))
  2797. return
  2798. }
  2799. identifier := fmt.Sprintf("%s-%s", swagger.Info.Title, newmd5)
  2800. classname := strings.Replace(identifier, " ", "", -1)
  2801. classname = strings.Replace(classname, "-", "", -1)
  2802. parsedCode, err := shuffle.DumpPython(basePath, classname, swagger.Info.Version, pythonfunctions)
  2803. if err != nil {
  2804. log.Printf("Failed dumping python: %s", err)
  2805. resp.WriteHeader(500)
  2806. resp.Write([]byte(`{"success": false, "reason": "Failed dumping appcode"}`))
  2807. return
  2808. }
  2809. identifier = strings.Replace(identifier, " ", "-", -1)
  2810. identifier = strings.Replace(identifier, "_", "-", -1)
  2811. log.Printf("[INFO] Successfully parsed %s. Proceeding to docker container", identifier)
  2812. // Now that the baseline is setup, we need to make it into a cloud function
  2813. // 1. Upload the API to datastore for use
  2814. // 2. Get code from baseline/app_base.py & baseline/static_baseline.py
  2815. // 3. Stitch code together from these two + our new app
  2816. // 4. Zip the folder to cloud storage
  2817. // 5. Upload as cloud function
  2818. // 1. Upload the API to datastore
  2819. err = shuffle.DeployAppToDatastore(ctx, api)
  2820. //func DeployAppToDatastore(ctx context.Context, workflowapp WorkflowApp, bucketName string) error {
  2821. if err != nil {
  2822. log.Printf("[ERROR] Failed adding app to db: %s", err)
  2823. resp.WriteHeader(500)
  2824. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed adding app to db: %s"}`, err)))
  2825. return
  2826. }
  2827. // 2. Get all the required code
  2828. appbase, staticBaseline, err := shuffle.GetAppbase()
  2829. if err != nil {
  2830. log.Printf("[ERROR] Failed getting appbase: %s", err)
  2831. resp.WriteHeader(500)
  2832. resp.Write([]byte(`{"success": false, "reason": "Failed getting appbase code"}`))
  2833. return
  2834. }
  2835. // Have to do some quick checks of the python code (:
  2836. _, parsedCode = shuffle.FormatAppfile(parsedCode)
  2837. fixedAppbase := shuffle.FixAppbase(appbase)
  2838. runner := shuffle.GetRunnerOnprem(classname)
  2839. // 2. Put it together
  2840. stitched := string(staticBaseline) + strings.Join(fixedAppbase, "\n") + parsedCode + string(runner)
  2841. //log.Println(stitched)
  2842. // 3. Zip and stream it directly in the directory
  2843. _, err = shuffle.StreamZipdata(ctx, identifier, stitched, shuffle.GetAppRequirements(), "")
  2844. if err != nil {
  2845. log.Printf("[ERROR] Zipfile error: %s", err)
  2846. resp.WriteHeader(500)
  2847. resp.Write([]byte(`{"success": false, "reason": "Failed to build zipfile"}`))
  2848. return
  2849. }
  2850. log.Printf("[INFO] Successfully stitched ZIPFILE for %s", identifier)
  2851. // Copy baseline Dockerfile to build directory
  2852. dockerfileSource := "../app_gen/python-lib/baseline/Dockerfile"
  2853. dockerfileDestination := fmt.Sprintf("%s/Dockerfile", basePath)
  2854. // Read and copy the baseline Dockerfile
  2855. dockerfileContent, err := ioutil.ReadFile(dockerfileSource)
  2856. if err != nil {
  2857. foundDockerfile := shuffle.GetBaseDockerfile()
  2858. if len(foundDockerfile) > 0 {
  2859. dockerfileContent = foundDockerfile
  2860. } else {
  2861. log.Printf("[ERROR] Failed to read baseline Dockerfile: %s", err)
  2862. resp.WriteHeader(500)
  2863. resp.Write([]byte(`{"success": false, "reason": "Failed to read baseline Dockerfile"}`))
  2864. return
  2865. }
  2866. }
  2867. err = ioutil.WriteFile(dockerfileDestination, dockerfileContent, 0644)
  2868. if err != nil {
  2869. log.Printf("[ERROR] Failed to copy Dockerfile to build directory: %s", err)
  2870. resp.WriteHeader(500)
  2871. resp.Write([]byte(`{"success": false, "reason": "Failed to copy Dockerfile"}`))
  2872. return
  2873. }
  2874. // Verify the Dockerfile was created
  2875. if _, err := os.Stat(dockerfileDestination); os.IsNotExist(err) {
  2876. log.Printf("[ERROR] Dockerfile does not exist at destination: %s", dockerfileDestination)
  2877. resp.WriteHeader(500)
  2878. resp.Write([]byte(`{"success": false, "reason": "Dockerfile was not created properly"}`))
  2879. return
  2880. }
  2881. // 4. Build the image locally.
  2882. // FIXME: Should be moved to a local docker registry
  2883. dockerLocation := fmt.Sprintf("%s/Dockerfile", basePath)
  2884. log.Printf("[INFO] Dockerfile: %s", dockerLocation)
  2885. versionName := fmt.Sprintf("%s_%s", strings.ToLower(strings.ReplaceAll(api.Name, " ", "-")), api.AppVersion)
  2886. dockerTags := []string{
  2887. fmt.Sprintf("%s:%s", baseDockerName, identifier),
  2888. fmt.Sprintf("%s:%s", baseDockerName, versionName),
  2889. }
  2890. found := false
  2891. foundNumber := 0
  2892. log.Printf("[INFO] Checking for api with ID %s", newmd5)
  2893. for appCounter, app := range user.PrivateApps {
  2894. if app.ID == api.ID {
  2895. found = true
  2896. foundNumber = appCounter
  2897. break
  2898. } else if app.Name == api.Name && app.AppVersion == api.AppVersion {
  2899. found = true
  2900. foundNumber = appCounter
  2901. break
  2902. } else if app.PrivateID == test.Id && test.Editing {
  2903. found = true
  2904. foundNumber = appCounter
  2905. break
  2906. }
  2907. }
  2908. // Updating the user with the new app so that it can easily be retrieved
  2909. if !found {
  2910. user.PrivateApps = append(user.PrivateApps, api)
  2911. } else {
  2912. user.PrivateApps[foundNumber] = api
  2913. }
  2914. if len(user.Id) > 0 {
  2915. err = shuffle.SetUser(ctx, &user, true)
  2916. if err != nil {
  2917. log.Printf("[ERROR] Failed adding verification for user %s: %s", user.Username, err)
  2918. //resp.WriteHeader(500)
  2919. //resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Failed updating user"}`)))
  2920. //return
  2921. }
  2922. }
  2923. //log.Printf("DO I REACH HERE WHEN SAVING?")
  2924. parsed := shuffle.ParsedOpenApi{
  2925. ID: newmd5,
  2926. Body: string(body),
  2927. }
  2928. if !shuffle.ArrayContains(api.Contributors, user.Id) {
  2929. api.Contributors = append(api.Contributors, user.Id)
  2930. }
  2931. shuffle.SetAppRevision(ctx, api)
  2932. log.Printf("[INFO] API LENGTH FOR %s: %d, ID: %s", api.Name, len(parsed.Body), newmd5)
  2933. // FIXME: Might cause versioning issues if we re-use the same!!
  2934. // FIXME: Need a way to track different versions of the same app properly.
  2935. // Hint: Save API.id somewhere, and use newmd5 to save latest version
  2936. if len(user.Id) > 0 {
  2937. err = shuffle.SetOpenApiDatastore(ctx, newmd5, parsed)
  2938. if err != nil {
  2939. log.Printf("[ERROR] Failed saving app %s to database: %s", newmd5, err)
  2940. resp.WriteHeader(500)
  2941. resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "%"}`, err)))
  2942. return
  2943. }
  2944. shuffle.SetOpenApiDatastore(ctx, api.ID, parsed)
  2945. } else {
  2946. //log.Printf("
  2947. }
  2948. // Backup every single one
  2949. /*
  2950. err = increaseStatisticsField(ctx, "total_apps_created", newmd5, 1, user.ActiveOrg.Id)
  2951. if err != nil {
  2952. log.Printf("Failed to increase success execution stats: %s", err)
  2953. }
  2954. err = increaseStatisticsField(ctx, "openapi_apps_created", newmd5, 1, user.ActiveOrg.Id)
  2955. if err != nil {
  2956. log.Printf("Failed to increase success execution stats: %s", err)
  2957. }
  2958. */
  2959. cacheKey := fmt.Sprintf("workflowapps-sorted-100")
  2960. shuffle.DeleteCache(ctx, cacheKey)
  2961. cacheKey = fmt.Sprintf("workflowapps-sorted-500")
  2962. shuffle.DeleteCache(ctx, cacheKey)
  2963. cacheKey = fmt.Sprintf("workflowapps-sorted-1000")
  2964. shuffle.DeleteCache(ctx, cacheKey)
  2965. shuffle.DeleteCache(ctx, fmt.Sprintf("apps_%s", user.Id))
  2966. // Doing this last to ensure we can copy the docker image over
  2967. // even though builds fail
  2968. err = buildImage(dockerTags, dockerLocation)
  2969. if err != nil {
  2970. log.Printf("[ERROR] Docker build error: %s", err)
  2971. resp.WriteHeader(500)
  2972. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Error in Docker build: %s"}`, err)))
  2973. return
  2974. }
  2975. if len(user.ActiveOrg.Id) > 0 {
  2976. org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
  2977. if err != nil {
  2978. log.Printf("[ERROR] Failed getting org during image build (%s): %s", user.ActiveOrg.Id, err)
  2979. } else {
  2980. log.Printf("[INFO] Successfully uploaded app %s to org %s (2). Validating and distributing image to available environments in org.", api.ID, org.Id)
  2981. imagenames := []string{
  2982. fmt.Sprintf("%s_%s", api.Name, api.AppVersion),
  2983. fmt.Sprintf("%s_%s", api.Name, test.Id),
  2984. }
  2985. err = shuffle.DistributeAppToEnvironments(ctx, *org, imagenames)
  2986. if err != nil {
  2987. log.Printf("[ERROR] Failed distributing app to environments: %s", err)
  2988. }
  2989. }
  2990. }
  2991. log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID)
  2992. if len(user.Id) > 0 {
  2993. resp.WriteHeader(200)
  2994. resp.Write([]byte(fmt.Sprintf(`{"success": true, "id": "%s"}`, api.ID)))
  2995. }
  2996. org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
  2997. if err != nil {
  2998. log.Printf("[ERROR] Failed getting org during image build (%s): %s", user.ActiveOrg.Id, err)
  2999. } else {
  3000. imagenames := []string{
  3001. fmt.Sprintf("%s_%s", api.Name, api.AppVersion),
  3002. fmt.Sprintf("%s_%s", api.Name, api.ID),
  3003. }
  3004. err = shuffle.DistributeAppToEnvironments(ctx, *org, imagenames)
  3005. if err != nil {
  3006. log.Printf("[ERROR] Failed distributing app to environments: %s", err)
  3007. }
  3008. }
  3009. }
  3010. // Creates an app from the app builder
  3011. func verifySwagger(resp http.ResponseWriter, request *http.Request) {
  3012. cors := shuffle.HandleCors(resp, request)
  3013. if cors {
  3014. return
  3015. }
  3016. //log.Printf("[INFO] TRY TO SET APP TO LIVE!!!")
  3017. user, err := shuffle.HandleApiAuthentication(resp, request)
  3018. if err != nil {
  3019. log.Printf("Api authentication failed in verify swagger: %s", err)
  3020. resp.WriteHeader(401)
  3021. resp.Write([]byte(`{"success": false}`))
  3022. return
  3023. }
  3024. if user.Role == "org-reader" {
  3025. log.Printf("[WARNING] Org-reader doesn't have access to check swagger doc: %s (%s)", user.Username, user.Id)
  3026. resp.WriteHeader(401)
  3027. resp.Write([]byte(`{"success": false, "reason": "Read only user"}`))
  3028. return
  3029. }
  3030. body, err := ioutil.ReadAll(request.Body)
  3031. if err != nil {
  3032. resp.WriteHeader(401)
  3033. resp.Write([]byte(`{"success": false, "reason": "Failed reading body"}`))
  3034. return
  3035. }
  3036. buildSwaggerApp(resp, body, user, false)
  3037. }
  3038. // Hotloads new apps from a folder
  3039. func handleAppHotload(ctx context.Context, location string, forceUpdate bool) error {
  3040. basepath := "base"
  3041. fs, err := shuffle.CreateFs(basepath, location)
  3042. if err != nil {
  3043. log.Printf("Failed memfs creation - probably bad path: %s", err)
  3044. return errors.New(fmt.Sprintf("Failed to find directory %s", location))
  3045. } else {
  3046. log.Printf("[INFO] Memfs creation from %s done", location)
  3047. }
  3048. dir, err := fs.ReadDir("")
  3049. if err != nil {
  3050. log.Printf("[WARNING] Failed reading folder: %s", err)
  3051. return err
  3052. }
  3053. _, _, err = IterateAppGithubFolders(ctx, fs, dir, "", "", forceUpdate, false)
  3054. if err != nil {
  3055. log.Printf("[WARNING] Githubfolders error: %s", err)
  3056. return err
  3057. }
  3058. cacheKey := fmt.Sprintf("workflowapps-sorted")
  3059. shuffle.DeleteCache(ctx, cacheKey)
  3060. cacheKey = fmt.Sprintf("workflowapps-sorted-100")
  3061. shuffle.DeleteCache(ctx, cacheKey)
  3062. cacheKey = fmt.Sprintf("workflowapps-sorted-500")
  3063. shuffle.DeleteCache(ctx, cacheKey)
  3064. cacheKey = fmt.Sprintf("workflowapps-sorted-1000")
  3065. shuffle.DeleteCache(ctx, cacheKey)
  3066. //shuffle.DeleteCache(ctx, fmt.Sprintf("apps_%s", user.Id))
  3067. return nil
  3068. }
  3069. func handleCloudExecutionOnprem(workflowId, startNode, executionSource, executionArgument string) error {
  3070. ctx := context.Background()
  3071. // 1. Get the workflow
  3072. // 2. Execute it with the data
  3073. workflow, err := shuffle.GetWorkflow(ctx, workflowId)
  3074. if err != nil {
  3075. return err
  3076. }
  3077. // FIXME: Handle auth
  3078. _ = workflow
  3079. parsedArgument := executionArgument
  3080. newExec := shuffle.ExecutionRequest{
  3081. ExecutionSource: executionSource,
  3082. ExecutionArgument: parsedArgument,
  3083. }
  3084. var execution shuffle.ExecutionRequest
  3085. err = json.Unmarshal([]byte(parsedArgument), &execution)
  3086. if err == nil {
  3087. //log.Printf("[INFO] FOUND EXEC %#v", execution)
  3088. if len(execution.ExecutionArgument) > 0 {
  3089. parsedArgument := strings.Replace(string(execution.ExecutionArgument), "\\\"", "\"", -1)
  3090. log.Printf("New exec argument: %s", execution.ExecutionArgument)
  3091. if strings.HasPrefix(parsedArgument, "{") && strings.HasSuffix(parsedArgument, "}") {
  3092. log.Printf("\nData is most likely JSON from %s\n", newExec.ExecutionSource)
  3093. }
  3094. newExec.ExecutionArgument = parsedArgument
  3095. }
  3096. } else {
  3097. log.Printf("Unmarshal issue: %s", err)
  3098. }
  3099. if len(startNode) > 0 {
  3100. newExec.Start = startNode
  3101. }
  3102. b, err := json.Marshal(newExec)
  3103. if err != nil {
  3104. log.Printf("Failed marshal")
  3105. return err
  3106. }
  3107. //log.Println(string(b))
  3108. newRequest := &http.Request{
  3109. URL: &url.URL{},
  3110. Method: "POST",
  3111. Body: ioutil.NopCloser(bytes.NewReader(b)),
  3112. }
  3113. _, _, err = handleExecution(workflowId, shuffle.Workflow{}, newRequest, workflow.OrgId)
  3114. return err
  3115. }
  3116. func handleCloudJob(job shuffle.CloudSyncJob) error {
  3117. ctx := context.Background()
  3118. // May need authentication in all of these..?
  3119. log.Printf("[INFO] Handle job with type %s and action %s", job.Type, job.Action)
  3120. shuffle.IncrementCache(ctx, job.OrgId, "org_sync_actions")
  3121. if job.Type == "outlook" {
  3122. if job.Action == "execute" {
  3123. // FIXME: Get the email
  3124. ctx := context.Background()
  3125. maildata := shuffle.MailDataOutlook{}
  3126. err := json.Unmarshal([]byte(job.ThirdItem), &maildata)
  3127. if err != nil {
  3128. log.Printf("Maildata unmarshal error: %s", err)
  3129. return err
  3130. }
  3131. hookId := job.Id
  3132. hook, err := shuffle.GetTriggerAuth(ctx, hookId)
  3133. if err != nil {
  3134. log.Printf("[INFO] Failed getting trigger %s (callback cloud): %s", hookId, err)
  3135. return err
  3136. }
  3137. backendPort := os.Getenv("BACKEND_PORT")
  3138. if backendPort == "" {
  3139. backendPort = "5001"
  3140. }
  3141. redirectDomain := fmt.Sprintf("localhost:%s", backendPort)
  3142. redirectUrl := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain)
  3143. outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", hook.OauthToken, redirectUrl)
  3144. if err != nil {
  3145. log.Printf("Oauth client failure - triggerauth: %s", err)
  3146. return err
  3147. }
  3148. emails, err := shuffle.GetOutlookEmail(outlookClient, maildata)
  3149. //log.Printf("EMAILS: %d", len(emails))
  3150. //log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err)
  3151. //type FullEmail struct {
  3152. email := shuffle.FullEmail{}
  3153. if len(emails) == 1 {
  3154. email = emails[0]
  3155. }
  3156. emailBytes, err := json.Marshal(email)
  3157. if err != nil {
  3158. log.Printf("[INFO] Failed email marshaling: %s", err)
  3159. return err
  3160. }
  3161. log.Printf("[INFO] Should handle outlook webhook for workflow %s with start node %s and data of length %d", job.PrimaryItemId, job.SecondaryItem, len(job.ThirdItem))
  3162. err = handleCloudExecutionOnprem(job.PrimaryItemId, job.SecondaryItem, "outlook", string(emailBytes))
  3163. if err != nil {
  3164. log.Printf("[WARNING] Failed executing workflow from cloud outlook hook: %s", err)
  3165. } else {
  3166. log.Printf("[INFO] Successfully executed workflow from cloud outlook hook!")
  3167. }
  3168. }
  3169. } else if job.Type == "webhook" {
  3170. if job.Action == "execute" {
  3171. log.Printf("[INFO] Should handle normal webhook for workflow %s with start node %s and data %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
  3172. err := handleCloudExecutionOnprem(job.PrimaryItemId, job.SecondaryItem, "webhook", job.ThirdItem)
  3173. if err != nil {
  3174. log.Printf("[INFO] Failed executing workflow from cloud hook: %s", err)
  3175. } else {
  3176. log.Printf("[INFO] Successfully executed workflow from cloud hook!")
  3177. }
  3178. }
  3179. } else if job.Type == "schedule" {
  3180. if job.Action == "execute" {
  3181. log.Printf("[INFO] Should handle schedule for workflow %s with start node %s and data %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
  3182. err := handleCloudExecutionOnprem(job.PrimaryItemId, job.SecondaryItem, "schedule", job.ThirdItem)
  3183. if err != nil {
  3184. log.Printf("[INFO] Failed executing workflow from cloud schedule: %s", err)
  3185. } else {
  3186. log.Printf("[INFO] Successfully executed workflow from cloud schedule")
  3187. }
  3188. }
  3189. } else if job.Type == "email_trigger" {
  3190. if job.Action == "execute" {
  3191. log.Printf("[INFO] Should handle email for workflow %s with start node %s and data %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
  3192. err := handleCloudExecutionOnprem(job.PrimaryItemId, job.SecondaryItem, "email_trigger", job.ThirdItem)
  3193. if err != nil {
  3194. log.Printf("Failed executing workflow from email trigger: %s", err)
  3195. } else {
  3196. log.Printf("Successfully executed workflow from cloud email trigger")
  3197. }
  3198. }
  3199. } else if job.Type == "user_input" {
  3200. if job.Action == "continue" {
  3201. log.Printf("[INFO] Should handle user_input CONTINUE for workflow %s with start node %s and execution ID %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
  3202. // FIXME: Handle authorization
  3203. ctx := context.Background()
  3204. workflowExecution, err := shuffle.GetWorkflowExecution(ctx, job.ThirdItem)
  3205. if err != nil {
  3206. return err
  3207. }
  3208. if job.PrimaryItemId != workflowExecution.Workflow.ID {
  3209. return errors.New("Bad workflow ID when stopping execution.")
  3210. }
  3211. workflowExecution.Status = "EXECUTING"
  3212. err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
  3213. if err != nil {
  3214. return err
  3215. }
  3216. fullUrl := fmt.Sprintf("%s/api/v1/workflows/%s/execute?authorization=%s&start=%s&reference_execution=%s&answer=true", syncUrl, job.PrimaryItemId, job.FourthItem, job.SecondaryItem, job.ThirdItem)
  3217. newRequest, err := http.NewRequest(
  3218. "GET",
  3219. fullUrl,
  3220. nil,
  3221. )
  3222. if err != nil {
  3223. log.Printf("Failed continuing workflow in request builder: %s", err)
  3224. return err
  3225. }
  3226. _, _, err = handleExecution(job.PrimaryItemId, shuffle.Workflow{}, newRequest, job.OrgId)
  3227. if err != nil {
  3228. log.Printf("Failed continuing workflow from cloud user_input: %s", err)
  3229. return err
  3230. } else {
  3231. log.Printf("Successfully executed workflow from cloud user_input")
  3232. }
  3233. } else if job.Action == "stop" {
  3234. log.Printf("Should handle user_input STOP for workflow %s with start node %s and execution ID %s", job.PrimaryItemId, job.SecondaryItem, job.ThirdItem)
  3235. ctx := context.Background()
  3236. workflowExecution, err := shuffle.GetWorkflowExecution(ctx, job.ThirdItem)
  3237. if err != nil {
  3238. return err
  3239. }
  3240. if job.PrimaryItemId != workflowExecution.Workflow.ID {
  3241. return errors.New("Bad workflow ID when stopping execution.")
  3242. }
  3243. /*
  3244. if job.FourthItem != workflowExecution.Authorization {
  3245. return errors.New("Bad authorization when stopping execution.")
  3246. }
  3247. */
  3248. newResults := []shuffle.ActionResult{}
  3249. for _, result := range workflowExecution.Results {
  3250. if result.Action.AppName == "User Input" && result.Result == "Waiting for user feedback based on configuration" {
  3251. result.Status = "ABORTED"
  3252. result.Result = "Aborted manually by user."
  3253. }
  3254. newResults = append(newResults, result)
  3255. }
  3256. workflowExecution.Results = newResults
  3257. workflowExecution.Status = "ABORTED"
  3258. err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
  3259. if err != nil {
  3260. return err
  3261. }
  3262. log.Printf("Successfully updated user input to aborted.")
  3263. }
  3264. } else {
  3265. log.Printf("No handler for type %s and action %s", job.Type, job.Action)
  3266. }
  3267. return nil
  3268. }
  3269. // Handles jobs from remote (cloud)
  3270. func remoteOrgJobController(org shuffle.Org, body []byte) error {
  3271. type retStruct struct {
  3272. Success bool `json:"success"`
  3273. Reason string `json:"reason"`
  3274. Jobs []shuffle.CloudSyncJob `json:"jobs"`
  3275. SyncFeatures shuffle.SyncFeatures `json:"sync_features"`
  3276. Subscriptions []shuffle.PaymentSubscription `json:"subscriptions"`
  3277. Licensed bool `json:"licensed"`
  3278. CloudSyncUrl string `json:"cloud_sync_url,omitempty"`
  3279. }
  3280. responseData := retStruct{}
  3281. err := json.Unmarshal(body, &responseData)
  3282. if err != nil {
  3283. return err
  3284. }
  3285. ctx := context.Background()
  3286. if !responseData.Success {
  3287. log.Printf("[WARNING] Should stop org job controller because no success?")
  3288. if strings.Contains(strings.ToLower(responseData.Reason), "bad apikey") || strings.Contains(responseData.Reason, "Error getting the organization") || strings.Contains(responseData.Reason, "Organization isn't syncing") {
  3289. log.Printf("[WARNING] Remote error; Bad apikey or org error. Stopping sync for org: %s", responseData.Reason)
  3290. if value, exists := scheduledOrgs[org.Id]; exists {
  3291. // Looks like this does the trick? Hurr
  3292. log.Printf("[INFO] STOPPING ORG SCHEDULE for: %s", org.Id)
  3293. value.Lock()
  3294. } else {
  3295. log.Printf("[INFO] Failed finding the schedule for org %s (%s)", org.Name, org.Id)
  3296. }
  3297. org, err := shuffle.GetOrg(ctx, org.Id)
  3298. if err != nil {
  3299. log.Printf("[WARNING] Failed finding org %s: %s", org.Id, err)
  3300. return err
  3301. }
  3302. // Just in case
  3303. org, err = handleStopCloudSync(syncUrl, *org)
  3304. if err != nil {
  3305. log.Printf("[ERROR] Failed stopping cloud sync remotely: %s", err)
  3306. }
  3307. org.SyncConfig.Interval = 0
  3308. org.CloudSync = false
  3309. org.SyncConfig.Apikey = ""
  3310. startDate := time.Now().Unix()
  3311. org.SyncFeatures.Webhook = shuffle.SyncData{Active: false, Type: "trigger", Name: "Webhook", StartDate: startDate}
  3312. org.SyncFeatures.UserInput = shuffle.SyncData{Active: false, Type: "trigger", Name: "User Input", StartDate: startDate}
  3313. org.SyncFeatures.EmailTrigger = shuffle.SyncData{Active: false, Type: "action", Name: "Email Trigger", StartDate: startDate}
  3314. org.SyncFeatures.Schedules = shuffle.SyncData{Active: false, Type: "trigger", Name: "Schedule", StartDate: startDate, Limit: 0}
  3315. org.SyncFeatures.SendMail = shuffle.SyncData{Active: false, Type: "action", Name: "Send Email", StartDate: startDate, Limit: 0}
  3316. org.SyncFeatures.SendSms = shuffle.SyncData{Active: false, Type: "action", Name: "Send SMS", StartDate: startDate, Limit: 0}
  3317. org.CloudSyncActive = false
  3318. err = shuffle.SetOrg(ctx, *org, org.Id)
  3319. if err != nil {
  3320. log.Printf("[WARNING] Failed setting organization when stopping sync: %s", err)
  3321. } else {
  3322. log.Printf("[INFO] Successfully STOPPED org cloud sync for %s (%s)", org.Name, org.Id)
  3323. }
  3324. return nil
  3325. }
  3326. return errors.New("[ERROR] Remote job handler issues.")
  3327. }
  3328. if len(responseData.Jobs) > 0 {
  3329. //log.Printf("[INFO] Remote JOB ret: %s", string(body))
  3330. log.Printf("Got job with reason %s and %d job(s)", responseData.Reason, len(responseData.Jobs))
  3331. }
  3332. cacheKey := fmt.Sprintf("org_sync_features_%s", org.Id)
  3333. featuresBytes, err := json.Marshal(responseData.SyncFeatures)
  3334. if err != nil {
  3335. log.Printf("[ERROR] Failed to marshal SyncFeatures for cache: %s", err)
  3336. } else {
  3337. shuffle.SetCache(ctx, cacheKey, featuresBytes, 1800)
  3338. }
  3339. subscriptionCacheKey := fmt.Sprintf("org_subscriptions_%s", org.Id)
  3340. subscriptionsBytes, err := json.Marshal(responseData.Subscriptions)
  3341. if err != nil {
  3342. log.Printf("[ERROR] Failed to marshal Subscriptions for cache: %s", err)
  3343. } else {
  3344. shuffle.SetCache(ctx, subscriptionCacheKey, subscriptionsBytes, 1800)
  3345. }
  3346. licenseCacheKey := fmt.Sprintf("org_licensed_%s", org.Id)
  3347. licensedBytes, err := json.Marshal(responseData.Licensed)
  3348. if err != nil {
  3349. log.Printf("[ERROR] Failed to marshal Licensed for cache: %s", err)
  3350. } else {
  3351. shuffle.SetCache(ctx, licenseCacheKey, licensedBytes, 1800)
  3352. }
  3353. for _, job := range responseData.Jobs {
  3354. err = handleCloudJob(job)
  3355. if err != nil {
  3356. log.Printf("[ERROR] Failed job from cloud: %s", err)
  3357. }
  3358. }
  3359. return nil
  3360. }
  3361. func remoteOrgJobHandler(org shuffle.Org, interval int) error {
  3362. // Check if it's 1 in 10 (10% chance random)
  3363. backupJob := shuffle.BackupJob{}
  3364. // Check if workflow backup is active
  3365. // Check if app backup is active
  3366. ctx := context.Background()
  3367. foundUser := org.Users[0]
  3368. for _, user := range org.Users {
  3369. if user.Role == "admin" {
  3370. foundUser = user
  3371. break
  3372. }
  3373. }
  3374. // Check if it's 1/20 times (600 seconds - 10 min on average)
  3375. // Only problem: May take time to sync the first time, which is annoying
  3376. // This is to ensure that we don't spam the shuffle cloud servers with a lot of data
  3377. shouldBackupData := false
  3378. randomNumber := rand.Intn(20)
  3379. if randomNumber == 0 {
  3380. shouldBackupData = true
  3381. }
  3382. // Just to prevent it from spamming large outbound requests
  3383. if shouldBackupData {
  3384. if org.SyncConfig.WorkflowBackup {
  3385. workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser, 250, "")
  3386. if err != nil {
  3387. log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err)
  3388. } else {
  3389. backupJob.Workflows = workflows
  3390. }
  3391. }
  3392. if org.SyncConfig.AppBackup && len(org.Users) > 0 {
  3393. foundUser.ActiveOrg.Id = org.Id
  3394. apps, err := shuffle.GetPrioritizedApps(ctx, foundUser)
  3395. if err != nil {
  3396. log.Printf("[ERROR] Failed getting backup apps for org %s: %s", org.Id, err)
  3397. } else {
  3398. parsedApps := []shuffle.WorkflowApp{}
  3399. for _, app := range apps {
  3400. if len(app.Actions) == 0 {
  3401. continue
  3402. }
  3403. if !app.Generated {
  3404. continue
  3405. }
  3406. parsedApps = append(parsedApps, app)
  3407. }
  3408. backupJob.Apps = parsedApps
  3409. }
  3410. }
  3411. // Send stats once every 10 times or so..?
  3412. // For now, just send every time
  3413. info, err := shuffle.GetOrgStatistics(ctx, org.Id)
  3414. if err != nil {
  3415. log.Printf("[ERROR] Failed getting org statistics backup for org %s: %s", org.Id, err)
  3416. } else {
  3417. backupJob.Stats = *info
  3418. }
  3419. }
  3420. backupJobData, err := json.Marshal(backupJob)
  3421. if err != nil {
  3422. log.Printf("[ERROR] Failed marshalling backup job: %s", err)
  3423. backupJobData = []byte{}
  3424. }
  3425. cloudSyncRegionUrlCacheKey := fmt.Sprintf("org_cloudsync_region_url_%s", org.Id)
  3426. cloudSyncRegionUrlCached, err := shuffle.GetCache(ctx, cloudSyncRegionUrlCacheKey)
  3427. if err == nil {
  3428. if cachedBytes, ok := cloudSyncRegionUrlCached.([]byte); ok && len(cachedBytes) > 0 {
  3429. syncUrl = string(cachedBytes)
  3430. log.Printf("[DEBUG] Using cached cloud sync region url for org %s: %s", org.Id, syncUrl)
  3431. }
  3432. }
  3433. if len(syncUrl) == 0 || !strings.HasPrefix(syncUrl, "http") {
  3434. syncUrl = "https://shuffler.io"
  3435. }
  3436. syncUrl := fmt.Sprintf("%s/api/v1/cloud/sync", syncUrl)
  3437. client := shuffle.GetExternalClient(syncUrl)
  3438. req, err := http.NewRequest(
  3439. "POST",
  3440. syncUrl,
  3441. bytes.NewBuffer(backupJobData),
  3442. )
  3443. req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey))
  3444. newresp, err := client.Do(req)
  3445. if err != nil {
  3446. //log.Printf("Failed request in org sync: %s", err)
  3447. return err
  3448. }
  3449. defer newresp.Body.Close()
  3450. respBody, err := ioutil.ReadAll(newresp.Body)
  3451. if err != nil {
  3452. log.Printf("[ERROR] Failed body read in job sync: %s", err)
  3453. return err
  3454. }
  3455. //log.Printf("Remote Data: %s", respBody)
  3456. err = remoteOrgJobController(org, respBody)
  3457. if err != nil {
  3458. //log.Printf("[ERROR] Failed cloud sync job controller run for '%s': %s", respBody, err)
  3459. return err
  3460. }
  3461. return nil
  3462. }
  3463. // /api/v1/mcp
  3464. // /api/v1/agent
  3465. // /api/v1/apps/{appid}/mcp
  3466. func runMCPAction(resp http.ResponseWriter, request *http.Request) {
  3467. cors := shuffle.HandleCors(resp, request)
  3468. if cors {
  3469. return
  3470. }
  3471. ctx := shuffle.GetContext(request)
  3472. parentExec := shuffle.WorkflowExecution{}
  3473. user, err := shuffle.HandleApiAuthentication(resp, request)
  3474. if err != nil {
  3475. // Look for org_id query as app may be private
  3476. // No validation is done here, as it's just running the app
  3477. // to find a user
  3478. orgId := request.URL.Query().Get("org_id")
  3479. if len(orgId) > 0 {
  3480. user.ActiveOrg.Id = orgId
  3481. } else {
  3482. executionId := request.URL.Query().Get("execution_id")
  3483. authorization := request.URL.Query().Get("authorization")
  3484. if len(executionId) == 0 || len(authorization) == 0 {
  3485. log.Printf("[WARNING] Bad execution id/auth in single action validate (1): %#v, %#v. Continuing with the 'public' org id", executionId, authorization)
  3486. err := shuffle.ValidateRequestOverload(resp, request)
  3487. if err != nil {
  3488. log.Printf("[INFO] Request overload for IP %s in single action execution", shuffle.GetRequestIp(request))
  3489. resp.WriteHeader(429)
  3490. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Too many requests. Please try again in 30 seconds."}`)))
  3491. return
  3492. }
  3493. user.Username = shuffle.GetRequestIp(request)
  3494. user.ActiveOrg.Name = shuffle.GetRequestIp(request)
  3495. user.ActiveOrg.Id = "public"
  3496. } else {
  3497. // Find the execution
  3498. exec, err := shuffle.GetWorkflowExecution(ctx, executionId)
  3499. if err != nil {
  3500. log.Printf("[WARNING] Bad execution id in single action validate (2): %s", err)
  3501. resp.WriteHeader(401)
  3502. resp.Write([]byte(`{"success": false, "reason": "Bad execution mapping (1)"}`))
  3503. return
  3504. }
  3505. if exec.Authorization != authorization {
  3506. log.Printf("[WARNING] Bad execution auth in single action validate (3): %#v, %#v", exec.Authorization, authorization)
  3507. resp.WriteHeader(403)
  3508. resp.Write([]byte(`{"success": false, "reason": "Bad execution mapping (2)"}`))
  3509. return
  3510. }
  3511. parentExec = *exec
  3512. user.ActiveOrg.Id = exec.OrgId
  3513. if len(user.ActiveOrg.Id) == 0 {
  3514. user.ActiveOrg.Id = exec.ExecutionOrg
  3515. }
  3516. user.Username = fmt.Sprintf("org %s", user.ActiveOrg.Id)
  3517. }
  3518. }
  3519. if len(user.ActiveOrg.Id) == 0 {
  3520. resp.WriteHeader(401)
  3521. resp.Write([]byte(`{"success": false, "reason": "No org_id found to map back to"}`))
  3522. return
  3523. }
  3524. }
  3525. location := strings.Split(request.URL.Path, "/")
  3526. var fileId string
  3527. if location[1] == "api" {
  3528. if len(location) <= 3 {
  3529. resp.WriteHeader(400)
  3530. resp.Write([]byte(`{"success": false}`))
  3531. return
  3532. }
  3533. if len(location) == 4 {
  3534. fileId = location[3] // /api/v1/agent or /api/v1/mcp
  3535. } else {
  3536. fileId = location[4] // /api/v1/apps/{appid}/mcp
  3537. }
  3538. }
  3539. //log.Printf("[AUDIT] User Authentication failed in execute SINGLE action - CONTINUING ANYWAY: %s. Found OrgID: %#v", err, user.ActiveOrg.Id)
  3540. log.Printf("[AUDIT] User %s (%s) in org %s (%s) is running SINGLE App run for App ID '%s'", user.Username, user.Id, user.ActiveOrg.Name, user.ActiveOrg.Id, fileId)
  3541. body, err := ioutil.ReadAll(request.Body)
  3542. if err != nil {
  3543. log.Printf("[INFO] Failed single execution POST body read: %s", err)
  3544. resp.WriteHeader(401)
  3545. resp.Write([]byte(`{"success": false}`))
  3546. return
  3547. }
  3548. foundRequest := shuffle.MCPRequest{}
  3549. //func HandleAiAgentExecutionStart(execution WorkflowExecution, startNode Action, createNextActions bool) (Action, error) {
  3550. // Unmarshal it
  3551. err = json.Unmarshal(body, &foundRequest)
  3552. if err != nil {
  3553. log.Printf("[INFO] Failed single execution POST body unmarshal: %s", err)
  3554. resp.WriteHeader(400)
  3555. resp.Write([]byte(`{"success": false}`))
  3556. return
  3557. }
  3558. foundEnvironment := "Shuffle"
  3559. if len(foundRequest.Params.Environment) > 0 {
  3560. foundEnvironment = foundRequest.Params.Environment
  3561. }
  3562. if len(foundRequest.Params.Input.Text) < 5 {
  3563. resp.WriteHeader(400)
  3564. resp.Write([]byte(`{"success": false, "reason": "Input text is required and must be at least 5 characters"}`))
  3565. return
  3566. }
  3567. var newAction shuffle.Action
  3568. if fileId == "agent" {
  3569. newAction = shuffle.Action{
  3570. Name: "agent",
  3571. AppName: "AI Agent",
  3572. AppID: "shuffle_agent",
  3573. AppVersion: "1.0.0",
  3574. Environment: foundEnvironment,
  3575. Parameters: []shuffle.WorkflowAppActionParameter{
  3576. {
  3577. Name: "app_name",
  3578. Value: "openai",
  3579. },
  3580. {
  3581. Name: "input",
  3582. Value: foundRequest.Params.Input.Text,
  3583. },
  3584. },
  3585. }
  3586. } else {
  3587. foundId := ""
  3588. if len(foundRequest.Params.ToolID) > 0 {
  3589. foundId = foundRequest.Params.ToolID
  3590. } else {
  3591. if len(foundRequest.Params.ToolName) == 32 {
  3592. foundId = foundRequest.Params.ToolName
  3593. } else {
  3594. foundApps, err := shuffle.FindWorkflowAppByName(ctx, foundRequest.Params.ToolName)
  3595. if err != nil || len(foundApps) == 0 {
  3596. log.Printf("[INFO] Failed to find app by name '%s' in single execution: %s", foundRequest.Params.ToolName, err)
  3597. resp.WriteHeader(400)
  3598. resp.Write([]byte(`{"success": false, "reason": "Valid param.tool_id (app ID) is required"}`))
  3599. return
  3600. }
  3601. for _, app := range foundApps {
  3602. if app.Name == foundRequest.Params.ToolName {
  3603. foundId = app.ID
  3604. break
  3605. }
  3606. }
  3607. }
  3608. }
  3609. app, err := shuffle.GetApp(ctx, foundId, shuffle.User{}, false)
  3610. if err != nil {
  3611. log.Printf("[INFO] Failed to find app by id '%s' in single execution: %s", foundId, err)
  3612. resp.WriteHeader(400)
  3613. resp.Write([]byte(`{"success": false}`))
  3614. return
  3615. }
  3616. if !app.Public {
  3617. if user.Id == app.Owner || user.ActiveOrg.Id == app.ReferenceOrg || shuffle.ArrayContains(app.Contributors, user.Id) {
  3618. log.Printf("[AUDIT] Support & Admin user %s (%s) got access to app %s (MCP)", user.Username, user.Id, app.ID)
  3619. } else if user.Role == "admin" && app.Owner == "" {
  3620. log.Printf("[AUDIT] Any admin can GET %s (%s), since it doesn't have an owner (GET - MCP).", app.Name, app.ID)
  3621. } else {
  3622. log.Printf("[AUDIT] User %s (%s) in org %s (%s) was denied access to app %s (MCP)", user.Username, user.Id, user.ActiveOrg.Name, user.ActiveOrg.Id, app.ID)
  3623. resp.WriteHeader(403)
  3624. resp.Write([]byte(`{"success": false}`))
  3625. return
  3626. }
  3627. } else {
  3628. log.Printf("[AUDIT] User %s (%s) in org %s (%s) got access to public app %s (MCP)", user.Username, user.Id, user.ActiveOrg.Name, user.ActiveOrg.Id, app.ID)
  3629. }
  3630. // Check permissions
  3631. parsedName := strings.ToLower(strings.ReplaceAll(app.Name, " ", "_"))
  3632. parsedApp := fmt.Sprintf("app:%s:%s", app.ID, parsedName)
  3633. // Run the action
  3634. newAction = shuffle.Action{
  3635. Name: "agent",
  3636. AppName: "AI Agent",
  3637. AppID: "shuffle_agent",
  3638. AppVersion: "1.0.0",
  3639. Environment: foundEnvironment,
  3640. Parameters: []shuffle.WorkflowAppActionParameter{
  3641. {
  3642. Name: "app_name",
  3643. Value: "openai",
  3644. },
  3645. {
  3646. Name: "input",
  3647. Value: foundRequest.Params.Input.Text,
  3648. },
  3649. {
  3650. Name: "app_name",
  3651. Value: parsedApp,
  3652. },
  3653. },
  3654. }
  3655. }
  3656. marshalledAction, err := json.Marshal(newAction)
  3657. if err != nil {
  3658. log.Printf("[ERROR] Failed to marshal single action body: %s", err)
  3659. resp.WriteHeader(500)
  3660. resp.Write([]byte(`{"success": false}`))
  3661. return
  3662. }
  3663. // Special handling for agent calls
  3664. if fileId == "agent" {
  3665. if len(parentExec.ExecutionId) > 0 {
  3666. targetActionId := request.URL.Query().Get("action_id")
  3667. agentNodeFound := false
  3668. var agentNode shuffle.Action
  3669. for i, action := range parentExec.Workflow.Actions {
  3670. if len(targetActionId) > 0 && action.ID != targetActionId {
  3671. continue
  3672. }
  3673. if len(targetActionId) == 0 && action.AppName != "AI Agent" {
  3674. continue
  3675. }
  3676. if len(foundRequest.Params.Input.Text) > 0 {
  3677. for j, param := range parentExec.Workflow.Actions[i].Parameters {
  3678. if param.Name == "input" {
  3679. parentExec.Workflow.Actions[i].Parameters[j].Value = foundRequest.Params.Input.Text
  3680. break
  3681. }
  3682. }
  3683. }
  3684. agentNode = parentExec.Workflow.Actions[i]
  3685. agentNodeFound = true
  3686. log.Printf("[DEBUG][%s] AI Agent: Running AI Agent node '%s' (ID: %s)", parentExec.ExecutionId, agentNode.Label, agentNode.ID)
  3687. break
  3688. }
  3689. if !agentNodeFound {
  3690. log.Printf("[ERROR][%s] No AI Agent node found in parent workflow (Target ID: %s)", parentExec.ExecutionId, targetActionId)
  3691. resp.WriteHeader(400)
  3692. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No AI Agent node found in parent workflow matching request"}`)))
  3693. return
  3694. }
  3695. go shuffle.HandleAiAgentExecutionStart(parentExec, agentNode, false)
  3696. resp.WriteHeader(200)
  3697. resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s", "mode": "hybrid"}`, parentExec.ExecutionId, parentExec.Authorization)))
  3698. return
  3699. } else {
  3700. //standalone mode
  3701. workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, "agent_starter", marshalledAction, false, "")
  3702. if err != nil {
  3703. log.Printf("[ERROR] Failed to prepare standalone agent execution: %s", err)
  3704. resp.WriteHeader(500)
  3705. resp.Write([]byte(`{"success": false, "reason": "Failed to create agent execution"}`))
  3706. return
  3707. }
  3708. log.Printf("[INFO] Standalone agent execution created: %s", workflowExecution.ExecutionId)
  3709. resp.WriteHeader(200)
  3710. resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s", "mode": "standalone"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
  3711. return
  3712. }
  3713. }
  3714. // For non-agent calls (MCP or other apps): continue with existing flow
  3715. workflowExecution, err := shuffle.PrepareSingleAction(ctx, user, "agent", marshalledAction, false, "")
  3716. if fileId == "agent_starter" {
  3717. log.Printf("[INFO] Returning early for agent_starter single action execution: %s", workflowExecution.ExecutionId)
  3718. resp.WriteHeader(200)
  3719. resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
  3720. return
  3721. }
  3722. debugUrl := fmt.Sprintf("/workflows/%s?execution_id=%s", workflowExecution.Workflow.ID, workflowExecution.ExecutionId)
  3723. resp.Header().Add("X-Debug-Url", debugUrl)
  3724. if err != nil {
  3725. returndata := shuffle.ResultChecker{
  3726. Success: false,
  3727. Reason: fmt.Sprintf("%s", err),
  3728. }
  3729. // Special handler for decision reruns~
  3730. if strings.Contains(err.Error(), "Successfully") {
  3731. returndata.Success = true
  3732. resp.WriteHeader(200)
  3733. } else {
  3734. log.Printf("[INFO] Failed workflowrequest POST read in single action (4): %s", err)
  3735. resp.WriteHeader(400)
  3736. }
  3737. respBytes, err := json.Marshal(returndata)
  3738. if err != nil {
  3739. resp.Write([]byte(`{"success": false}`))
  3740. return
  3741. }
  3742. resp.Write(respBytes)
  3743. return
  3744. }
  3745. foundEnv := ""
  3746. params := []string{}
  3747. for _, action := range workflowExecution.Workflow.Actions {
  3748. for _, param := range action.Parameters {
  3749. params = append(params, param.Name)
  3750. }
  3751. if len(action.Environment) > 0 {
  3752. foundEnv = action.Environment
  3753. break
  3754. }
  3755. }
  3756. go shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions")
  3757. executionRequest := shuffle.ExecutionRequest{
  3758. ExecutionId: workflowExecution.ExecutionId,
  3759. WorkflowId: workflowExecution.Workflow.ID,
  3760. Authorization: workflowExecution.Authorization,
  3761. Environments: []string{foundEnv},
  3762. }
  3763. parsedEnv := fmt.Sprintf("%s_%s", strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(foundEnv, " ", "-"), "_", "-")), workflowExecution.ExecutionOrg)
  3764. // Check if environment is distributed from parent org
  3765. if len(workflowExecution.ExecutionOrg) > 0 {
  3766. environments, err := shuffle.GetEnvironments(ctx, workflowExecution.ExecutionOrg)
  3767. if err != nil {
  3768. log.Printf("[ERROR] Failed getting environments for org %s in single action. May fail to verify env.: %s", workflowExecution.ExecutionOrg, err)
  3769. } else {
  3770. for _, env := range environments {
  3771. if env.Archived {
  3772. continue
  3773. }
  3774. if env.Name != foundEnv {
  3775. continue
  3776. }
  3777. if env.OrgId != workflowExecution.ExecutionOrg && len(env.OrgId) > 0 {
  3778. if debug {
  3779. log.Printf("[DEBUG][%s] Found suborg environment %s for org %s in single action. Re-mapping it to org-id %s", workflowExecution.ExecutionId, env.Name, env.OrgId, env.OrgId)
  3780. }
  3781. parsedEnv = fmt.Sprintf("%s_%s", strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(foundEnv, " ", "-"), "_", "-")), env.OrgId)
  3782. break
  3783. }
  3784. }
  3785. }
  3786. }
  3787. log.Printf("[INFO][%s] Adding new single-action job to env queue (4 - MCP): %s", workflowExecution.ExecutionId, parsedEnv)
  3788. err = shuffle.SetWorkflowQueue(ctx, executionRequest, parsedEnv)
  3789. if err != nil {
  3790. log.Printf("[WARNING][%s] Failed adding %s to db (single action queue): %s", workflowExecution.ExecutionId, parsedEnv, err)
  3791. }
  3792. actionId := ""
  3793. if len(workflowExecution.Workflow.Actions) == 1 {
  3794. actionId = workflowExecution.Workflow.Actions[0].ID
  3795. }
  3796. mappedResponse := shuffle.MCPResponse{
  3797. Jsonrpc: foundRequest.Jsonrpc,
  3798. ID: foundRequest.ID,
  3799. }
  3800. singleResult := shuffle.HandleRetValidation(ctx, workflowExecution, 1, 45, actionId)
  3801. agentOutput := shuffle.AgentOutput{}
  3802. err = json.Unmarshal([]byte(singleResult.Result), &agentOutput)
  3803. if err == nil && len(agentOutput.Output) > 0 {
  3804. log.Printf("[INFO] Returning agent output in MCP response: %s", agentOutput.Output)
  3805. marshalledAgentOutput, err := json.Marshal(agentOutput)
  3806. if err != nil {
  3807. log.Printf("[ERROR] Failed to marshal agent output in MCP response: %s", err)
  3808. } else {
  3809. marshalledOutput := map[string]interface{}{}
  3810. err = json.Unmarshal(marshalledAgentOutput, &marshalledOutput)
  3811. if err != nil {
  3812. log.Printf("[ERROR] Failed to unmarshal agent output in MCP response: %s", err)
  3813. }
  3814. marshalledOutput["message"] = agentOutput.Output
  3815. mappedResponse.Result = marshalledOutput
  3816. }
  3817. } else {
  3818. marshalledSingleResult, err := json.Marshal(singleResult)
  3819. if err != nil {
  3820. log.Printf("[ERROR] Failed to marshal single result in MCP response: %s", err)
  3821. }
  3822. marshalledResult := map[string]interface{}{}
  3823. err = json.Unmarshal(marshalledSingleResult, &marshalledResult)
  3824. if err != nil {
  3825. log.Printf("[ERROR] Failed to unmarshal single result in MCP response: %s", err)
  3826. }
  3827. }
  3828. marshalledMappedResponse, err := json.Marshal(mappedResponse)
  3829. if err != nil {
  3830. log.Printf("[ERROR] Failed to marshal mapped response in MCP response: %s", err)
  3831. resp.WriteHeader(500)
  3832. resp.Write([]byte(`{"success": false}`))
  3833. return
  3834. }
  3835. resp.WriteHeader(200)
  3836. resp.Write(marshalledMappedResponse)
  3837. }
  3838. func runInitCloudSetup() {
  3839. action := shuffle.CloudSyncJob{
  3840. Type: "setup",
  3841. Action: "init",
  3842. OrgId: "",
  3843. PrimaryItemId: "",
  3844. }
  3845. err := executeCloudAction(action, "")
  3846. if err != nil {
  3847. log.Printf("[INFO] Failed initial setup: %s", err)
  3848. } else {
  3849. log.Printf("[INFO] Finished initial cloudsync setup!")
  3850. }
  3851. }
  3852. func runInitEs(ctx context.Context) {
  3853. log.Printf("[DEBUG] Starting INIT setup for Elasticsearch/Opensearch")
  3854. httpProxy := os.Getenv("HTTP_PROXY")
  3855. if len(httpProxy) > 0 {
  3856. log.Printf("[INFO] Running with HTTP proxy %s (env: HTTP_PROXY)", httpProxy)
  3857. }
  3858. httpsProxy := os.Getenv("HTTPS_PROXY")
  3859. if len(httpsProxy) > 0 {
  3860. log.Printf("[INFO] Running with HTTPS proxy %s (env: HTTPS_PROXY)", httpsProxy)
  3861. }
  3862. defaultEnv := os.Getenv("ENVIRONMENT_NAME")
  3863. if len(defaultEnv) == 0 {
  3864. defaultEnv = "Shuffle"
  3865. log.Printf("[DEBUG] Setting default environment for org to %s", defaultEnv)
  3866. }
  3867. log.Printf("[DEBUG] Getting organizations for Elasticsearch/Opensearch")
  3868. activeOrgs, err := shuffle.GetAllOrgs(ctx)
  3869. log.Printf("[DEBUG] Got %d organizations to look into. If this is 0, we wait 10 more seconds until DB is ready and try again.", len(activeOrgs))
  3870. if len(activeOrgs) == 0 {
  3871. time.Sleep(10 * time.Second)
  3872. activeOrgs, err = shuffle.GetAllOrgs(ctx)
  3873. }
  3874. setUsers := false
  3875. _ = setUsers
  3876. if err != nil {
  3877. if fmt.Sprintf("%s", err) == "EOF" || strings.Contains(fmt.Sprintf("%s", err), "bad status") {
  3878. time.Sleep(10 * time.Second)
  3879. runInitEs(ctx)
  3880. return
  3881. }
  3882. log.Printf("[DEBUG] Error getting organizations: %s", err)
  3883. runInitCloudSetup()
  3884. } else {
  3885. // Add all users to it
  3886. if len(activeOrgs) == 1 {
  3887. setUsers = true
  3888. } else if len(activeOrgs) == 0 {
  3889. log.Printf(`[DEBUG] No orgs. Setting NEW org "default"`)
  3890. runInitCloudSetup()
  3891. //orgSetupName := "default"
  3892. //orgId := uuid.NewV4().String()
  3893. //newOrg := shuffle.Org{
  3894. // Name: orgSetupName,
  3895. // Id: orgId,
  3896. // Org: orgSetupName,
  3897. // Users: []shuffle.User{},
  3898. // Roles: []string{"admin", "user"},
  3899. // CloudSync: false,
  3900. //}
  3901. //err = shuffle.SetOrg(ctx, newOrg, orgId)
  3902. //if err != nil {
  3903. // log.Printf("Failed setting organization: %s", err)
  3904. //} else {
  3905. // log.Printf("Successfully created the default org!")
  3906. // setUsers = true
  3907. // item := shuffle.Environment{
  3908. // Name: defaultEnv,
  3909. // Type: "onprem",
  3910. // OrgId: orgId,
  3911. // Default: true,
  3912. // Id: uuid.NewV4().String(),
  3913. // }
  3914. // err = shuffle.SetEnvironment(ctx, &item)
  3915. // if err != nil {
  3916. // log.Printf("[WARNING] Failed setting up new environment for new org")
  3917. // }
  3918. //}
  3919. } else {
  3920. log.Printf("[DEBUG] Found %d org(s) in total.", len(activeOrgs))
  3921. if len(activeOrgs) == 1 {
  3922. if len(activeOrgs[0].Users) == 0 {
  3923. log.Printf("[ERROR] Main Org doesn't have any user. Creating.")
  3924. users, err := shuffle.GetAllUsers(ctx)
  3925. if err != nil && len(users) == 0 {
  3926. log.Printf("Failed getting users in org fix")
  3927. } else {
  3928. // Remapping everyone to admin. This should never happen.
  3929. for _, user := range users {
  3930. user.ActiveOrg = shuffle.OrgMini{
  3931. Id: activeOrgs[0].Id,
  3932. Name: activeOrgs[0].Name,
  3933. Role: "admin",
  3934. }
  3935. activeOrgs[0].Users = append(activeOrgs[0].Users, user)
  3936. }
  3937. err = shuffle.SetOrg(ctx, activeOrgs[0], activeOrgs[0].Id)
  3938. if err != nil {
  3939. log.Printf("Failed setting org: %s", err)
  3940. } else {
  3941. log.Printf("Successfully updated org to have users!")
  3942. }
  3943. }
  3944. }
  3945. }
  3946. }
  3947. }
  3948. log.Printf("[INFO] Waiting 30 seconds during init to make sure the opensearch instance is up and running with security features enabled")
  3949. time.Sleep(30 * time.Second)
  3950. shuffle.InitOpensearchIndexes()
  3951. // FIXME: This should ONLY run on one backend instance. This may cause interference.
  3952. schedules, err := shuffle.GetAllSchedules(ctx, "ALL")
  3953. if err != nil {
  3954. log.Printf("[WARNING] Failed getting schedules during service init: %s", err)
  3955. } else {
  3956. log.Printf("[INFO] Setting up %d schedule(s)", len(schedules))
  3957. url := &url.URL{}
  3958. job := func(schedule shuffle.ScheduleOld) func() {
  3959. return func() {
  3960. log.Printf("[INFO] Running schedule %s with interval %d.", schedule.Id, schedule.Seconds)
  3961. request := &http.Request{
  3962. URL: url,
  3963. Method: "POST",
  3964. Body: ioutil.NopCloser(strings.NewReader(schedule.WrappedArgument)),
  3965. }
  3966. orgId := ""
  3967. if len(activeOrgs) > 0 {
  3968. orgId = activeOrgs[0].Id
  3969. }
  3970. if len(schedule.Org) == 36 {
  3971. orgId = schedule.Org
  3972. }
  3973. _, _, err := handleExecution(schedule.WorkflowId, shuffle.Workflow{}, request, orgId)
  3974. if err != nil {
  3975. log.Printf("[WARNING] Failed to execute %s: %s", schedule.WorkflowId, err)
  3976. }
  3977. }
  3978. }
  3979. for _, schedule := range schedules {
  3980. if strings.ToLower(schedule.Environment) == "cloud" {
  3981. log.Printf("[DEBUG] Skipping cloud schedule")
  3982. continue
  3983. }
  3984. // FIXME: Add a randomized timer to avoid all schedules running at the same time
  3985. // Many are at 5 minutes / 1 hour. The point is to spread these out
  3986. // a bit instead of all of them starting at the exact same time
  3987. //log.Printf("Schedule: %#v", schedule)
  3988. //log.Printf("Schedule time: every %d seconds", schedule.Seconds)
  3989. if schedule.Seconds == 0 && len(schedule.Frequency) > 0 {
  3990. cronJob, err := CronScheduler.Cron(schedule.Frequency).Do(job(schedule))
  3991. if err != nil {
  3992. log.Printf("[ERROR] Failed to start schedule for workflow %s: %s", schedule.WorkflowId, err)
  3993. } else {
  3994. log.Printf("[DEBUG] Successfully started schedule for workflow %s", schedule.WorkflowId)
  3995. }
  3996. cronJobs[schedule.Id] = cronJob
  3997. } else {
  3998. jobret, err := newscheduler.Every(schedule.Seconds).Seconds().NotImmediately().Run(job(schedule))
  3999. if err != nil {
  4000. log.Printf("[ERROR] Failed to start schedule for workflow %s: %s", schedule.WorkflowId, err)
  4001. } else {
  4002. log.Printf("[DEBUG] Successfully started schedule for workflow %s", schedule.WorkflowId)
  4003. }
  4004. scheduledJobs[schedule.Id] = jobret
  4005. }
  4006. }
  4007. }
  4008. parsedApikey := ""
  4009. users, err := shuffle.GetAllUsers(ctx)
  4010. if len(users) == 0 {
  4011. log.Printf("[INFO] Trying to set up user based on environments SHUFFLE_DEFAULT_USERNAME & SHUFFLE_DEFAULT_PASSWORD")
  4012. username := os.Getenv("SHUFFLE_DEFAULT_USERNAME")
  4013. password := os.Getenv("SHUFFLE_DEFAULT_PASSWORD")
  4014. if len(username) == 0 || len(password) == 0 || len(activeOrgs) > 0 {
  4015. log.Printf("[DEBUG] SHUFFLE_DEFAULT_USERNAME and SHUFFLE_DEFAULT_PASSWORD not defined as environments. Running without default user.")
  4016. } else {
  4017. apikey := os.Getenv("SHUFFLE_DEFAULT_APIKEY")
  4018. if len(parsedApikey) == 0 {
  4019. parsedApikey = apikey
  4020. }
  4021. log.Printf("[DEBUG] Creating org for default user %s", username)
  4022. orgId := uuid.NewV4().String()
  4023. orgSetupName := "default"
  4024. tmpOrg := shuffle.OrgMini{
  4025. Name: orgSetupName,
  4026. Id: orgId,
  4027. }
  4028. err = createNewUser(username, password, "admin", apikey, tmpOrg)
  4029. if err != nil {
  4030. log.Printf("[ERROR] Failed to create default user %s: %s", username, err)
  4031. } else {
  4032. log.Printf("[INFO] Successfully created user %s", username)
  4033. }
  4034. user, err := shuffle.GetUser(ctx, username)
  4035. newOrg := shuffle.Org{
  4036. Name: orgSetupName,
  4037. Id: orgId,
  4038. Org: orgSetupName,
  4039. Users: []shuffle.User{*user},
  4040. Roles: []string{"admin", "user"},
  4041. CloudSync: false,
  4042. }
  4043. err = shuffle.SetOrg(ctx, newOrg, newOrg.Id)
  4044. if err != nil {
  4045. log.Printf("[ERROR] Failed setting organization when creating original user: %s", err)
  4046. } else {
  4047. log.Printf("[DEBUG] Successfully created the default org with id %s!", orgId)
  4048. item := shuffle.Environment{
  4049. Name: defaultEnv,
  4050. Type: "onprem",
  4051. OrgId: orgId,
  4052. Default: true,
  4053. Id: uuid.NewV4().String(),
  4054. }
  4055. err = shuffle.SetEnvironment(ctx, &item)
  4056. if err != nil {
  4057. log.Printf("[WARNING] Failed setting up new environment")
  4058. }
  4059. }
  4060. }
  4061. } else {
  4062. for _, user := range users {
  4063. if user.Role == "admin" && len(user.ApiKey) > 0 {
  4064. parsedApikey = user.ApiKey
  4065. log.Printf("[DEBUG] Using apikey of %s (%s) for cleanup", user.Username, user.Id)
  4066. break
  4067. }
  4068. }
  4069. }
  4070. log.Printf("[INFO] Starting cloud schedules for orgs if enabled!")
  4071. type requestStruct struct {
  4072. ApiKey string `json:"api_key"`
  4073. }
  4074. for _, org := range activeOrgs {
  4075. if len(org.Id) == 0 {
  4076. log.Printf("[DEBUG] No ID found for org with name '%s'. Why was it made?", org.Name)
  4077. continue
  4078. }
  4079. if !org.CloudSync {
  4080. log.Printf("[INFO] Skipping org syncCheck for '%s' because sync isn't set (1).", org.Id)
  4081. continue
  4082. }
  4083. //interval := int(org.SyncConfig.Interval)
  4084. interval := 30
  4085. if interval == 0 {
  4086. log.Printf("[WARNING] Skipping org %s because sync isn't set (0).", org.Id)
  4087. continue
  4088. }
  4089. log.Printf("[DEBUG] Should start cloud schedule for org %s (%s)", org.Name, org.Id)
  4090. job := func() {
  4091. err := remoteOrgJobHandler(org, interval)
  4092. if err != nil {
  4093. log.Printf("[ERROR] Failed request with remote org sync for org %s (2): %s", org.Id, err)
  4094. }
  4095. }
  4096. jobret, err := newscheduler.Every(int(interval)).Seconds().NotImmediately().Run(job)
  4097. if err != nil {
  4098. log.Printf("[ERROR] Failed to schedule org: %s", err)
  4099. } else {
  4100. log.Printf("[INFO] Started sync on interval %d for org %s (%s)", interval, org.Name, org.Id)
  4101. scheduledOrgs[org.Id] = jobret
  4102. }
  4103. }
  4104. forceUpdateEnv := os.Getenv("SHUFFLE_APP_FORCE_UPDATE")
  4105. forceUpdate := false
  4106. if len(forceUpdateEnv) > 0 && forceUpdateEnv == "true" {
  4107. log.Printf("Forcing to rebuild apps")
  4108. forceUpdate = true
  4109. }
  4110. // FIXME: Have this for all envs in all orgs (loop and find).
  4111. if len(parsedApikey) == 0 {
  4112. log.Printf("[WARNING] No apikey found for cleanup. Skipping cleanup schedule.")
  4113. } else {
  4114. cleanupSchedule := 300
  4115. if len(os.Getenv("SHUFFLE_RERUN_SCHEDULE")) > 0 {
  4116. newfrequency, err := strconv.Atoi(os.Getenv("SHUFFLE_RERUN_SCHEDULE"))
  4117. if err == nil {
  4118. cleanupSchedule = newfrequency
  4119. if cleanupSchedule < 300 {
  4120. log.Printf("[WARNING] A Cleanupschedule of less than 300 seconds won't help.")
  4121. cleanupSchedule = 300
  4122. }
  4123. }
  4124. }
  4125. environments := []string{defaultEnv}
  4126. // Comma separated list of RERUN environments
  4127. if len(os.Getenv("SHUFFLE_RERUN_ENVIRONMENTS")) > 0 {
  4128. foundenv := strings.Split(os.Getenv("SHUFFLE_RERUN_ENVIRONMENTS"), ",")
  4129. for i, env := range foundenv {
  4130. if len(env) == 0 {
  4131. continue
  4132. }
  4133. environments[i] = strings.TrimSpace(env)
  4134. if !shuffle.ArrayContains(environments, env) {
  4135. environments = append(foundenv, env)
  4136. }
  4137. }
  4138. }
  4139. log.Printf("[DEBUG] Starting schedule setup for execution cleanup every %d seconds. Running first immediately. Environments: %#v", cleanupSchedule, environments)
  4140. cleanupJob := func() func() {
  4141. return func() {
  4142. //log.Printf("[INFO] Running schedule for cleaning up or re-running unfinished workflows in %d environments.", len(environments))
  4143. backendPort := os.Getenv("BACKEND_PORT")
  4144. if backendPort == "" {
  4145. backendPort = "5001"
  4146. }
  4147. for _, environment := range environments {
  4148. // Allowed without PROXY management as it's localhost
  4149. // client := shuffle.GetExternalClient(syncUrl)
  4150. httpClient := &http.Client{}
  4151. url := fmt.Sprintf("http://localhost:%s/api/v1/environments/%s/stop", backendPort, environment)
  4152. req, err := http.NewRequest(
  4153. "GET",
  4154. url,
  4155. nil,
  4156. )
  4157. // FIXME: This will stop working of the user rotates their key lol
  4158. req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, parsedApikey))
  4159. if err != nil {
  4160. log.Printf("[ERROR] Failed CREATING environment request for %s: %s", environment, err)
  4161. continue
  4162. }
  4163. newresp, err := httpClient.Do(req)
  4164. if err != nil {
  4165. log.Printf("[ERROR] Failed running environment request %s: %s", environment, err)
  4166. continue
  4167. }
  4168. respBody, err := ioutil.ReadAll(newresp.Body)
  4169. if err != nil {
  4170. log.Printf("[ERROR] Failed setting respbody %s for execution stop. Status: %d", err, newresp.StatusCode)
  4171. continue
  4172. }
  4173. if newresp.StatusCode != 200 {
  4174. if !strings.Contains(string(respBody), "is active") {
  4175. log.Printf("[WARNING] Failed stopping runs in environment %s. Status code: %d. Body: %s", environment, newresp.StatusCode, string(respBody))
  4176. }
  4177. continue
  4178. }
  4179. url = fmt.Sprintf("http://localhost:%s/api/v1/environments/%s/rerun", backendPort, environment)
  4180. req, err = http.NewRequest(
  4181. "GET",
  4182. url,
  4183. nil,
  4184. )
  4185. req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, parsedApikey))
  4186. if err != nil {
  4187. log.Printf("[ERROR] Failed CREATING environment request to rerun for %s: %s", environment, err)
  4188. continue
  4189. }
  4190. newresp, err = httpClient.Do(req)
  4191. if err != nil {
  4192. log.Printf("[ERROR] Failed running environment request to rerun for %s: %s", environment, err)
  4193. continue
  4194. }
  4195. if newresp.StatusCode != 200 {
  4196. log.Printf("[WARNING] Failed rerunning environment %s. Status code: %d", environment, newresp.StatusCode)
  4197. }
  4198. //respBody, err := ioutil.ReadAll(newresp.Body)
  4199. //if err != nil {
  4200. // log.Printf("[ERROR] Failed setting respbody %s", err)
  4201. // continue
  4202. //}
  4203. //log.Printf("[DEBUG] Ran workflow RERUN request for %s with the response. Body: %s", environment, string(respBody))
  4204. }
  4205. }
  4206. }
  4207. jobret, err := newscheduler.Every(cleanupSchedule).Seconds().Run(cleanupJob())
  4208. if err != nil {
  4209. log.Printf("[ERROR] Failed to schedule Cleanup: %s", err)
  4210. } else {
  4211. _ = jobret
  4212. }
  4213. }
  4214. // Getting apps to see if we should initialize a test
  4215. workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 1000, 0)
  4216. log.Printf("[INFO] Getting and validating workflowapps. Got %d with err %#v", len(workflowapps), err)
  4217. if err != nil && len(workflowapps) == 0 {
  4218. log.Printf("[WARNING] Failed getting apps (runInit): %s", err)
  4219. } else if err == nil && len(workflowapps) < 10 {
  4220. log.Printf("[DEBUG] Downloading default apps as %d were found", len(workflowapps))
  4221. fs := memfs.New()
  4222. storer := memory.NewStorage()
  4223. url := os.Getenv("SHUFFLE_APP_DOWNLOAD_LOCATION")
  4224. if len(url) == 0 {
  4225. log.Printf("[INFO] Skipping download of apps since no URL is set. Default would be https://github.com/shuffle/python-apps")
  4226. url = "https://github.com/shuffle/python-apps"
  4227. //url = ""
  4228. //return
  4229. }
  4230. username := os.Getenv("SHUFFLE_DOWNLOAD_AUTH_USERNAME")
  4231. password := os.Getenv("SHUFFLE_DOWNLOAD_AUTH_PASSWORD")
  4232. cloneOptions := &git.CloneOptions{
  4233. URL: url,
  4234. }
  4235. if len(username) > 0 && len(password) > 0 {
  4236. cloneOptions.Auth = &http2.BasicAuth{
  4237. Username: username,
  4238. Password: password,
  4239. }
  4240. }
  4241. cloneOptions = shuffle.CheckGitProxy(cloneOptions)
  4242. branch := os.Getenv("SHUFFLE_DOWNLOAD_AUTH_BRANCH")
  4243. if len(branch) > 0 && branch != "master" && branch != "main" {
  4244. cloneOptions.ReferenceName = plumbing.ReferenceName(branch)
  4245. }
  4246. log.Printf("[DEBUG] Getting apps from url '%s'", url)
  4247. r, err := git.Clone(storer, fs, cloneOptions)
  4248. if err != nil {
  4249. log.Printf("[ERROR] Failed loading repo into memory (init): %s", err)
  4250. }
  4251. dir, err := fs.ReadDir("")
  4252. if err != nil {
  4253. log.Printf("[WARNING] Failed reading folder (init): %s", err)
  4254. }
  4255. _ = r
  4256. //iterateAppGithubFolders(fs, dir, "", "testing")
  4257. _, _, err = IterateAppGithubFolders(ctx, fs, dir, "", "", forceUpdate, true)
  4258. if err != nil {
  4259. log.Printf("[WARNING] Error from app load in init: %s", err)
  4260. }
  4261. //_, _, err = iterateAppGithubFolders(fs, dir, "", "", forceUpdate)
  4262. // Hotloads locally
  4263. location := os.Getenv("SHUFFLE_APP_HOTLOAD_FOLDER")
  4264. if len(location) == 0 {
  4265. location = "./shuffle-apps"
  4266. }
  4267. if len(location) != 0 {
  4268. handleAppHotload(ctx, location, false)
  4269. }
  4270. } else {
  4271. log.Printf("[DEBUG] Skipping download of default apps as %d were found", len(workflowapps))
  4272. }
  4273. if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" {
  4274. healthcheckInterval := 60
  4275. log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats, and dashboard on /health. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval)
  4276. job := func() {
  4277. // Prepare a fake http.responsewriter
  4278. resp := httptest.NewRecorder()
  4279. request := http.Request{}
  4280. // Add the "force=true" query to the fake request
  4281. request.URL, err = url.Parse("/api/v1/health?force=true")
  4282. if err != nil {
  4283. log.Printf("[ERROR] Failed to parse test url for healthstats: %s", err)
  4284. }
  4285. shuffle.RunOpsHealthCheck(resp, &request)
  4286. }
  4287. _, err := newscheduler.Every(int(healthcheckInterval)).Minutes().Run(job)
  4288. if err != nil {
  4289. log.Printf("[ERROR] Failed to schedule healthcheck: %s", err)
  4290. } else {
  4291. log.Printf("[DEBUG] Successfully started healthcheck interval of %d minutes", healthcheckInterval)
  4292. }
  4293. }
  4294. // Self-cleaning
  4295. go func() {
  4296. cursor := ""
  4297. cnt := 0
  4298. newCtx := context.Background()
  4299. for _, org := range activeOrgs {
  4300. if len(org.Id) == 0 {
  4301. log.Printf("[DEBUG] No ID found for org with name '%s'. Why was it made?", org.Name)
  4302. continue
  4303. }
  4304. log.Printf("[INFO] Starting self-cleanup of cache keys for org %s", org.Id)
  4305. for {
  4306. keys, newCursor, err := shuffle.GetAllCacheKeys(newCtx, org.Id, "", 1000, cursor)
  4307. if err != nil {
  4308. //log.Printf("[ERROR] Failed getting all cache keys for cleanup: %s", err)
  4309. break
  4310. }
  4311. if newCursor == cursor || len(newCursor) == 0 {
  4312. break
  4313. }
  4314. if len(keys) == 0 {
  4315. break
  4316. }
  4317. cursor = newCursor
  4318. cnt += 1
  4319. if cnt > 10 {
  4320. break
  4321. }
  4322. }
  4323. log.Printf("[INFO] Finished self-cleanup of cache keys for org %s", org.Id)
  4324. }
  4325. }()
  4326. log.Printf("[INFO] Finished INIT (ES)")
  4327. }
  4328. func handleVerifyCloudsync(orgId string) (shuffle.SyncFeatures, error) {
  4329. ctx := context.Background()
  4330. org, err := shuffle.GetOrg(ctx, orgId)
  4331. if err != nil {
  4332. return shuffle.SyncFeatures{}, err
  4333. }
  4334. //r.HandleFunc("/api/v1/getorgs", handleGetOrgs).Methods("GET", "OPTIONS")
  4335. syncURL := fmt.Sprintf("%s/api/v1/cloud/sync/get_access", syncUrl)
  4336. client := shuffle.GetExternalClient(syncURL)
  4337. req, err := http.NewRequest(
  4338. "GET",
  4339. syncURL,
  4340. nil,
  4341. )
  4342. req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey))
  4343. newresp, err := client.Do(req)
  4344. if err != nil {
  4345. return shuffle.SyncFeatures{}, err
  4346. }
  4347. respBody, err := ioutil.ReadAll(newresp.Body)
  4348. if err != nil {
  4349. return shuffle.SyncFeatures{}, err
  4350. }
  4351. responseData := retStruct{}
  4352. err = json.Unmarshal(respBody, &responseData)
  4353. if err != nil {
  4354. return shuffle.SyncFeatures{}, err
  4355. }
  4356. if newresp.StatusCode != 200 {
  4357. return shuffle.SyncFeatures{}, errors.New(fmt.Sprintf("Got status code %d when getting org remotely. Expected 200. Contact support.", newresp.StatusCode))
  4358. }
  4359. if !responseData.Success {
  4360. return shuffle.SyncFeatures{}, errors.New(responseData.Reason)
  4361. }
  4362. return responseData.SyncFeatures, nil
  4363. }
  4364. // Actually stops syncing with cloud for an org.
  4365. // Disables potential schedules, removes environments, breaks workflows etc.
  4366. func handleStopCloudSync(syncUrl string, org shuffle.Org) (*shuffle.Org, error) {
  4367. if len(org.SyncConfig.Apikey) == 0 {
  4368. return &org, errors.New(fmt.Sprintf("Couldn't find any sync key to disable org %s", org.Id))
  4369. }
  4370. log.Printf("[INFO] Should run cloud sync disable for org %s with URL %s", org.Id, syncUrl)
  4371. client := shuffle.GetExternalClient(syncUrl)
  4372. req, err := http.NewRequest(
  4373. "DELETE",
  4374. syncUrl,
  4375. nil,
  4376. )
  4377. req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, org.SyncConfig.Apikey))
  4378. newresp, err := client.Do(req)
  4379. if err != nil {
  4380. return &org, err
  4381. }
  4382. respBody, err := ioutil.ReadAll(newresp.Body)
  4383. if err != nil {
  4384. return &org, err
  4385. }
  4386. log.Printf("[INFO] Remote disable ret: %s", string(respBody))
  4387. responseData := retStruct{}
  4388. err = json.Unmarshal(respBody, &responseData)
  4389. if err != nil {
  4390. return &org, err
  4391. }
  4392. // FIXME: If it says bad API-key, stop cloud sync for the Org
  4393. if newresp.StatusCode != 200 {
  4394. return &org, errors.New(fmt.Sprintf("Got status code %d when disabling org remotely. Expected 200. Contact support.", newresp.StatusCode))
  4395. }
  4396. if !responseData.Success {
  4397. //log.Printf("Success reason: %s", responseData.Reason)
  4398. return &org, errors.New(responseData.Reason)
  4399. }
  4400. log.Printf("[INFO] Everything is success. Should disable org sync for %s", org.Id)
  4401. ctx := context.Background()
  4402. org.CloudSync = false
  4403. org.SyncFeatures = shuffle.SyncFeatures{}
  4404. org.SyncConfig = shuffle.SyncConfig{}
  4405. org.Subscriptions = []shuffle.PaymentSubscription{}
  4406. err = shuffle.SetOrg(ctx, org, org.Id)
  4407. if err != nil {
  4408. newerror := fmt.Sprintf("[WARNING] ERROR: Failed updating even though there was success: %s", err)
  4409. log.Printf(newerror)
  4410. return &org, errors.New(newerror)
  4411. }
  4412. environments, err := shuffle.GetEnvironments(ctx, org.Id)
  4413. if err != nil {
  4414. log.Printf("[WARNING] Failed getting envs in stop sync: %s", err)
  4415. return &org, err
  4416. }
  4417. // Don't disable, this will be deleted entirely
  4418. for _, environment := range environments {
  4419. if environment.Type == "cloud" {
  4420. environment.Name = "Cloud"
  4421. environment.Archived = true
  4422. err = shuffle.SetEnvironment(ctx, &environment)
  4423. if err == nil {
  4424. log.Printf("[INFO] Updated cloud environment %s", environment.Name)
  4425. } else {
  4426. log.Printf("[INFO] Failed to update cloud environment %s", environment.Name)
  4427. }
  4428. }
  4429. }
  4430. // FIXME: This doesn't work?
  4431. if value, exists := scheduledOrgs[org.Id]; exists {
  4432. // Looks like this does the trick? Hurr
  4433. log.Printf("[WARNING] STOPPING ORG SCHEDULE for: %s", org.Id)
  4434. value.Lock()
  4435. }
  4436. return &org, nil
  4437. }
  4438. // INFO: https://docs.google.com/drawings/d/1JJebpPeEVEbmH_qsAC6zf9Noygp7PytvesrkhE19QrY/edit
  4439. /*
  4440. This is here to both enable and disable cloud sync features for an organization
  4441. */
  4442. func handleCloudSetup(resp http.ResponseWriter, request *http.Request) {
  4443. cors := shuffle.HandleCors(resp, request)
  4444. if cors {
  4445. return
  4446. }
  4447. user, err := shuffle.HandleApiAuthentication(resp, request)
  4448. if err != nil {
  4449. log.Printf("Api authentication failed in cloud setup: %s", err)
  4450. resp.WriteHeader(401)
  4451. resp.Write([]byte(`{"success": false}`))
  4452. return
  4453. }
  4454. if user.Role != "admin" {
  4455. log.Printf("Not admin.")
  4456. resp.WriteHeader(401)
  4457. resp.Write([]byte(`{"success": false, "reason": "Not admin"}`))
  4458. return
  4459. }
  4460. body, err := ioutil.ReadAll(request.Body)
  4461. if err != nil {
  4462. resp.WriteHeader(401)
  4463. resp.Write([]byte(`{"success": false, "reason": "Failed reading body"}`))
  4464. return
  4465. }
  4466. type ReturnData struct {
  4467. Apikey string `datastore:"apikey"`
  4468. Organization shuffle.Org `datastore:"organization"`
  4469. Disable bool `datastore:"disable"`
  4470. }
  4471. var tmpData ReturnData
  4472. err = json.Unmarshal(body, &tmpData)
  4473. if err != nil {
  4474. log.Printf("Failed unmarshalling test: %s", err)
  4475. resp.WriteHeader(401)
  4476. resp.Write([]byte(`{"success": false}`))
  4477. return
  4478. }
  4479. ctx := context.Background()
  4480. org, err := shuffle.GetOrg(ctx, tmpData.Organization.Id)
  4481. if err != nil {
  4482. log.Printf("Organization doesn't exist: %s", err)
  4483. resp.WriteHeader(401)
  4484. resp.Write([]byte(`{"success": false}`))
  4485. return
  4486. }
  4487. // FIXME: Check if user is admin of this org
  4488. //log.Printf("Checking org %s", org.Name)
  4489. userFound := false
  4490. admin := false
  4491. for _, inneruser := range org.Users {
  4492. if inneruser.Id == user.Id {
  4493. userFound = true
  4494. //log.Printf("[INFO] Role: %s", inneruser.Role)
  4495. if inneruser.Role == "admin" {
  4496. admin = true
  4497. }
  4498. break
  4499. }
  4500. }
  4501. if !userFound {
  4502. log.Printf("User %s doesn't exist in organization %s", user.Id, org.Id)
  4503. resp.WriteHeader(401)
  4504. resp.Write([]byte(`{"success": false}`))
  4505. return
  4506. }
  4507. // FIXME: Enable admin check in org for sync setup and conf.
  4508. _ = admin
  4509. //if !admin {
  4510. // log.Printf("User %s isn't admin hence can't set up sync for org %s", user.Id, org.Id)
  4511. // resp.WriteHeader(401)
  4512. // resp.Write([]byte(`{"success": false}`))
  4513. // return
  4514. //}
  4515. //log.Printf("Apidata: %s", tmpData.Apikey)
  4516. // FIXME: Path
  4517. // just in case setup/stop URL is overwritten by region url
  4518. if syncUrl != "https://shuffler.io" || syncUrl != "http://localhost:5002" {
  4519. syncUrl = "https://shuffler.io"
  4520. }
  4521. apiPath := "/api/v1/cloud/sync/setup"
  4522. if tmpData.Disable {
  4523. if !org.CloudSync {
  4524. log.Printf("[WARNING] Org %s isn't syncing. Can't stop.", org.Id)
  4525. resp.WriteHeader(401)
  4526. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Skipped cloud sync setup. Already syncing."}`)))
  4527. return
  4528. }
  4529. log.Printf("[INFO] Should disable sync for org %s", org.Id)
  4530. apiPath := "/api/v1/cloud/sync/stop"
  4531. syncPath := fmt.Sprintf("%s%s", syncUrl, apiPath)
  4532. _, err = handleStopCloudSync(syncPath, *org)
  4533. if err != nil {
  4534. ret := shuffle.ResultChecker{
  4535. Success: false,
  4536. Reason: fmt.Sprintf("%s", err),
  4537. }
  4538. resp.WriteHeader(401)
  4539. b, err := json.Marshal(ret)
  4540. if err != nil {
  4541. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  4542. return
  4543. }
  4544. resp.Write(b)
  4545. } else {
  4546. // Delete cache for the sync features
  4547. cacheKey := fmt.Sprintf("org_sync_features_%s", org.Id)
  4548. shuffle.DeleteCache(ctx, cacheKey)
  4549. subscriptionCacheKey := fmt.Sprintf("org_subscriptions_%s", org.Id)
  4550. shuffle.DeleteCache(ctx, subscriptionCacheKey)
  4551. licenseCacheKey := fmt.Sprintf("org_licensed_%s", org.Id)
  4552. shuffle.DeleteCache(ctx, licenseCacheKey)
  4553. resp.WriteHeader(200)
  4554. resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Successfully disabled cloud sync for org."}`)))
  4555. }
  4556. return
  4557. }
  4558. // Everything below here is to SET UP CLOUD SYNC.
  4559. // If you want to disable cloud sync, see previous section.
  4560. if org.CloudSync {
  4561. log.Printf("[WARNING] Org %s is already syncing. Skip", org.Id)
  4562. resp.WriteHeader(400)
  4563. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Your org is already syncing. Nothing to set up."}`)))
  4564. return
  4565. }
  4566. syncPath := fmt.Sprintf("%s%s", syncUrl, apiPath)
  4567. type requestStruct struct {
  4568. ApiKey string `json:"api_key"`
  4569. }
  4570. requestData := requestStruct{
  4571. ApiKey: tmpData.Apikey,
  4572. }
  4573. b, err := json.Marshal(requestData)
  4574. if err != nil {
  4575. log.Printf("[ERROR] Failed marshaling api key data: %s", err)
  4576. resp.WriteHeader(401)
  4577. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed cloud sync: %s"}`, err)))
  4578. return
  4579. }
  4580. req, err := http.NewRequest(
  4581. "POST",
  4582. syncPath,
  4583. bytes.NewBuffer(b),
  4584. )
  4585. client := shuffle.GetExternalClient(syncPath)
  4586. newresp, err := client.Do(req)
  4587. if err != nil {
  4588. resp.WriteHeader(400)
  4589. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed cloud sync: %s. Contact support."}`, err)))
  4590. //setBadMemcache(ctx, docPath)
  4591. return
  4592. }
  4593. respBody, err := ioutil.ReadAll(newresp.Body)
  4594. if err != nil {
  4595. resp.WriteHeader(500)
  4596. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't parse sync data. Contact support."}`)))
  4597. return
  4598. }
  4599. responseData := retStruct{}
  4600. err = json.Unmarshal(respBody, &responseData)
  4601. if err != nil {
  4602. resp.WriteHeader(500)
  4603. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed handling cloud data"}`)))
  4604. return
  4605. }
  4606. if newresp.StatusCode != 200 {
  4607. resp.WriteHeader(401)
  4608. resp.Write(respBody)
  4609. return
  4610. }
  4611. if !responseData.Success {
  4612. resp.WriteHeader(400)
  4613. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, responseData.Reason)))
  4614. return
  4615. }
  4616. // FIXME:
  4617. // 1. Set cloudsync for org to be active
  4618. // 2. Add iterative sync schedule for interval seconds
  4619. // 3. Add another environment for the org's users
  4620. org.CloudSync = true
  4621. // set cache here for 30 min
  4622. cacheKey := fmt.Sprintf("org_sync_features_%s", org.Id)
  4623. featuresBytes, err := json.Marshal(responseData.SyncFeatures)
  4624. if err != nil {
  4625. log.Printf("[ERROR] Failed to marshal SyncFeatures for cache: %s", err)
  4626. } else {
  4627. shuffle.SetCache(ctx, cacheKey, featuresBytes, 1800)
  4628. }
  4629. subscriptionCacheKey := fmt.Sprintf("org_subscriptions_%s", org.Id)
  4630. subscriptionsBytes, err := json.Marshal(responseData.Subscriptions)
  4631. if err != nil {
  4632. log.Printf("[ERROR] Failed to marshal Subscriptions for cache: %s", err)
  4633. } else {
  4634. shuffle.SetCache(ctx, subscriptionCacheKey, subscriptionsBytes, 1800)
  4635. }
  4636. licenseCacheKey := fmt.Sprintf("org_licensed_%s", org.Id)
  4637. licensedBytes, err := json.Marshal(responseData.Licensed)
  4638. if err != nil {
  4639. log.Printf("[ERROR] Failed to marshal Licensed for cache: %s", err)
  4640. } else {
  4641. shuffle.SetCache(ctx, licenseCacheKey, licensedBytes, 1800)
  4642. }
  4643. if len(responseData.CloudSyncUrl) > 0 {
  4644. cloudSyncRegionUrlCacheKey := fmt.Sprintf("org_cloudsync_region_url_%s", org.Id)
  4645. shuffle.SetCache(ctx, cloudSyncRegionUrlCacheKey, []byte(responseData.CloudSyncUrl), 1800)
  4646. }
  4647. org.SyncConfig = shuffle.SyncConfig{
  4648. Apikey: responseData.SessionKey,
  4649. Interval: responseData.IntervalSeconds,
  4650. WorkflowBackup: true,
  4651. AppBackup: true,
  4652. }
  4653. interval := int(responseData.IntervalSeconds)
  4654. log.Printf("[INFO] Starting cloud sync on interval %d", interval)
  4655. job := func() {
  4656. err := remoteOrgJobHandler(*org, interval)
  4657. if err != nil {
  4658. log.Printf("[ERROR] Failed request with remote org sync (1): %s", err)
  4659. }
  4660. }
  4661. jobret, err := newscheduler.Every(int(interval)).Seconds().NotImmediately().Run(job)
  4662. if err != nil {
  4663. log.Printf("[ERROR] Failed to schedule org: %s", err)
  4664. } else {
  4665. log.Printf("[INFO] Started sync on interval %d for org %s", interval, org.Name)
  4666. scheduledOrgs[org.Id] = jobret
  4667. }
  4668. // ONLY checked added if workflows are allow huh
  4669. if org.SyncFeatures.Workflows.Active {
  4670. log.Printf("[INFO] Should activate cloud workflows for org %s!", org.Id)
  4671. // 1. Find environment
  4672. // 2. If cloud env found, enable it (un-archive)
  4673. // 3. If it doesn't create it
  4674. environments, err := shuffle.GetEnvironments(ctx, org.Id)
  4675. log.Printf("GETTING ENVS: %#v", environments)
  4676. if err == nil {
  4677. // Don't disable, this will be deleted entirely
  4678. found := false
  4679. for _, environment := range environments {
  4680. if environment.Type == "cloud" {
  4681. environment.Name = "Cloud"
  4682. environment.Archived = false
  4683. err = shuffle.SetEnvironment(ctx, &environment)
  4684. if err == nil {
  4685. log.Printf("[INFO] Re-added cloud environment %s", environment.Name)
  4686. } else {
  4687. log.Printf("[INFO] Failed to re-enable cloud environment %s", environment.Name)
  4688. }
  4689. found = true
  4690. break
  4691. }
  4692. }
  4693. if !found {
  4694. log.Printf("[INFO] Env for cloud not found. Should add it!")
  4695. newEnv := shuffle.Environment{
  4696. Name: "Cloud",
  4697. Type: "cloud",
  4698. Archived: false,
  4699. Registered: true,
  4700. Default: false,
  4701. OrgId: org.Id,
  4702. Id: uuid.NewV4().String(),
  4703. }
  4704. err = shuffle.SetEnvironment(ctx, &newEnv)
  4705. if err != nil {
  4706. log.Printf("Failed setting up NEW org environment for org %s: %s", org.Id, err)
  4707. } else {
  4708. log.Printf("Successfully added new environment for org %s", org.Id)
  4709. }
  4710. }
  4711. } else {
  4712. log.Printf("Failed setting org environment, because none were found: %s", err)
  4713. }
  4714. }
  4715. err = shuffle.SetOrg(ctx, *org, org.Id)
  4716. if err != nil {
  4717. log.Printf("ERROR: Failed updating org even though there was success: %s", err)
  4718. resp.WriteHeader(400)
  4719. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed setting up org after sync success. Contact support."}`)))
  4720. return
  4721. }
  4722. if responseData.IntervalSeconds > 0 {
  4723. // FIXME:
  4724. log.Printf("[INFO] Should set up interval for %d with session key %s for org %s", responseData.IntervalSeconds, responseData.SessionKey, org.Name)
  4725. }
  4726. resp.WriteHeader(200)
  4727. resp.Write(respBody)
  4728. }
  4729. func makeWorkflowPublic(resp http.ResponseWriter, request *http.Request) {
  4730. cors := shuffle.HandleCors(resp, request)
  4731. if cors {
  4732. return
  4733. }
  4734. user, userErr := shuffle.HandleApiAuthentication(resp, request)
  4735. if userErr != nil {
  4736. log.Printf("[WARNING] Api authentication failed in make workflow public: %s", userErr)
  4737. resp.WriteHeader(401)
  4738. resp.Write([]byte(`{"success": false}`))
  4739. return
  4740. }
  4741. if user.Role == "org-reader" {
  4742. log.Printf("[WARNING] Org-reader doesn't have access publish workflow: %s (%s)", user.Username, user.Id)
  4743. resp.WriteHeader(401)
  4744. resp.Write([]byte(`{"success": false, "reason": "Read only user"}`))
  4745. return
  4746. }
  4747. location := strings.Split(request.URL.String(), "/")
  4748. var fileId string
  4749. if location[1] == "api" {
  4750. if len(location) <= 4 {
  4751. resp.WriteHeader(401)
  4752. resp.Write([]byte(`{"success": false}`))
  4753. return
  4754. }
  4755. fileId = location[4]
  4756. }
  4757. ctx := context.Background()
  4758. if strings.Contains(fileId, "?") {
  4759. fileId = strings.Split(fileId, "?")[0]
  4760. }
  4761. if len(fileId) != 36 {
  4762. resp.WriteHeader(401)
  4763. resp.Write([]byte(`{"success": false, "reason": "Workflow ID when getting workflow is not valid"}`))
  4764. return
  4765. }
  4766. workflow, err := shuffle.GetWorkflow(ctx, fileId)
  4767. if err != nil {
  4768. log.Printf("[WARNING] Workflow %s doesn't exist in app publish. User: %s", fileId, user.Id)
  4769. resp.WriteHeader(401)
  4770. resp.Write([]byte(`{"success": false}`))
  4771. return
  4772. }
  4773. // CHECK orgs of user, or if user is owner
  4774. // FIXME - add org check too, and not just owner
  4775. // Check workflow.Sharing == private / public / org too
  4776. if user.Id != workflow.Owner || len(user.Id) == 0 {
  4777. if workflow.OrgId == user.ActiveOrg.Id {
  4778. log.Printf("[AUDIT] User %s is accessing workflow %s as admin (public)", user.Username, workflow.ID)
  4779. } else {
  4780. log.Printf("[AUDIT] Wrong user (%s) for workflow %s (public)", user.Username, workflow.ID)
  4781. resp.WriteHeader(401)
  4782. resp.Write([]byte(`{"success": false}`))
  4783. return
  4784. }
  4785. }
  4786. if !workflow.IsValid || !workflow.PreviouslySaved {
  4787. log.Printf("[INFO] Failed uploading workflow because it's invalid or not saved")
  4788. resp.WriteHeader(401)
  4789. resp.Write([]byte(`{"success": false, "reason": "Invalid workflows are not sharable"}`))
  4790. return
  4791. }
  4792. // Starting validation of the POST workflow
  4793. body, err := ioutil.ReadAll(request.Body)
  4794. if err != nil {
  4795. log.Printf("[WARNING] Body data error on mail: %s", err)
  4796. resp.WriteHeader(401)
  4797. resp.Write([]byte(`{"success": false}`))
  4798. return
  4799. }
  4800. parsedWorkflow := shuffle.Workflow{}
  4801. err = json.Unmarshal(body, &parsedWorkflow)
  4802. if err != nil {
  4803. log.Printf("[WARNING] Unmarshal error on mail: %s", err)
  4804. resp.WriteHeader(401)
  4805. resp.Write([]byte(`{"success": false}`))
  4806. return
  4807. }
  4808. // Super basic validation. Doesn't really matter.
  4809. if parsedWorkflow.ID != workflow.ID || len(parsedWorkflow.Actions) != len(workflow.Actions) {
  4810. log.Printf("[WARNING] Bad ID during publish: %s vs %s", workflow.ID, parsedWorkflow.ID)
  4811. resp.WriteHeader(401)
  4812. resp.Write([]byte(`{"success": false}`))
  4813. return
  4814. }
  4815. if !workflow.IsValid || !workflow.PreviouslySaved {
  4816. log.Printf("[INFO] Failed uploading new workflow because it's invalid or not saved")
  4817. resp.WriteHeader(401)
  4818. resp.Write([]byte(`{"success": false, "reason": "Invalid workflows are not sharable"}`))
  4819. return
  4820. }
  4821. workflowData, err := json.Marshal(parsedWorkflow)
  4822. if err != nil {
  4823. log.Printf("[WARNING] Failed marshalling workflow: %s", err)
  4824. resp.WriteHeader(401)
  4825. resp.Write([]byte(`{"success": false}`))
  4826. return
  4827. }
  4828. // Sanitization is done in the frontend as well
  4829. parsedWorkflow = shuffle.SanitizeWorkflow(parsedWorkflow)
  4830. parsedWorkflow.ID = uuid.NewV4().String()
  4831. action := shuffle.CloudSyncJob{
  4832. Type: "workflow",
  4833. Action: "publish",
  4834. OrgId: user.ActiveOrg.Id,
  4835. PrimaryItemId: workflow.ID,
  4836. SecondaryItem: string(workflowData),
  4837. FifthItem: user.Id,
  4838. }
  4839. org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
  4840. if err != nil {
  4841. log.Printf("[WARNING] Failed setting getting org during cloud job setting: %s", err)
  4842. resp.WriteHeader(401)
  4843. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  4844. return
  4845. }
  4846. err = executeCloudAction(action, org.SyncConfig.Apikey)
  4847. if err != nil {
  4848. log.Printf("[WARNING] Failed cloud PUBLISH: %s", err)
  4849. resp.WriteHeader(401)
  4850. resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
  4851. return
  4852. }
  4853. log.Printf("[INFO] Successfully published workflow %s (%s) TO CLOUD", workflow.Name, workflow.ID)
  4854. resp.WriteHeader(200)
  4855. resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
  4856. }
  4857. func handleAppZipUpload(resp http.ResponseWriter, request *http.Request) {
  4858. cors := shuffle.HandleCors(resp, request)
  4859. if cors {
  4860. return
  4861. }
  4862. resp.WriteHeader(500)
  4863. resp.Write([]byte(`{"success": false, "reason": "The upload API is not yet implemented in self-hosted Shuffle. Please explore the app hotloading system: https://shuffler.io/docs/app_creation#uploading-an-app"}`))
  4864. return
  4865. //https://stackoverflow.com/questions/22964950/http-request-formfile-handle-zip-files
  4866. request.ParseMultipartForm(32 << 20)
  4867. f, _, err := request.FormFile("shuffle_file")
  4868. if err != nil {
  4869. log.Printf("[ERROR] Couldn't upload file: %s", err)
  4870. resp.WriteHeader(401)
  4871. resp.Write([]byte(`{"success": false, "reason": "Failed uploading file. Correct usage is: shuffle_file=@filepath"}`))
  4872. return
  4873. }
  4874. fileSize, err := f.Seek(0, 2) //2 = from end
  4875. if err != nil {
  4876. panic(err)
  4877. }
  4878. _, err = f.Seek(0, 0)
  4879. if err != nil {
  4880. panic(err)
  4881. }
  4882. buf := new(bytes.Buffer)
  4883. fileSize, err = io.Copy(buf, f)
  4884. if err != nil {
  4885. panic(err)
  4886. }
  4887. zipdata, err := zip.NewReader(bytes.NewReader(buf.Bytes()), fileSize)
  4888. if err != nil {
  4889. panic(err)
  4890. }
  4891. // https://github.com/alexmullins/zip/blob/master/example_test.go
  4892. for _, item := range zipdata.File {
  4893. log.Printf("\n\nName: %s\n\n", item.FileHeader.Name)
  4894. log.Printf("item: %#v", item)
  4895. rr, err := item.Open()
  4896. if err != nil {
  4897. log.Fatal(err)
  4898. }
  4899. _, err = io.Copy(os.Stdout, rr)
  4900. if err != nil {
  4901. log.Fatal(err)
  4902. }
  4903. rr.Close()
  4904. }
  4905. resp.WriteHeader(200)
  4906. resp.Write([]byte("OK"))
  4907. }
  4908. func initHandlers() {
  4909. var err error
  4910. ctx := context.Background()
  4911. CronScheduler.StartAsync()
  4912. log.Printf("[DEBUG] Starting Shuffle backend - initializing database connection")
  4913. //requestCache = cache.New(5*time.Minute, 10*time.Minute)
  4914. //es := shuffle.GetEsConfig()
  4915. elasticConfig := "elasticsearch"
  4916. if strings.ToLower(os.Getenv("SHUFFLE_ELASTIC")) == "false" {
  4917. elasticConfig = ""
  4918. }
  4919. for {
  4920. _, err = shuffle.RunInit(*shuffle.GetDatastore(), *shuffle.GetStorage(), gceProject, "onprem", true, elasticConfig, false, 0)
  4921. if err != nil {
  4922. log.Printf("[ERROR] Error in initial database connection. Retrying in 5 seconds. %s", err)
  4923. time.Sleep(5 * time.Second)
  4924. continue
  4925. }
  4926. break
  4927. }
  4928. log.Printf("[DEBUG] Initialized Shuffle database connection. Setting up environment.")
  4929. if elasticConfig == "elasticsearch" {
  4930. time.Sleep(10 * time.Second)
  4931. go runInitEs(ctx)
  4932. } else {
  4933. //go shuffle.runInit(ctx)
  4934. log.Printf("[ERROR] Opensearch is the only viable option. Please set SHUFFLE_ELASTIC=true")
  4935. os.Exit(1)
  4936. }
  4937. r := mux.NewRouter()
  4938. r.HandleFunc("/api/v1/_ah/health", shuffle.HealthCheckHandler)
  4939. r.HandleFunc("/api/v1/health", shuffle.RunOpsHealthCheck).Methods("GET", "OPTIONS")
  4940. r.HandleFunc("/api/v1/health/stats", shuffle.GetOpsDashboardStats).Methods("GET", "OPTIONS")
  4941. r.HandleFunc("/api/v1/health/opensearch-prefix", shuffle.HandleFixOpensearchPrefix).Methods("POST", "OPTIONS")
  4942. // Make user related locations
  4943. // Fix user changes with org
  4944. r.HandleFunc("/api/v1/users/login", shuffle.HandleLogin).Methods("POST", "OPTIONS")
  4945. r.HandleFunc("/api/v1/users/register", handleRegister).Methods("POST", "OPTIONS")
  4946. r.HandleFunc("/api/v1/users/checkusers", checkAdminLogin).Methods("GET", "OPTIONS")
  4947. r.HandleFunc("/api/v1/users/getinfo", handleInfo).Methods("GET", "OPTIONS")
  4948. r.HandleFunc("/api/v1/users/{userId}/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
  4949. r.HandleFunc("/api/v1/users/apps", shuffle.HandleGetUserApps).Methods("GET", "OPTIONS")
  4950. r.HandleFunc("/api/v1/users/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
  4951. r.HandleFunc("/api/v1/users/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS")
  4952. r.HandleFunc("/api/v1/users/getsettings", shuffle.HandleSettings).Methods("GET", "OPTIONS")
  4953. r.HandleFunc("/api/v1/users/getusers", shuffle.HandleGetUsers).Methods("GET", "OPTIONS")
  4954. r.HandleFunc("/api/v1/users/updateuser", shuffle.HandleUpdateUser).Methods("PUT", "OPTIONS")
  4955. // r.HandleFunc("/api/v1/users/{userID}/remove", shuffle.HandleDeleteUsersAccount).Methods("DELETE", "OPTIONS")
  4956. r.HandleFunc("/api/v1/users/{user}", shuffle.DeleteUser).Methods("DELETE", "OPTIONS")
  4957. r.HandleFunc("/api/v1/users/passwordchange", shuffle.HandlePasswordChange).Methods("POST", "OPTIONS")
  4958. r.HandleFunc("/api/v1/users/{key}/get2fa", shuffle.HandleGet2fa).Methods("GET", "OPTIONS")
  4959. r.HandleFunc("/api/v1/users/{key}/set2fa", shuffle.HandleSet2fa).Methods("POST", "OPTIONS")
  4960. r.HandleFunc("/api/v1/users", shuffle.HandleGetUsers).Methods("GET", "OPTIONS")
  4961. // General - duplicates and old.
  4962. r.HandleFunc("/api/v1/getusers", shuffle.HandleGetUsers).Methods("GET", "OPTIONS")
  4963. r.HandleFunc("/api/v1/login", shuffle.HandleLogin).Methods("POST", "OPTIONS")
  4964. r.HandleFunc("/api/v1/logout", shuffle.HandleLogout).Methods("POST", "OPTIONS")
  4965. r.HandleFunc("/api/v1/register", handleRegister).Methods("POST", "OPTIONS")
  4966. r.HandleFunc("/api/v1/checkusers", checkAdminLogin).Methods("GET", "OPTIONS")
  4967. r.HandleFunc("/api/v1/getinfo", handleInfo).Methods("GET", "OPTIONS")
  4968. r.HandleFunc("/api/v1/me", handleInfo).Methods("GET", "OPTIONS")
  4969. r.HandleFunc("/api/v1/getsettings", shuffle.HandleSettings).Methods("GET", "OPTIONS")
  4970. r.HandleFunc("/api/v1/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
  4971. r.HandleFunc("/api/v1/passwordchange", shuffle.HandlePasswordChange).Methods("POST", "OPTIONS")
  4972. r.HandleFunc("/api/v1/getenvironments", shuffle.HandleGetEnvironments).Methods("GET", "OPTIONS")
  4973. r.HandleFunc("/api/v1/setenvironments", shuffle.HandleSetEnvironments).Methods("PUT", "OPTIONS")
  4974. r.HandleFunc("/api/v1/docs", shuffle.GetDocList).Methods("GET", "OPTIONS")
  4975. r.HandleFunc("/api/v1/docs/{key}", shuffle.GetDocs).Methods("GET", "OPTIONS")
  4976. // Queuebuilder and Workflow streams. First is to update a stream, second to get a stream
  4977. // Changed from workflows/streams to streams, as appengine was messing up
  4978. // This does not increase the API counter
  4979. // Used by frontend
  4980. r.HandleFunc("/api/v1/streams", handleSetWorkflowExecution).Methods("POST")
  4981. r.HandleFunc("/api/v1/streams/results", handleGetWorkflowExecutionResult).Methods("POST", "OPTIONS")
  4982. // Used by orborus
  4983. r.HandleFunc("/api/v1/workflows/queue", handleGetWorkflowqueue).Methods("GET", "POST")
  4984. r.HandleFunc("/api/v1/workflows/queue/confirm", handleGetWorkflowqueueConfirm).Methods("POST")
  4985. // App specific. Partially Singul.
  4986. r.HandleFunc("/api/v1/apps/categories", shuffle.GetActiveCategories).Methods("GET", "OPTIONS")
  4987. r.HandleFunc("/api/v1/apps/categories/run", singul.RunCategoryAction).Methods("POST", "OPTIONS")
  4988. r.HandleFunc("/api/v1/apps/{key}/execute", executeSingleAction).Methods("POST", "OPTIONS")
  4989. r.HandleFunc("/api/v1/apps/{key}/run", executeSingleAction).Methods("POST", "OPTIONS")
  4990. // Agent / MCP actions
  4991. r.HandleFunc("/api/v1/apps/{key}/mcp", runMCPAction).Methods("POST", "OPTIONS")
  4992. r.HandleFunc("/api/v1/mcp", runMCPAction).Methods("POST", "OPTIONS")
  4993. r.HandleFunc("/api/v1/agent", runMCPAction).Methods("POST", "OPTIONS")
  4994. //r.HandleFunc("/api/v1/apps/categories/run", shuffle.RunCategoryAction).Methods("POST", "OPTIONS")
  4995. r.HandleFunc("/api/v1/apps/upload", handleAppZipUpload).Methods("POST", "OPTIONS")
  4996. r.HandleFunc("/api/v1/apps/{appId}/activate", activateWorkflowAppDocker).Methods("GET", "OPTIONS")
  4997. r.HandleFunc("/api/v1/apps/{appId}/deactivate", activateWorkflowAppDocker).Methods("GET", "OPTIONS")
  4998. r.HandleFunc("/api/v1/apps/{appId}/distribute", activateWorkflowAppDocker).Methods("GET", "OPTIONS")
  4999. r.HandleFunc("/api/v1/apps/frameworkConfiguration", shuffle.GetFrameworkConfiguration).Methods("GET", "OPTIONS")
  5000. r.HandleFunc("/api/v1/apps/frameworkConfiguration", shuffle.SetFrameworkConfiguration).Methods("POST", "OPTIONS")
  5001. r.HandleFunc("/api/v1/apps/{appId}", shuffle.UpdateWorkflowAppConfig).Methods("PATCH", "OPTIONS")
  5002. r.HandleFunc("/api/v1/apps/{appId}", shuffle.DeleteWorkflowApp).Methods("DELETE", "OPTIONS")
  5003. r.HandleFunc("/api/v1/apps/{appId}/config", shuffle.GetWorkflowAppConfig).Methods("GET", "OPTIONS")
  5004. r.HandleFunc("/api/v1/apps/run_hotload", handleAppHotloadRequest).Methods("GET", "POST", "OPTIONS")
  5005. r.HandleFunc("/api/v1/apps/{appName}/run_hotload", handleSingleAppHotloadRequest).Methods("POST", "OPTIONS")
  5006. r.HandleFunc("/api/v1/apps/get_existing", LoadSpecificApps).Methods("POST", "OPTIONS")
  5007. r.HandleFunc("/api/v1/apps/download_remote", LoadSpecificApps).Methods("POST", "OPTIONS")
  5008. r.HandleFunc("/api/v1/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
  5009. r.HandleFunc("/api/v1/apps", getWorkflowApps).Methods("GET", "OPTIONS")
  5010. r.HandleFunc("/api/v1/apps", setNewWorkflowApp).Methods("PUT", "OPTIONS")
  5011. r.HandleFunc("/api/v1/apps/search", getSpecificApps).Methods("POST", "OPTIONS")
  5012. r.HandleFunc("/api/v1/apps/authentication", shuffle.GetAppAuthentication).Methods("GET", "OPTIONS")
  5013. r.HandleFunc("/api/v1/apps/authentication", shuffle.AddAppAuthentication).Methods("PUT", "OPTIONS")
  5014. r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", shuffle.SetAuthenticationConfig).Methods("POST", "OPTIONS")
  5015. r.HandleFunc("/api/v1/apps/authentication/{appauthId}", shuffle.DeleteAppAuthentication).Methods("DELETE", "OPTIONS")
  5016. r.HandleFunc("/api/v1/authentication/group", shuffle.AddAppAuthenticationGroup).Methods("POST", "OPTIONS")
  5017. r.HandleFunc("/api/v1/authentication/group", shuffle.GetAppAuthenticationGroup).Methods("GET", "OPTIONS")
  5018. r.HandleFunc("/api/v1/authentication/group/{key}", shuffle.DeleteAppAuthenticationGroup).Methods("DELETE", "OPTIONS")
  5019. r.HandleFunc("/api/v1/authentication/groups", shuffle.AddAppAuthenticationGroup).Methods("POST", "OPTIONS")
  5020. r.HandleFunc("/api/v1/authentication/groups", shuffle.GetAppAuthenticationGroup).Methods("GET", "OPTIONS")
  5021. r.HandleFunc("/api/v1/authentication/groups/{key}", shuffle.DeleteAppAuthenticationGroup).Methods("DELETE", "OPTIONS")
  5022. // Related to use-cases that are not directly workflows.
  5023. r.HandleFunc("/api/v1/workflows/usecases/{key}", shuffle.HandleGetUsecase).Methods("GET", "OPTIONS")
  5024. r.HandleFunc("/api/v1/workflows/usecases", shuffle.LoadUsecases).Methods("GET", "OPTIONS")
  5025. r.HandleFunc("/api/v1/workflows/usecases", shuffle.UpdateUsecases).Methods("POST", "OPTIONS")
  5026. // Legacy app things
  5027. r.HandleFunc("/api/v1/workflows/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
  5028. r.HandleFunc("/api/v1/workflows/apps", getWorkflowApps).Methods("GET", "OPTIONS")
  5029. r.HandleFunc("/api/v1/workflows/apps", setNewWorkflowApp).Methods("PUT", "OPTIONS")
  5030. // Workflows
  5031. // FIXME - implement the queue counter lol
  5032. /* Everything below here increases the counters*/
  5033. r.HandleFunc("/api/v1/workflows", shuffle.GetWorkflows).Methods("GET", "OPTIONS")
  5034. r.HandleFunc("/api/v1/workflows", shuffle.SetNewWorkflow).Methods("POST", "OPTIONS")
  5035. r.HandleFunc("/api/v1/workflows/search", shuffle.HandleWorkflowRunSearch).Methods("POST", "OPTIONS")
  5036. r.HandleFunc("/api/v1/workflows/schedules", shuffle.HandleGetSchedules).Methods("GET", "OPTIONS")
  5037. r.HandleFunc("/api/v1/workflows/{key}/executions", shuffle.GetWorkflowExecutions).Methods("GET", "OPTIONS")
  5038. r.HandleFunc("/api/v1/workflows/{key}/executions/count", shuffle.HandleGetWorkflowRunCount).Methods("GET", "OPTIONS")
  5039. r.HandleFunc("/api/v1/workflows/{key}/executions/{key}/rerun", checkUnfinishedExecution).Methods("GET", "POST", "OPTIONS")
  5040. r.HandleFunc("/api/v1/workflows/{key}/executions/{key}/abort", shuffle.AbortExecution).Methods("GET", "OPTIONS")
  5041. r.HandleFunc("/api/v1/workflows/{key}/schedule", scheduleWorkflow).Methods("POST", "OPTIONS")
  5042. r.HandleFunc("/api/v1/workflows/download_remote", shuffle.LoadSpecificWorkflows).Methods("POST", "OPTIONS")
  5043. r.HandleFunc("/api/v1/workflows/{key}/run", executeWorkflow).Methods("GET", "POST", "OPTIONS")
  5044. r.HandleFunc("/api/v1/workflows/{key}/execute", executeWorkflow).Methods("GET", "POST", "OPTIONS")
  5045. r.HandleFunc("/api/v1/workflows/{key}/schedule/{schedule}", stopSchedule).Methods("DELETE", "OPTIONS")
  5046. r.HandleFunc("/api/v1/workflows/{key}/stream", shuffle.HandleStreamWorkflow).Methods("GET", "OPTIONS")
  5047. r.HandleFunc("/api/v1/workflows/{key}/stream", shuffle.HandleStreamWorkflowUpdate).Methods("POST", "OPTIONS")
  5048. r.HandleFunc("/api/v1/workflows/{key}/duplicate", shuffle.DuplicateWorkflow).Methods("POST", "OPTIONS")
  5049. r.HandleFunc("/api/v1/workflows/{key}", deleteWorkflow).Methods("DELETE", "OPTIONS")
  5050. r.HandleFunc("/api/v1/workflows/{key}", shuffle.SaveWorkflow).Methods("PUT", "OPTIONS")
  5051. r.HandleFunc("/api/v1/workflows/{key}", shuffle.GetSpecificWorkflow).Methods("GET", "OPTIONS")
  5052. r.HandleFunc("/api/v1/workflows/recommend", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS")
  5053. // First v2 API
  5054. r.HandleFunc("/api/v2/workflows/{key}/executions", shuffle.GetWorkflowExecutionsV2).Methods("GET", "OPTIONS")
  5055. r.HandleFunc("/api/v2/workflows/generate/llm", shuffle.HandleWorkflowGenerationResponse).Methods("POST", "OPTIONS")
  5056. r.HandleFunc("/api/v2/workflows/edit/llm", shuffle.HandleEditWorkflowWithLLM).Methods("POST", "OPTIONS")
  5057. r.HandleFunc("/api/v2/workflows/generate", shuffle.GenerateSingulWorkflows).Methods("POST", "OPTIONS")
  5058. r.HandleFunc("/api/v2/datastore", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
  5059. r.HandleFunc("/api/v2/datastore", shuffle.HandleSetDatastoreKey).Methods("POST", "OPTIONS")
  5060. r.HandleFunc("/api/v2/datastore/category/{category_key}", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
  5061. r.HandleFunc("/api/v2/datastore/automate", shuffle.HandleDatastoreCategoryConfig).Methods("POST", "OPTIONS")
  5062. // New for recommendations in Shuffle
  5063. r.HandleFunc("/api/v1/recommendations/get_actions", shuffle.HandleActionRecommendation).Methods("POST", "OPTIONS")
  5064. r.HandleFunc("/api/v1/recommendations/modify", shuffle.HandleRecommendationAction).Methods("POST", "OPTIONS")
  5065. r.HandleFunc("/api/v1/workflows/{key}/revisions", shuffle.GetWorkflowRevisions).Methods("GET", "OPTIONS")
  5066. r.HandleFunc("/api/v1/workflows/{key}/child_workflows", shuffle.GetChildWorkflows).Methods("GET", "OPTIONS")
  5067. // Triggers
  5068. r.HandleFunc("/api/v1/hooks/new", shuffle.HandleNewHook).Methods("POST", "OPTIONS")
  5069. r.HandleFunc("/api/v1/hooks", shuffle.HandleNewHook).Methods("POST", "OPTIONS")
  5070. r.HandleFunc("/api/v1/hooks/{key}", handleWebhookCallback).Methods("POST", "GET", "PATCH", "PUT", "DELETE", "OPTIONS")
  5071. r.HandleFunc("/api/v1/hooks/{key}/delete", shuffle.HandleDeleteHook).Methods("DELETE", "OPTIONS")
  5072. r.HandleFunc("/api/v1/hooks/{key}", shuffle.HandleDeleteHook).Methods("DELETE", "OPTIONS")
  5073. // This structure is horrendous. Needs fixing after we got the prototype up
  5074. r.HandleFunc("/api/v1/detections", shuffle.HandleListDetectionCategories).Methods("GET", "OPTIONS")
  5075. r.HandleFunc("/api/v1/detections/{detectionType}/connect", shuffle.HandleDetectionAutoConnect).Methods("GET", "OPTIONS")
  5076. r.HandleFunc("/api/v1/detections/{detection_type}", shuffle.HandleGetDetectionRules).Methods("GET", "OPTIONS")
  5077. r.HandleFunc("/api/v1/detections/{detection_type}/selected_rules/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS")
  5078. r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET", "OPTIONS")
  5079. r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST", "OPTIONS")
  5080. r.HandleFunc("/api/v1/detections/{detection_type}/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS")
  5081. // OpenAPI configuration
  5082. r.HandleFunc("/api/v1/verify_swagger", verifySwagger).Methods("POST", "OPTIONS")
  5083. r.HandleFunc("/api/v1/verify_openapi", verifySwagger).Methods("POST", "OPTIONS")
  5084. r.HandleFunc("/api/v1/get_openapi_uri", shuffle.EchoOpenapiData).Methods("POST", "OPTIONS")
  5085. r.HandleFunc("/api/v1/validate_openapi", shuffle.ValidateSwagger).Methods("POST", "OPTIONS")
  5086. r.HandleFunc("/api/v1/get_openapi/{key}", getOpenapi).Methods("GET", "OPTIONS")
  5087. // Specific triggers
  5088. r.HandleFunc("/api/v1/workflows/{key}/outlook", shuffle.HandleCreateOutlookSub).Methods("POST", "OPTIONS")
  5089. r.HandleFunc("/api/v1/workflows/{key}/outlook/{triggerId}", shuffle.HandleDeleteOutlookSub).Methods("DELETE", "OPTIONS")
  5090. r.HandleFunc("/api/v1/triggers/outlook/register", shuffle.HandleNewOutlookRegister).Methods("GET", "OPTIONS")
  5091. r.HandleFunc("/api/v1/triggers/outlook/getFolders", shuffle.HandleGetOutlookFolders).Methods("GET", "OPTIONS")
  5092. r.HandleFunc("/api/v1/triggers/outlook/{key}", shuffle.HandleGetSpecificTrigger).Methods("GET", "OPTIONS")
  5093. r.HandleFunc("/api/v1/triggers/gmail/register", shuffle.HandleNewGmailRegister).Methods("GET", "OPTIONS")
  5094. r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS")
  5095. r.HandleFunc("/api/v1/triggers/pipeline", shuffle.HandleNewPipelineRegister).Methods("POST", "OPTIONS")
  5096. r.HandleFunc("/api/v1/triggers/github/register", shuffle.HandleNewGithubRegister).Methods("PUT", "OPTIONS")
  5097. //r.HandleFunc("/api/v1/triggers/pipeline/save", shuffle.HandleSavePipelineInfo).Methods("PUT", "OPTIONS")
  5098. r.HandleFunc("/api/v1/pipelines/{key}", handlePipelineCallback).Methods("POST", "GET", "PATCH", "PUT", "DELETE", "OPTIONS")
  5099. r.HandleFunc("/api/v1/triggers", shuffle.HandleGetTriggers).Methods("GET", "OPTIONS")
  5100. //r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS")
  5101. r.HandleFunc("/api/v1/triggers/gmail/{key}", shuffle.HandleGetSpecificTrigger).Methods("GET", "OPTIONS")
  5102. r.HandleFunc("/api/v1/workflows/{key}/gmail", shuffle.HandleCreateGmailSub).Methods("POST", "OPTIONS")
  5103. r.HandleFunc("/api/v1/workflows/{key}/gmail/{triggerId}", shuffle.HandleDeleteGmailSub).Methods("DELETE", "OPTIONS")
  5104. //r.HandleFunc("/api/v1/triggers/gmail/{key}", handleGetSpecificGmailTrigger).Methods("GET", "OPTIONS")
  5105. //r.HandleFunc("/api/v1/triggers/outlook/getFolders", shuffle.HandleGetOutlookFolders).Methods("GET", "OPTIONS")
  5106. //r.HandleFunc("/api/v1/triggers/outlook/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS")
  5107. //r.HandleFunc("/api/v1/triggers/outlook/{key}/callback", handleOutlookCallback).Methods("POST", "OPTIONS")
  5108. //r.HandleFunc("/api/v1/stats/{key}", handleGetSpecificStats).Methods("GET", "OPTIONS")
  5109. // EVERYTHING below here is NEW for 0.8.0 (written 25.05.2021)
  5110. r.HandleFunc("/api/v1/workflows/{key}/publish", makeWorkflowPublic).Methods("POST", "OPTIONS")
  5111. r.HandleFunc("/api/v1/cloud/setup", handleCloudSetup).Methods("POST", "OPTIONS")
  5112. r.HandleFunc("/api/v1/orgs", shuffle.HandleGetOrgs).Methods("GET", "OPTIONS")
  5113. r.HandleFunc("/api/v1/orgs/", shuffle.HandleGetOrgs).Methods("GET", "OPTIONS")
  5114. r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleGetOrg).Methods("GET", "OPTIONS")
  5115. r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleEditOrg).Methods("POST", "OPTIONS")
  5116. r.HandleFunc("/api/v1/orgs/{orgid}/forms", shuffle.HandleGetOrgForms).Methods("GET", "OPTIONS")
  5117. r.HandleFunc("/api/v1/orgs/{orgId}/create_sub_org", shuffle.HandleCreateSubOrg).Methods("POST", "OPTIONS")
  5118. r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org
  5119. r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleDeleteOrg).Methods("DELETE", "OPTIONS")
  5120. r.HandleFunc("/api/v1/orgs/{orgId}/suborgs", shuffle.HandleGetSubOrgs).Methods("GET", "OPTIONS")
  5121. // This is a new API that validates if a key has been seen before.
  5122. // Not sure what the best course of action is for it.
  5123. r.HandleFunc("/api/v1/getenvironments", shuffle.HandleGetEnvironments).Methods("GET", "OPTIONS")
  5124. r.HandleFunc("/api/v1/setenvironments", shuffle.HandleSetEnvironments).Methods("PUT", "OPTIONS")
  5125. r.HandleFunc("/api/v1/environments/{key}/stop", shuffle.HandleStopExecutions).Methods("GET", "POST", "OPTIONS")
  5126. r.HandleFunc("/api/v1/environments/{key}/rerun", shuffle.HandleRerunExecutions).Methods("GET", "POST", "OPTIONS")
  5127. r.HandleFunc("/api/v1/environments/{key}/stats", shuffle.HandleGetenvStats).Methods("GET", "OPTIONS")
  5128. r.HandleFunc("/api/v1/environments/{key}/config", shuffle.HandleSetenvConfig).Methods("POST", "OPTIONS")
  5129. r.HandleFunc("/api/v1/environments", shuffle.HandleGetEnvironments).Methods("GET", "OPTIONS")
  5130. r.HandleFunc("/api/v1/orgs/{orgId}/validate_app_values", shuffle.HandleKeyValueCheck).Methods("POST", "OPTIONS")
  5131. r.HandleFunc("/api/v1/orgs/{orgId}/list_cache", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
  5132. r.HandleFunc("/api/v1/orgs/{orgId}/cache/{cache_key}", shuffle.HandleGetCacheKey).Methods("GET", "OPTIONS")
  5133. r.HandleFunc("/api/v1/orgs/{orgId}/get_cache", shuffle.HandleGetCacheKey).Methods("POST", "OPTIONS")
  5134. r.HandleFunc("/api/v1/orgs/{orgId}/set_cache", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
  5135. r.HandleFunc("/api/v1/orgs/{orgId}/delete_cache", shuffle.HandleDeleteCacheKeyPost).Methods("POST", "OPTIONS")
  5136. r.HandleFunc("/api/v1/orgs/{orgId}/cache/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
  5137. r.HandleFunc("/api/v1/orgs/{orgId}/cache/config", shuffle.HandleCacheConfig).Methods("POST", "OPTIONS")
  5138. r.HandleFunc("/api/v1/orgs/{orgId}/stats", shuffle.HandleGetStatistics).Methods("GET", "OPTIONS")
  5139. r.HandleFunc("/api/v1/orgs/{orgId}/stats", shuffle.HandleAppendStatistics).Methods("POST", "OPTIONS")
  5140. r.HandleFunc("/api/v1/orgs/{orgId}/stats/{key}", shuffle.GetSpecificStats).Methods("GET", "OPTIONS")
  5141. r.HandleFunc("/api/v1/orgs/{orgId}/statistics", shuffle.HandleGetStatistics).Methods("GET", "OPTIONS")
  5142. r.HandleFunc("/api/v1/stats", shuffle.HandleGetStatistics).Methods("GET", "OPTIONS")
  5143. r.HandleFunc("/api/v1/stats", shuffle.HandleAppendStatistics).Methods("POST", "OPTIONS")
  5144. r.HandleFunc("/api/v1/stats/{key}", shuffle.GetSpecificStats).Methods("GET", "OPTIONS")
  5145. r.HandleFunc("/api/v1/orgs/{orgId}/cache", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
  5146. r.HandleFunc("/api/v1/orgs/{orgId}/cache", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
  5147. r.HandleFunc("/api/v1/orgs/{orgId}/cache/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
  5148. r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleListCacheKeys).Methods("GET", "OPTIONS")
  5149. r.HandleFunc("/api/v1/orgs/{orgId}/datastore", shuffle.HandleSetCacheKey).Methods("POST", "OPTIONS")
  5150. r.HandleFunc("/api/v1/orgs/{orgId}/datastore/{cache_key}", shuffle.HandleDeleteCacheKey).Methods("DELETE", "OPTIONS")
  5151. // Docker orborus specific - downloads an image
  5152. r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "GET", "OPTIONS")
  5153. r.HandleFunc("/api/v1/login_sso", shuffle.HandleSAML).Methods("GET", "POST", "OPTIONS")
  5154. r.HandleFunc("/api/v1/login_openid", shuffle.HandleOpenId).Methods("GET", "POST", "OPTIONS")
  5155. // Important for email, IDS etc. Create this by:
  5156. // PS: For cloud, this has to use cloud storage.
  5157. // https://developer.box.com/reference/get-files-id-content/
  5158. r.HandleFunc("/api/v1/files/download_remote", shuffle.HandleDownloadRemoteFiles).Methods("POST", "OPTIONS")
  5159. r.HandleFunc("/api/v1/files/namespaces/{namespace}", shuffle.HandleGetFileNamespace).Methods("GET", "OPTIONS")
  5160. r.HandleFunc("/api/v1/files/{fileId}/content", shuffle.HandleGetFileContent).Methods("GET", "OPTIONS")
  5161. r.HandleFunc("/api/v1/files/create", shuffle.HandleCreateFile).Methods("POST", "OPTIONS")
  5162. r.HandleFunc("/api/v1/files/{fileId}/upload", shuffle.HandleUploadFile).Methods("POST", "OPTIONS", "PATCH")
  5163. r.HandleFunc("/api/v1/files/{fileId}/edit", shuffle.HandleEditFile).Methods("PUT", "OPTIONS")
  5164. r.HandleFunc("/api/v1/files/{fileId}", shuffle.HandleGetFileMeta).Methods("GET", "OPTIONS")
  5165. r.HandleFunc("/api/v1/files/{fileId}", shuffle.HandleDeleteFile).Methods("DELETE", "OPTIONS")
  5166. r.HandleFunc("/api/v1/files", shuffle.HandleGetFiles).Methods("GET", "OPTIONS")
  5167. r.HandleFunc("/api/v1/files/{fileId}/config", shuffle.HandleSetFileConfig).Methods("POST", "OPTIONS")
  5168. r.HandleFunc("/api/v1/files/namespaces/{namespace}/share", shuffle.HandleShareNamespace).Methods("POST", "OPTIONS")
  5169. // This structure is horrendous. Needs fixing after we got the prototype up
  5170. r.HandleFunc("/api/v1/detections/{detectionType}/connect", shuffle.HandleDetectionAutoConnect).Methods("GET", "OPTIONS")
  5171. r.HandleFunc("/api/v1/detections/{detection_type}", shuffle.HandleGetDetectionRules).Methods("GET", "OPTIONS")
  5172. r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET", "OPTIONS")
  5173. r.HandleFunc("/api/v1/detections/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST", "OPTIONS")
  5174. r.HandleFunc("/api/v1/detections/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS")
  5175. // This is weird.
  5176. r.HandleFunc("/api/v1/detections/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS")
  5177. //r.HandleFunc("/api/v1/detections/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS")
  5178. // Introduced in 0.9.21 to handle notifications for e.g. failed Workflow
  5179. r.HandleFunc("/api/v1/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS")
  5180. r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS")
  5181. r.HandleFunc("/api/v1/notifications/clear", shuffle.HandleClearNotifications).Methods("GET", "OPTIONS")
  5182. r.HandleFunc("/api/v1/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS")
  5183. r.HandleFunc("/api/v1/users/notifications", shuffle.HandleCreateNotification).Methods("POST", "OPTIONS")
  5184. r.HandleFunc("/api/v1/users/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS")
  5185. r.HandleFunc("/api/v1/users/notifications/clear", shuffle.HandleClearNotifications).Methods("GET", "OPTIONS")
  5186. r.HandleFunc("/api/v1/users/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS")
  5187. r.HandleFunc("/api/v1/conversation", shuffle.RunActionAI).Methods("POST", "OPTIONS")
  5188. //r.HandleFunc("/api/v1/users/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS")
  5189. r.HandleFunc("/api/v1/dashboards/{key}/widgets", shuffle.HandleNewWidget).Methods("POST", "OPTIONS")
  5190. r.HandleFunc("/api/v1/dashboards/{key}/widgets/{widget_id}", shuffle.HandleGetWidget).Methods("GET", "OPTIONS")
  5191. // Need to add auth in pprof
  5192. // if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" || strings.ToLower(os.Getenv("DEBUG_MEMORY")) == "true" {
  5193. // log.Printf("[DEBUG] Memory debugging is enabled on /debug/pprof")
  5194. // r.HandleFunc("/debug/pprof/", pprof.Index)
  5195. // r.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
  5196. // r.HandleFunc("/debug/pprof/profile", pprof.Profile)
  5197. // r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
  5198. // r.HandleFunc("/debug/pprof/trace", pprof.Trace)
  5199. // } else {
  5200. // log.Printf("[DEBUG] Memory debugging is disabled. To enable, set SHUFFLE_DEBUG_MEMORY or DEBUG_MEMORY to true")
  5201. // }
  5202. r.Use(shuffle.RequestMiddleware)
  5203. http.Handle("/", r)
  5204. }
  5205. // Had to move away from mux, which means Method is fucked up right now.
  5206. func main() {
  5207. if os.Getenv("DEBUG") == "true" {
  5208. debug = true
  5209. }
  5210. initHandlers()
  5211. hostname, err := os.Hostname()
  5212. if err != nil {
  5213. hostname = "MISSING"
  5214. }
  5215. innerPort := os.Getenv("BACKEND_PORT")
  5216. if innerPort == "" {
  5217. log.Printf("[DEBUG] Running on %s:5001", hostname)
  5218. log.Fatal(http.ListenAndServe(":5001", nil))
  5219. os.Setenv("BACKEND_PORT", "5001")
  5220. } else {
  5221. log.Printf("[DEBUG] Running on %s:%s", hostname, innerPort)
  5222. log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", innerPort), nil))
  5223. }
  5224. }