"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle);u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})
-}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(""),t.close(),n=un(e,t),Pt.detach()),Gt[e]=n),n}function un(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,n){x.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(x.css(e,"display"))?x.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x.support.opacity||(x.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=x.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===x.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,n){return n?x.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,n){x.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?x(e).position()[n]+"px":r):t}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!x.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||x.css(e,"display"))},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(x.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Ct.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),x.param=function(e,n){var r,i=[],o=function(e,t){t=x.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var mn,yn,vn=x.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Cn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Nn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=x.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=o.href}catch(Ln){yn=a.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(T)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(l){var u;return o[l]=!0,x.each(e[l]||[],function(e,l){var c=l(n,r,i);return"string"!=typeof c||a||o[c]?a?!(u=c):t:(n.dataTypes.unshift(c),s(c),!1)}),u}return s(n.dataTypes[0])||!o["*"]&&s("*")}function _n(e,n){var r,i,o=x.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,l=e.indexOf(" ");return l>=0&&(i=e.slice(l,e.length),e=e.slice(0,l)),x.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&x.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?x("").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(n.unit=o,n.start=+a||+r||0,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);a.finish=function(){t.stop(!0)},(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window);
diff --git a/static/js/provision.js b/static/js/provision.js
deleted file mode 100644
index c1bb2cc..0000000
--- a/static/js/provision.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-(function () {
- // Note: we explicitly ignore persona suggested certificate duration
- // and issue short-lived certs. Because re-provisioning is
- // user invisible, this allows us a means of remote session
- // termination in case of security breaches.
- navigator.id.beginProvisioning(function(email /* , cert_duration */) {
- navigator.id.genKeyPair(function(pubkey) {
- $.ajax({
- url: '/api/provision',
- data: JSON.stringify({
- pubkey: pubkey,
- user: email,
- "_csrf": $('[name=_csrf]').val()
- }),
- type: 'POST',
- headers: { "Content-Type": 'application/json' },
- dataType: 'json',
- success: function(r) {
- navigator.id.registerCertificate(r.cert);
- },
- error: function(r) {
- navigator.id.raiseProvisioningFailure("could not provision user");
- }
- });
- });
- });
-})();
diff --git a/static/js/signin.js b/static/js/signin.js
deleted file mode 100644
index 94631fe..0000000
--- a/static/js/signin.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-$(document).ready(function() {
-
- setTimeout(function() {
- $('[name=user]').attr('disabled', 'true');
- }, 300);
-
- // XXX: currently the server is plucking email out of get data when
- // the auth page is rendered. It does this to render to the user
- // their substituted email address.
- //
- // We need the client to bounce the email off the server and sub it in.
-
- navigator.id.beginAuthentication(function(email) {
- var msg;
-
- // all cancel buttons work the same
- $("form button.cancel").click(function(e) {
- e.preventDefault();
- msg = "user canceled authentication";
- navigator.id.raiseAuthenticationFailure(msg);
- });
-
- $("form").submit(function(e) {
- e.preventDefault();
-
- var pass = $.trim($("#pass").val()),
- auth_url = $('form').attr('action');
-
- // validate password client side
- if (pass.length < 6) {
- showTooltip("Yikes, Passwords have to be at least 6 characters",
- "#pass");
- return;
- }
-
- $.ajax({
- url: auth_url,
- type: 'POST',
- data: { user: email, pass: pass, "_csrf": $('[name=_csrf]').val() },
- success: function() {
- navigator.id.completeAuthentication();
- },
- error: function() {
- showTooltip("The account cannot be logged in with this username and password", "#user");
- }
- });
- });
- });
-
- // From here below is tooltip code
-
- var ANIMATION_TIME = 250,
- TOOLTIP_MIN_DISPLAY = 2000,
- TOOLTIP_OFFSET_TOP_PX = 5,
- TOOLTIP_OFFSET_LEFT_PX = 10,
- READ_WPM = 200,
- hideTimer;
-
- function showTooltip(msg, anchor) {
- if (hideTimer) {
- clearTimeout(hideTimer);
- hideTimer = null;
- }
-
- $(".tooltip").hide();
- $(".tooltip .contents").text(msg);
- $(".tooltip").fadeIn(ANIMATION_TIME);
-
- anchorTooltip(".tooltip", anchor);
-
- var displayTimeMS = calculateDisplayTime(msg);
-
- hideTimeout = setTimeout(function() {
- hideTimeout = null;
- $(".tooltip").fadeOut(ANIMATION_TIME);
- }, displayTimeMS);
- }
-
- function anchorTooltip(tooltip, anchor) {
- tooltip = $(tooltip);
- anchor = $(anchor);
- var tooltipOffset = anchor.offset();
- tooltipOffset.top -= (tooltip.outerHeight() + TOOLTIP_OFFSET_TOP_PX);
- tooltipOffset.left += TOOLTIP_OFFSET_LEFT_PX;
-
- tooltip.css(tooltipOffset);
- }
-
- function calculateDisplayTime(text) {
- // Calculate the amount of time a tooltip should display based on the
- // number of words in the content divided by the number of words an average
- // person can read per minute.
- var contents = text.replace(/\s+/, ' ').trim(),
- words = contents.split(' ').length,
- // The average person can read ± 250 wpm.
- wordTimeMS = (words / READ_WPM) * 60 * 1000,
- displayTimeMS = Math.max(wordTimeMS, TOOLTIP_MIN_DISPLAY);
-
- return displayTimeMS;
- }
-
-
-
-});
diff --git a/tests/auth.js b/tests/auth.js
deleted file mode 100644
index 71e0f28..0000000
--- a/tests/auth.js
+++ /dev/null
@@ -1,245 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// tests of authentication
-
-
-
-const
-should = require('should'),
-request = require('request').defaults({jar: require('request').jar()}),
-util = require('util'),
-fs = require('fs'),
-config = require('../server/lib/configuration');
-
-// let's pre-write an alias file for the purposes of this test
-
-describe('authentication', function() {
- var context;
-
- it('servers should start', function(done) {
- var testUtil = require('./lib/test-util');
- testUtil.startServers(function(err, ctx) {
- should.not.exist(err);
- context = ctx;
- done();
- });
- });
-
- var csrf_token;
- it('csrf can be fetched via the api', function(done) {
- request.get({
- url: util.format('%s/api/session_context', context.mozillaidp.url),
- json: true
- }, function(err, resp, body) {
- should.not.exist(err);
- (body).should.be.a('object');
- (body.csrf).should.be.a('string');
- (resp.headers['cache-control']).should.equal('no-cache, max-age=0');
- csrf_token = body.csrf;
- done();
- });
- });
-
- it('auth should fail with incorrect password', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: 'testtestwrong',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(401);
- (body.success).should.equal(false);
- (body.reason).should.equal('email or password incorrect');
- done();
- });
- });
-
- it('auth should fail with malformed email', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(400);
- (body.success).should.equal(false);
- (body.reason).should.equal('user: ValidatorError: Invalid email');
- done();
- });
- });
-
- it('auth should fail with short password', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: '12345',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(400);
- (body.success).should.equal(false);
- (body.reason).should.equal('pass: ValidatorError: String is not in range');
- done();
- });
- });
-
- it('auth should fail with extra arguments', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token,
- alias: 'user2+foo@mozilla.com',
- email: 'user2+bar@mozilla.com'
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(400);
- (body.success).should.equal(false);
- (body.reason).should.equal("unsupported parameter: 'alias', 'email'");
- done();
- });
- });
-
- it('auth should fail for DISABLED users', function(done) {
- // change the employeetype for a specific user
- var user = context.ldap.findUser('user3@mozilla.com');
- should.exist(user);
- user.attributes.employeetype = 'DISABLED';
-
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user3@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- (resp.statusCode).should.equal(401);
- (body.success).should.equal(false);
-
- // *always* clean up after yourself when editing the mock LDAP directory
- // data ... avoids bad things
- user.attributes.employeetype = 'Tester'
- done();
- });
- });
-
- it('auth should fail for users not found in ldap', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user-fake@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- (body.success).should.equal(false);
- (body.reason).should.equal('email not found');
- done();
- });
- });
-
- it('auth (@mozilla.com) should succeed when correct', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- done();
- });
- });
-
- it('aliased (@mozilla.com) user should authenticate', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'alias2@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token,
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- done();
- });
- });
-
- it('auth (@mozillafoundation.org) should succeed when correct', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozillafoundation.org',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- done();
- });
- });
-
- // this user lives under the o=org, dc=mozilla
- it('auth (test_a@mozilla.com) should succeed', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'test_a@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- done();
- });
- });
-
- // this user lives under the o=com, dc=mozilla
- it('auth (test_a@mozillafoundation.org) should succeed', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'test_a@mozillafoundation.org',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- done();
- });
- });
-
- it('aliased (@mozillafoundation.org) user should authenticate', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'alias2@mozillafoundation.org',
- pass: 'testtest',
- _csrf: csrf_token,
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- done();
- });
- });
-});
diff --git a/tests/certification.js b/tests/certification.js
deleted file mode 100644
index cfed68b..0000000
--- a/tests/certification.js
+++ /dev/null
@@ -1,266 +0,0 @@
-// vim: set shiftwidth=2
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// tests of certificate signing
-
-const
-should = require('should'),
-request = require('request').defaults({jar: require('request').jar()}),
-util = require('util'),
-testUtil = require('./lib/test-util'),
-crypto = require('../server/lib/crypto'),
-config = require('../server/lib/configuration'),
-jwcrypto = require('jwcrypto'),
-fs = require('fs');
-
-// load desired algorithms
-require("jwcrypto/lib/algs/rs");
-require("jwcrypto/lib/algs/ds");
-
-describe('certificate signing', function() {
- var context;
-
- it('servers should start', function(done) {
- testUtil.startServers(function(err, ctx) {
- should.not.exist(err);
- context = ctx;
- done();
- });
- });
-
- var keypair;
- it('client side keypair generation succeeds', function(done) {
- jwcrypto.generateKeypair(
- {algorithm: 'DS', keysize: 128},
- function(err, kp) {
- should.not.exist(err);
- keypair = kp;
- done();
- });
- });
-
- var csrf_token;
- it('token can be fetched via the api', function(done) {
- request.get({
- url: util.format('%s/api/session_context', context.mozillaidp.url),
- json: true
- }, function(err, resp, body) {
- should.not.exist(err);
- (body).should.be.a('object');
- (body.csrf).should.be.a('string');
- csrf_token = body.csrf;
- done();
- });
- });
-
- it('signing request fails without proper CSRF', function(done) {
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pubkey: keypair.publicKey.serialize()
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- // 403 returned when CSRF is missing
- (resp.statusCode).should.equal(403);
- done();
- });
- });
-
- it('signing request fails without an authenticated session', function(done) {
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pubkey: keypair.publicKey.serialize(),
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- // 401 returned when authentication is missing
- (resp.statusCode).should.equal(401);
- done();
- });
- });
-
- it('authentication should succeed', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- (resp.headers['cache-control']).should.equal('no-cache, max-age=0');
- done();
- });
- });
-
-
- it('signing request succeeds with an authenticated session', function(done) {
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pubkey: keypair.publicKey.serialize(),
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- var serverPubKey = jwcrypto.loadPublicKey(crypto.pubKey);
- jwcrypto.verify(body.cert, serverPubKey, function(err, payload) {
- should.not.exist(err);
- (payload.iss).should.equal(config.get('issuer'));
- // convert units (s -> ms) and add 10 second padding (certificates are issued
- // 10s in the past to mitigate minor clock skew)
- (payload.exp - payload.iat).should.equal((config.get('certificate_validity_s') + 10) * 1000);
- (payload.iss).should.equal('mozilla.personatest.org');
- (payload.principal.email).should.equal('user2@mozilla.com');
- done();
- });
- });
- });
-
- it('signing request succeeds for an alias', function(done) {
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'alias2@mozilla.com',
- pubkey: keypair.publicKey.serialize(),
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
-
- var serverPubKey = jwcrypto.loadPublicKey(crypto.pubKey);
- jwcrypto.verify(body.cert, serverPubKey, function(err, payload) {
- should.not.exist(err);
- (payload.iss).should.equal(config.get('issuer'));
- // convert units (s -> ms) and add 10 second padding (certificates are issued
- // 10s in the past to mitigate minor clock skew)
- (payload.exp - payload.iat).should.equal((config.get('certificate_validity_s') + 10) * 1000);
- (payload.iss).should.equal('mozilla.personatest.org');
- (payload.principal.email).should.equal('alias2@mozilla.com');
- done();
- });
- });
- });
-
- it('signing request fails if user is missing', function(done) {
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- pubkey: keypair.publicKey.serialize(),
- _csrf: csrf_token
- },
- json: true
- }, function(err, resp, body) {
- should.not.exist(err);
- (body.success).should.equal(false);
- (body.reason).should.equal("missing required parameter: 'user'");
- (resp.statusCode).should.equal(400);
- done();
- });
- });
-
- it('signing request fails if pubkey is missing', function(done) {
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- _csrf: csrf_token
- },
- json: true
- }, function(err, resp, body) {
- should.not.exist(err);
- (body.success).should.equal(false);
- (body.reason).should.equal("missing required parameter: 'pubkey'");
- (resp.statusCode).should.equal(400);
- done();
- });
- });
-
- it('signing fails when extra params are present', function(done) {
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pubkey: keypair.publicKey.serialize(),
- _csrf: csrf_token,
- bogus: 'param'
- },
- json: true
- }, function(err, resp, body) {
- should.not.exist(err);
- (body.success).should.equal(false);
- (body.reason).should.equal("unsupported parameter: 'bogus'");
- (resp.statusCode).should.equal(400);
- done();
- });
- });
-
- it('signed fails when employeeType===DISABLED', function(done) {
- var user = context.ldap.findUser("user2@mozilla.com");
- user.attributes.employeetype = "DISABLED";
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pubkey: keypair.publicKey.serialize(),
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- user.attributes.employeetype = "Tester";
- should.not.exist(err);
- (resp.statusCode).should.equal(401);
- done();
- });
- });
-
-
- it('signing request fails when user has changed their password', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
-
- var user = context.ldap.findUser('user2@mozilla.com');
- var oldChangeTime = user.attributes.pwdChangedTime;
- user.attributes.pwdChangedTime = "CHANGED"
-
- request.post({
- url: util.format('%s/api/provision', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pubkey: keypair.publicKey.serialize(),
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(401);
- (resp.body).should.equal('Password Changed. Reauthentication required.');
-
- // make sure the session was reset
- should.exist(resp.headers['set-cookie']);
-
- // * reset the value ...
- user.attributes.pwdChangedTime = oldChangeTime;
- done();
- });
- });
- });
-});
diff --git a/tests/csrf.js b/tests/csrf.js
deleted file mode 100644
index 827c813..0000000
--- a/tests/csrf.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// tests of CSRF protection
-
-const
-should = require('should'),
-request = require('request').defaults({jar: require('request').jar()}),
-util = require('util'),
-testUtil = require('./lib/test-util');
-
-
-describe('CSRF checking', function() {
- var context;
-
- it('servers should start', function(done) {
- testUtil.startServers(function(err, ctx) {
- should.not.exist(err);
- context = ctx;
- done();
- });
- });
-
- it('auth should fail without proper token', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: 'testtest',
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (body).should.equal('Forbidden');
- (resp.statusCode).should.equal(403);
- done();
- });
- });
-
- var csrf_token;
- it('token can be fetched via the api', function(done) {
- request.get({
- url: util.format('%s/api/session_context', context.mozillaidp.url),
- json: true
- }, function(err, resp, body) {
- should.not.exist(err);
- (body).should.be.a('object');
- (body.csrf).should.be.a('string');
- (resp.headers['cache-control']).should.equal('no-cache, max-age=0');
- csrf_token = body.csrf;
- done();
- });
- });
-
- it('auth should succeed with proper token', function(done) {
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: 'testtest',
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- (resp.statusCode).should.equal(200);
- done();
- });
- });
-});
diff --git a/tests/healthChecks.js b/tests/healthChecks.js
deleted file mode 100644
index d6bacd0..0000000
--- a/tests/healthChecks.js
+++ /dev/null
@@ -1,92 +0,0 @@
-const
- clorthoServer = require('../server/bin/clortho'),
- should = require('should'),
- request = require('request').defaults({jar: require('request').jar()}),
- util = require('util'),
- config = require('../server/lib/configuration');
-
-describe('Health Checking', function() {
- var serverURL = "";
-
- before(function() {
- });
-
- it('the server should startup', function(done) {
- clorthoServer.startup(function(err, address){
- should.not.exist(err);
- serverURL = "http://" + address.address + ":" + address.port;
- done();
- });
- });
-
- it('should return 200 on node_status', function(done) {
- request(
- util.format('%s%s', serverURL, "/node_status"),
- function(err, resp, body) {
- resp.statusCode.should.equal(200);
- body.should.equal("OK");
- done();
- }
- );
- });
-
- it('should return 200 on ldap_status OK', function(done) {
- request(
- util.format('%s%s', serverURL, "/ldap_status"),
- function(err, resp, body) {
- resp.statusCode.should.equal(200);
- resp.body.should.equal("OK");
- done();
- }
- );
- });
-
- it('should return 503 on ldap_status error', function(done) {
- // fml, side effects, we need this so when attempting to
- // bind it will look for an ldap server that doesn't exist
- var ldapurl = config.get("ldap_server_url");
- config.set('ldap_server_url', 'ldap://127.0.0.1:1');
- request(
- util.format('%s%s', serverURL, "/ldap_status"),
- function(err, resp, body) {
- resp.statusCode.should.equal(503);
- config.set('ldap_server_url', ldapurl);
- done();
- }
- );
- });
-
- it('should return 503 on ldap_status invalid password', function(done) {
- // fml, side effects, we need this so when attempting to
- // bind it will look for an ldap server that doesn't exist
- var tmp = config.get("ldap_bind_password");
- config.set('ldap_bind_password', 'fakkkkkkkkkkeeeee!');
- request(
- util.format('%s%s', serverURL, "/ldap_status"),
- function(err, resp, body) {
- resp.statusCode.should.equal(503);
- resp.body.should.equal("Error: InvalidCredentialsError");
- config.set('ldap_bind_password', tmp);
- done();
- }
- );
- });
-
- it('should return 503 on ldap_status invalid DN', function(done) {
- // fml, side effects, we need this so when attempting to
- // bind it will look for an ldap server that doesn't exist
- var tmp = config.get("ldap_bind_dn");
- config.set('ldap_bind_dn', 'o=notin,com=thetree');
- request(
- util.format('%s%s', serverURL, "/ldap_status"),
- function(err, resp, body) {
- resp.statusCode.should.equal(503);
- resp.body.should.equal("Error: NoSuchObjectError");
- config.set('ldap_bind_dn', tmp);
- done();
- }
- );
- });
-
-});
-
diff --git a/tests/jshint.js b/tests/jshint.js
deleted file mode 100644
index c5ea35b..0000000
--- a/tests/jshint.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// jshinting (syntax checking) of the source
-
-const
-should = require('should'),
-fs = require('fs'),
-path = require('path'),
-jshint = require('jshint').JSHINT,
-walk = require('walk');
-
-function jshintFormatter(errors) {
- return errors.map(function(e) {
- return e.error.reason + ' ' + e.file + ':' + e.error.line;
- });
-}
-
-describe('source code syntax', function() {
- // read jshintrc
- var jshintrc;
-
- it('.jshintrc should be readable', function(done) {
- jshintrc = JSON.parse(fs.readFileSync(path.join(__dirname, '../.jshintrc')).toString());
- (jshintrc).should.be.a('object');
- done();
- });
-
- var filesToLint = [
- path.join(__dirname, '../server/bin/clortho'),
- path.join(__dirname, '../server/routes.js')
- ];
-
- it('we should be able to discover files to lint', function(done) {
- var walker = walk.walkSync(path.join(__dirname, '../server/lib'), {});
-
- walker.on("file", function(root, fStat, next) {
- var f = path.join(root, fStat.name);
- if (/\.js$/.test(f)) {
- filesToLint.push(f);
- }
- next();
- });
- walker.on("end", done);
- });
-
- it('syntax checking should yield no errors', function(done) {
- var errors = [];
-
- function checkNext() {
- if (!filesToLint.length) {
- if (errors.length) {
- var buf = util.format("\n %d errors:\n * ",
- errors.length);
- buf += errors.join("\n * ");
- done(buf);
- } else {
- done(null);
- }
- return;
- }
- var f = filesToLint.shift();
- fs.readFile(f.toString(), function(err, data) {
- // now
- f = path.relative(process.cwd(), f);
- if (!jshint(data.toString(), jshintrc)) {
- jshint.errors.forEach(function(e) {
- errors.push(util.format("%s %s:%d - %s", e.id, f, e.line, e.reason));
- });
- }
- checkNext();
- });
- }
- checkNext();
- });
-});
diff --git a/tests/ldap.js b/tests/ldap.js
deleted file mode 100644
index a61ccd7..0000000
--- a/tests/ldap.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// Tests of our ldap abstraction against a local node.js LDAP server
-
-const
-should = require('should'),
-ldapMock = require('../server/lib/ldapMock')(),
-ldap = require('ldapjs'),
-auth = require('../server/lib/auth.js'),
-testUtil = require('./lib/test-util.js');
-
-var ldapServerInstance = null;
-
-describe('the LDAP authentication library', function() {
- before(function(done) {
- ldapServerInstance = ldapMock.server;
- ldapServerInstance.listen(65077, '127.0.0.1', done);
- });
-
- it('should throw when required parameters are missing', function(done) {
- (function() {
- auth.authUser({
- url: 'ldap://127.0.0.1:777',
- pass: 'secret'
- }, null);
- }).should.throw("missing required parameters: dn");
- done();
- });
-
- it('should fail with an unbound port', function(done) {
- auth.authUser({
- url: 'ldap://127.0.0.1:777',
- dn: 'o=example',
- pass: 'secret'
- }, function(err) {
- should.exist(err);
- (err.message).should.equal("connect ECONNREFUSED");
- done();
- });
- });
-
- it('should fail with a bogus ip', function(done) {
- auth.authUser({
- url: 'ldap://192.192.192.192:777',
- dn: 'o=example',
- pass: 'secret',
- connectTimeout: 30 // ms
- }, function(err) {
- should.exist(err);
- (err).should.equal("connect failed");
- done();
- });
- });
-
- it('should fail with incorrect username', function(done) {
- auth.authUser({
- url: ldapServerInstance.url,
- dn: 'o=badexample',
- pass: 'secret'
- }, function(err) {
- should.exist(err);
- (err.message).should.equal("No tree found for: o=badexample");
- done();
- });
- });
-
- it('should succeed when params are correct', function(done) {
- auth.authUser({
- url: ldapServerInstance.url,
- dn: 'mail=user1@mozilla.com, o=com, dc=mozilla',
- pass: 'testtest'
- }, function(err) {
- should.not.exist(err);
- done();
- });
- });
-});
diff --git a/tests/lib/test-util.js b/tests/lib/test-util.js
deleted file mode 100644
index 8c8be29..0000000
--- a/tests/lib/test-util.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// vim: set shiftwidth=2
-/* a home for utilities common to all tests. */
-
-// disable logging for tests when VERBOSE isn't defined in the env
-if (!process.env['VERBOSE']) {
- (require('../../server/lib/logging.js')).disable();
-}
-
-const
-clorthoServer = require('../../server/bin/clortho'),
-config = require('../../server/lib/configuration'),
-ldapMock = require('../../server/lib/ldapMock')();
-
-// nothing to export right now!
-module.exports = {
- // start local LDAP server and mozilla IdP bound to ephemeral ports
- startServers: function(cb) {
- var ctx = {
- mozillaidp: {
- url: ''
- },
- ldap: {
- url: '',
- instance: null,
- directory: ldapMock.directory,
- findUser: function(email) {
- for(var i=0; i < ldapMock.directory.length; i++) {
- if (ldapMock.directory[i].attributes.mail == email) {
- return ldapMock.directory[i];
- }
- }
-
- return null;
- }
- }
- };
- // set configuration for ldap server
- config.set('ldap_bind_dn', 'mail=user1@mozilla.com, o=com, dc=mozilla');
- config.set('ldap_bind_password', 'testtest');
- ctx.ldap.instance = ldapMock.server;
- ctx.ldap.instance.listen(0, '127.0.0.1', function(err, x) {
- if (err) return cb(err);
- ctx.ldap.url = ctx.ldap.instance.url;
- config.set('ldap_server_url', ctx.ldap.url);
- clorthoServer.startup(function(err, address) {
- if (err) return cb(err);
- ctx.mozillaidp.url = util.format('http://%s:%s',
- address.address,
- address.port);
- cb(null, ctx);
- });
- });
- }
-};
diff --git a/tests/p3p.js b/tests/p3p.js
deleted file mode 100644
index b42cda0..0000000
--- a/tests/p3p.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const
-should = require('should'),
-request = require('request').defaults({jar: require('request').jar()}),
-util = require('util'),
-fs = require('fs'),
-config = require('../server/lib/configuration');
-
-// let's pre-write an alias file for the purposes of this test
-
-describe('P3P Header', function() {
- var context;
-
- it('servers should start', function(done) {
- var testUtil = require('./lib/test-util');
- testUtil.startServers(function(err, ctx) {
- should.not.exist(err);
- context = ctx;
- done();
- });
- });
-
- it('P3P headers should be set when IE User-Agent is sent', function(done) {
- request.get({
- url: util.format('%s/api/session_context', context.mozillaidp.url),
- json: true,
- headers: {
- 'User-Agent': 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 3.3.69573; WOW64; en-US)'
- }
- }, function(err, resp, body) {
- should.not.exist(err);
- should.exist(resp.headers['p3p'])
- done();
- });
- });
-
- it('P3P headers should NOT set by default', function(done) {
- request.get({
- url: util.format('%s/api/session_context', context.mozillaidp.url),
- json: true,
- }, function(err, resp, body) {
- should.not.exist(err);
- should.not.exist(resp.headers['p3p'])
- done();
- });
- });
-});
diff --git a/tests/public_key.js b/tests/public_key.js
deleted file mode 100644
index f88e8a6..0000000
--- a/tests/public_key.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// tests of crypto routines of the server
-
-const
-should = require('should'),
-testUtil = require('./lib/test-util'),
-request = require('request').defaults({jar: require('request').jar()}),
-jwcrypto = require('jwcrypto');
-
-// load desired algorithms
-require("jwcrypto/lib/algs/rs");
-require("jwcrypto/lib/algs/ds");
-
-var serverURL;
-
-describe('the public key', function() {
- it('servers should start', function(done) {
- testUtil.startServers(function(err, ctx) {
- should.not.exist(err);
- serverURL = ctx.mozillaidp.url;
- done();
- });
- });
-
- it('.well-known/browserid should be properly formatted', function(done) {
- request(
- util.format('%s/.well-known/browserid', serverURL),
- function(err, resp, body) {
- should.not.exist(err);
- should.exist(body);
- var parsed = JSON.parse(body);
- should.exist(parsed['public-key']);
- // now let's parse the public key
- var key;
- (function() {
- key = jwcrypto.loadPublicKey(JSON.stringify(parsed['public-key']));
- }).should.not.throw();
- should.exist(key);
- done();
- });
- });
-});
diff --git a/tests/static.js b/tests/static.js
deleted file mode 100644
index ff84f10..0000000
--- a/tests/static.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// tests of static file serving
-
-const
-should = require('should'),
-testUtil = require('./lib/test-util'),
-request = require('request').defaults({jar: require('request').jar()}),
-util = require('util'),
-config = require('../server/lib/configuration');
-
-// explicitly disable development mode to test STS headers
-config.set('local_development', false);
-
-var securityPolicyValue = util.format("default-src 'self' %s",
- config.get('browserid_server'));
-var serverURL;
-
-describe('static file serving', function() {
- it('the server should startup', function(done) {
- testUtil.startServers(function(err, context) {
- should.not.exist(err);
- serverURL = context.mozillaidp.url;
- done();
- });
- });
-
- var signInPath, provisioningPath;
-
- it('should respond to requests on .well-known/browserid', function(done) {
- request(
- util.format('%s/.well-known/browserid', serverURL),
- function(err, resp, body) {
- should.not.exist(err);
- should.exist(body);
- var parsed = JSON.parse(body);
- // content type is important, part of the protocol
- (resp.headers['content-type']).should.equal('application/json');
-
- // cache headers are important, controls how long verifiers can cache the public key
- (resp.headers['cache-control']).should.equal('max-age=5, public');
-
- // we use STS everywhere
- (resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
-
- should.exist(parsed.authentication);
- signInPath = parsed.authentication
- should.exist(parsed.provisioning);
- provisioningPath = parsed.provisioning;
- should.exist(parsed['public-key']);
- done();
- });
- });
-
- it('should serve provisioning page', function(done) {
- request(
- util.format('%s%s', serverURL, provisioningPath),
- function(err, resp, body) {
- should.not.exist(err);
- should.exist(body);
- (resp.headers['content-type']).should.equal('text/html; charset=utf-8');
- (resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
- (resp.headers['x-content-security-policy']).should.equal(
- securityPolicyValue);
- done();
- });
- });
-
- it('should serve authenticate page', function(done) {
- request(
- util.format('%s%s', serverURL, signInPath),
- function(err, resp, body) {
- should.not.exist(err);
- should.exist(body);
- (resp.headers['content-type']).should.equal('text/html; charset=utf-8');
- (resp.headers['x-frame-options']).should.equal('DENY');
- (resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
- (resp.headers['x-content-security-policy']).should.equal(
- securityPolicyValue);
-
- done();
- });
- });
-
- it('should serve 404 page', function(done) {
- request(
- util.format('%s%s', serverURL, "/not_found"),
- function(err, resp, body) {
- should.not.exist(err);
- should.exist(body);
- (resp.statusCode).should.equal(404);
- (resp.headers['content-type']).should.equal('text/html; charset=utf-8');
- (resp.headers['x-frame-options']).should.equal('DENY');
- (resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
- (resp.headers['x-content-security-policy']).should.equal(
- securityPolicyValue);
-
- done();
- });
- });
-});
diff --git a/tests/throttle.js b/tests/throttle.js
deleted file mode 100644
index cfd374b..0000000
--- a/tests/throttle.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// tests of authentication throttling
-
-const
-should = require('should'),
-request = require('request').defaults({jar: require('request').jar()}),
-util = require('util'),
-testUtil = require('./lib/test-util'),
-config = require('../server/lib/configuration');
-
-describe('authentication throttling', function() {
- var context;
- function authNTimes(n, password, cb, _lastErr) {
- if (n === 0) return cb(_lastErr);
- request.post({
- url: util.format('%s/api/sign_in', context.mozillaidp.url),
- json: {
- user: 'user2@mozilla.com',
- pass: password,
- _csrf: csrf_token
- }
- }, function(err, resp, body) {
- var curErr = resp.statusCode === 200 ? null : 'failed';
- authNTimes(--n, password, cb, err || curErr);
- });
- }
-
- it('servers startup as expected', function(done) {
- testUtil.startServers(function(err, ctx) {
- should.not.exist(err);
- context = ctx;
- done();
- });
- });
-
- var csrf_token;
- it('a CSRF token can be fetched', function(done) {
- request.get({
- url: util.format('%s/api/session_context', context.mozillaidp.url),
- json: true
- }, function(err, resp, body) {
- should.not.exist(err);
- (body).should.be.a('object');
- (body.csrf).should.be.a('string');
- csrf_token = body.csrf;
- done();
- });
- });
-
- it('auth will succeed 5 consecutive times', function(done) {
- authNTimes(5, "testtest", done);
- });
-
- it('auth may fail 4 consecutive times', function(done) {
- authNTimes(4, "badpassword", function(err) {
- (err).should.equal('failed');
- done();
- });
- });
-
- it('auth should succeed on the fifth', function(done) {
- authNTimes(1, "testtest", done);
- });
-
- it('auth may fail 5 consecutive times', function(done) {
- authNTimes(5, "badpassword", function(err) {
- (err).should.equal('failed');
- done();
- });
- });
-
- it('which results in a locked out user', function(done) {
- authNTimes(1, "testtest", function(err) {
- done(err === 'failed' ? null : "failed to throttle");
- });
- });
-
- it('user is locked out with a bad password', function(done) {
- authNTimes(1, "bad password", function(err) {
- done(err === 'failed' ? null : "failed to throttle");
- });
- });
-
- it('user is able to login after auth_lockout_ms expires', function(done) {
- config.set('auth_lockout_ms', 10);
- setTimeout(function() {
- authNTimes(1, "testtest", done);
- }, 20);
- });
-});