text:
Promotion of Data Sharing
Internet research relies on a wide variety of data on the structure, dynamics, and usage patterns of operational Internet infrastructure, for parameterization and validation of scientific modeling and analysis efforts. As our use of and dependence on the Internet expands, an expanding range of data of interest and utility to an increasing number of disciplines must bring with it deeper consideration of privacy in collaboration and data sharing models, especially between industry and academia.
We have proposed to move the Internet research stakeholder community beyond the relatively siloed data sharing practices and into a more reputable and pervasive scientific discipline, by self-regulating through a transparent and repeatable sharing framework. Our model -- the Privacy-Sensitive Sharing (PS2) framework -- integrates privacy-enhancing technologies with a policy framework that applies proven and standard privacy principles and obligations of data seekers and data providers, in coordination with techniques that implement and provably enforce those obligations. The PS2 framework considers practical challenges confronting security professionals, network analysts, systems administrators, researchers, and legal advisors. It embodies the proposition that privacy problems are exacerbated by a shortage of transparency surrounding the who, what, when, where, how and why of sharing privacy-sensitive information We evaluate our framework along two primary criteria: (1) how well the policies and techniques address privacy risks; and, (2) how well policies and techniques achieve utility objectives. Below we excerpt from this paper, including a review of the practical risks and benefits of data sharing, as well as the motivation, components, and evaluation of our model.
Information on CAIDA's datasets can be found in the overview of CAIDA datasets page.
The current default, defensive posture to not share network data derives from the purgatory formed by the gaps in regulation and law, commercial pressures, and evolving considerations of both threat models and ethical behavior. The threat model from not data sharing is necessarily vague, as damages resulting from knowledge management deficiencies are beset with causation and correlation challenges. More fundamentally, we lack a risk profile for our communications fabric, partly as a result of the data-sharing dearth. Notably, society has not felt the pain points that normally motivate legislative, judicial or policy change - explicit and immediate body counts or billion dollar losses. Admittedly, the policies that have given rise to the Internet's tremendous growth and support for network innovations have also rendered the entire sector opaque, unamenable to objective empirical macroscopic analysis, in ways and for reasons disconcertingly resonant with the U.S. financial sector before its 2008 meltdown. The opaqueness, juxtaposed with this decade's proliferation of Internet security, scalability, sustainability, and stewardship issues, is a cause for concern for the integrity of the infrastructure as well the information economy it supports. [10].
Internet research stakeholders have an opportunity to tip the risk scales in favor of more protected data sharing, by proactively implementing appropriate management of privacy risks. Transparent and morally defensible self-regulation in the interests of building social capital and informing legal and judicial regimes will allow stakeholders to more practically influence policy and law at these crossroads. Information security controls were initially considered a liability (from a cost perspective) until regulations rendered lack of security a compliance liability. We anticipate circumstances to reveal that rather than data-sharing being a risk, not sharing data is a liability. We offer the PS2 as a tool to help move the community mindset in that direction as productively and safely as possible.
The strategic challenge is similar to other domains: how to balance utility goals with privacy risks for data seeker (DS) and data providers (DP). Internet researchers and systems security personnel are generally DS - entities seeking to share, responsibly disclose, acquire or otherwise exchange lawfully possessed network data. While data providers (DP) acknowledge the potential benefits of sharing, they are sufficiently uncertain about the privacy-utility risk that they yield to a normative presumption that the risks outweigh potential rewards. Data sharing relationships that occur are market-driven or organically developed. Unsurprisingly then, there are no widespread and standard procedures for network measurement data exchange. Inconsistent, ad hoc and/or opaque exchange protocols exist, but measuring their effectiveness and benefit is challenging. A formidable consequence is the difficulty of justifying resources for research and other collaboration costs that incentivize a sharing regime. On the other hand, the high cost of independently acquiring datasets is a motivation for re-use where possible.
Privacy is difficult to quantify, as is the utility of measurement-based research. Both variables are dynamic and lack normative understanding among both domain professionals and the general citizenry. As fields of study, privacy and network science are both hindered by the absence of: common vocabulary, open and configurable reference models, uniform means of analysis, common sets of use cases, and unsurprisingly, any standard cost (liability) accounting or ROI formulas. A circular conundrum is that the risk-averse data provider needs utility demonstrated before data is released, and the researcher needs data to prove utility.
The rational predilection against sharing is strengthened by an uncertain legal regime and the social costs of sensationalism-over-accuracy-driven media accounts in cases of anonymized data being reverse engineered. While there are no procedures or regulatory framework to foster widespread exchange, there is also no framework that prohibits it either. Although there is interest in efficient and widespread sharing of measurement data, it hangs against a backdrop of legal ambiguity and flawed solution models. This backdrop and our experiences with data-sharing inform our privacy-sensitive sharing framework (PS2).
2.1 An Uncertain Legal Regime
At least in the U.S. and European Union (EU) regulatory regimes, the concept of personally identifiable information (PII) is central to privacy law and data stewardship in general. Unlike the EU model which allots overarching protection for PII, the U.S. protects PII across a patchwork of caselaw, state and federal industry-specific laws covering health data, financial data, education data, employment data, insurance records, government-issued records, credit information, and cable and telephone records. Although the definition of PII bears common threads across sectors, it is nonetheless fractured along a continuum of first and second-order identifiers (defined in Section 3.1). Crafting frameworks that generalize PII across domains, and or support cross-domain information necessitates attaching to the most expansive definition.
At its core, there is ambiguity over fundamental concepts upon which privacy risk assessment turns. First, privacy presumes identity so unless identity is defined in relation to network data artifacts, the notions of privacy and PII are already disjointed in the Internet data realm. Both the legal and Internet research communities acknowledge that the concept of PII in Internet data is not clear - its definition is context-dependent, both in terms of technology and topology. Further, the ability to link network data to individuals - as well as the cost of doing so - changes over time as technologies and protocols evolve. Yet, PII is fundamental to interpreting and applying many laws. Most notably is the United States' primary law covering the privacy of network traffic: the Electronic Communications and Privacy Act (ECPA), which provides statutory privacy protection for the interception and disclosure of certain electronic communications.
For example, blanket characterizations of IPA or URLs as PII (or not) are necessarily inaccurate because they alone cannot capture the range of privacy risks - either category could include an instance with PII, but most observed instances do not. In practice there is little functional differentiation between these traffic components and other, privacy-protected PII, yet the related legal treatment of IPAs and URLs is far less consistent. A more accurate risk assessment depends on knowing who collected it and how they use, disclose, and dispose of the traffic data.
The risk management challenge lies in the linguistic incongruity between the legal and technical discourse about traffic data - its definitions, semantic classifications and interpretations. Officers of the court associate IPAs with a greater privacy risk than URLs based on our past and still partial ability to link IPAs to an individual. This distinction was always artificial (albeit not totally unfounded) since both types of data reference a device or virtual location rather than an individual, and many URLs directly reveal much more user information than an IP address.
More specifically, this legal-technical gap exposes privacy risks with network operational data insofar as many laws do not explicitly allow for research use of network data [4], and there is no bright line caselaw applying their respective exceptions to the context of sharing Internet data for research.
2.2 Flawed Technology Models
Most data-sharing efforts by the networking research community focus on improving computing technologies to solve the privacy problem, with anonymization commanding the bulk of the attention. A typical researcher approach is to enumerate the possibly privacy-sensitive information present in network traffic traces, and then implement a technical, typically cryptographic, solution to replace this information completely or partially with synthetic identifiers, normally implemented by encrypting or otherwise removing all or part of identifiers.
Since privacy risk is influenced by evolving contexts associated with relationships between people, data, technology and institutions, solely technical solutions are inherently insufficient to balance the privacy/utility tradeoff. Technical researchers may rightly ask why we would predicate sharing architectures on ambiguous, unquantifiable and fallible human trust enforced by law and policy, if we can build trust through technology. The response is simple: while a purely technical approach may significantly ameliorate privacy risk, it largely fails to render empirically grounded answers to most questions being asked about the Internet today.
For example, while anonymization schemes can enhance the privacy of IPA in shared network traces, if it removes the ability to do geographic or topological analysis, the research utility of that data for studying DDoS modus operandi is dramatically reduced. A policy control framework enables the technical dials to allow for more privacy risk if a specific use justifies it. For example, traces protected by prefix-preserving anonymization may be subject to re-identification risk or content observation risk, but policy controls can help data providers minimize the chances that sensitive information is misused or wrongfully disclosed.
2.3 Reactive Top-Down Policy
Strategies to incentivize sharing by amending or enacting legislation merit consideration, and if the past is any indication, communications legislation will eventually be updated to reflect the evolved needs from the last few decades. However, regulation, especially in the technology arena, is largely reactive to unanticipated side-effects and dangers rather than making proactive, fundamental adjustments to predictable difficulties. Further, the length of the legislative policy cycle, confluence of variables involved in changing law, and unpredictable change agents are not amenable to immediate solutions that interested stakeholder DS and DPs can execute. In the Internet measurement space, a legislative solution means awaiting the familiar change agent aforementioned: body counts or billion dollar losses that result from the lack of ground truth about the structure and function of networks that comprise our critical communications infrastructure.
3 Sharing risks and benefits
3.1 Who, What, When
Who is at risk when network data is shared?
Entities potentially at risk when network traffic is shared include: persons who are identified or identifiable in network traffic, researchers, and network providers (NP) such as ISPs, backbone providers, and private network owners. In addition to legal liabilities and ethical responsibilities, researchers and their institutions also risk withdrawal of data and or funding as a result of privacy leakage. Society also bears costs associated with misinformation, mistrust, and internalizing behavioral norms that may result from privacy harms.
Which traffic data components are privacy-relevant?
We call a first-order identifier one which functionally distinguishes an individual: first and last name, social security number, government-issued and other account identifiers, physical and email addresses, certain biometric markers, and possibly the same information about immediate family. A second-order identifier could be an IP address (IPA), machine access code (MAC) address, host name, birthdate, phone number, zip code, gender, and financial, health, or geographic information. These indirect identifiers can also include aggregated or behavioral profile information such as IP header information, which in many cases can reveal which applications are used, how often, and with which machines. Indirect identifiers also include URL click streams, which can reveal information about the content of communications, including search terms.
Under what conditions do these data types pose risk?
Network traffic measurement data can present a privacy risk when information in packets and flow records can directly expose non-public information about persons - such as health, sexual orientation, political affiliation, religious affiliation, criminal activity, associations, behavioral activities, physical or virtual location; or, organizations - such as intellectual property, trade secrets or other proprietary information. Network traffic may also indirectly expose non-public, sensitive information if correlated (linked) with other public or private data, such as lists of IPAs of worm-infected and thus vulnerable hosts. Network data can also yield mistaken attributions and inferences about behavior, potentially more damaging than correct inferences.
The privacy risk across time may also vary, as the threat may be immediately manifest upon disclosure of data, or it may be a latent risk which is held in abeyance until some future condition arises. Lack of transparency between the DP and DS regarding the shared data's nature, scope, and lineage is invariably a condition that enhances risk.
3.2 Privacy Risks of Internet Research - Laws and Courts of Public Opinion
It is impractical to enumerate all laws that may affect privacy risk, but such an enumeration is not prerequisite to capturing the foreseeable risks of network data sharing. It is sufficient to note that legal liability or ethical obligations underlie each privacy risk. Dismissing ethical obligations as discretionary and unenforceable overlooks how ethical violations are treated by public opinion, and also ignores the fact that many laws are an evolution of ethical norms. In the U.S., privacy-related legal liabilities can derive from the federal Constitution (most notably the Fourth Amendment), federal law and regulation, contract law, tort law (e.g., invasion of privacy), state law equivalents, and organizations' privacy policies. Beyond the legal risk, violations of ethical obligations can create normative harms that implicate reputation and cause financial damages.
We break down the privacy risks of data sharing into two categories: disclosure and misuse.
Public disclosure is the act of making information or data readily available to the general public via publication or posting on the web. The privacy risks of sharing data containing PII which is subsequently displayed on the web are obvious and incontrovertible. More common and challenging are publicly available network traces and activity logs which reveal identifying information about infected hosts. Such disclosure raises the risk that unpatched or vulnerable hosts will be further exploited, thus creating security and reputation risks for individuals and organizations.
Accidental or malicious disclosure is the act of making information or data available to a third party(s) as a result of inadequate data protection. AOL provided a quintessential example in 2006 when they released an anonymized data set of search queries that, with sufficient public meta-data were linked back to users conducting the searches who were then exposed in the NYT. [2]
Compelled disclosure to third parties risk arises with the obligations attendant to possessing data, such as having to respond to subpoenas requesting data disclosure in lawsuits. The RIAA campaign to massively subpoena ISPs and universities in an attempt to identify copyright infringers is a notorious example. To illustrate, many entities (including research organizations) have chosen not to retain traffic statistics of operational and research interest, to avoid any such compulsion.
Government disclosure involves the release of data to government entities. An infamous example is the disclosure of call data records by major telecommunications carriers to the National Security Agency around 2007 [6]. Release to the government introduces another level of risk involving civil rights and liberties, such as imprisonment and restrictions on speech and associations.
Misuse of user or network profiles arises with network traffic that contains information about proprietary or security-sensitive network architectures or business operations. Advancing traffic and topology analysis, data mining and classification techniques can derive sensitive information from seemingly benign traffic data, and thereby reveal user behaviors, associations, preferences or interests, which attackers, advertisers, or content owners can then exploit. Network operators themselves may use such information for network management, illustrated by Comcast's recent throttling of BitTorrent traffic. The relatively invasive traffic engineering technique, combined with a lack of transparency in deploying it, led to public uproar and an unprecedented FCC regulatory ruling.
Inference misuse risk involves synthesizing first-order or second-order identifiers to draw inferences about a person's behavior or identity.
Re-identification and De-anonymizing misuse involves reversing data anonymization or masks to link an obfuscated identifier with its associated person. Shared anonymized data poses a misuse risk because it is variably vulnerable to re-identification attacks using public or private information whose (increasing) availability is beyond the knowledge or control of the original or intermediate data provider [14]. Anonymized data may not immediately expose PII, but any time a piece of de-identified data has been linked to first order identifying information, other anonymous aspects of the obfuscated data are easier to de-anonymize. Aggregation or statistical techniques for anonymization are not immune to re-identification risk. Examples of reidentification risk are the 2007 Netflix prize incident [12], and a similarly embarrassing episode of re-identification within the Internet research community [1].
De-anonymization risk bears special consideration in the growing incongruity around PII. DPs face increasing legal and societal pressures to protect the expanding amounts of PII they amass for legitimate business purposes. Yet, DPs are under equal pressure from the marketplace to uncover and mine PII in order to better connect supply and demand, and increase profit margins on their goods and services. DPs will turn to anonymization to avoid triggering privacy laws that exempt aggregate or anonymized data.
Like the arms race between exploits and defenses in the systems security arena, de-anonymization techniques will likely become commoditized to support investigative reporting, law enforcement, business intelligence, research, legal dispute resolution, and the presumed criminal threatscape. Several state legislatures have enacted laws to ban the release of sensitive private information because of this re-identification risk [7], although these are not contention-free. Re-identifications concerns motivated the National Human Genome Research Institute's recent removal of open access to the pooled genomics data it posted on the Internet in 2006 [9].
3.3 Utility of Internet Measurement
The benefits of network traffic measurement derive from the value of empirical network science [5], which includes a better understanding of the structure and functions of networks that comprise critical Internet infrastructure. Network researchers and funding agencies struggle to establish a science agenda, partly due to their lack of visibility into the infrastructure, but also because the field is younger and less well-defined than traditional scientific disciplines.
The following criteria help measure and communicate empirical network research utility:
The objective for sharing the data produces or promotes social welfare or generalizable knowledge.
The network research data is not already being shared, or if it is, there remains a qualitative need for sharing between other DS and DPs
The research could not be conducted without the shared data.
The scientific methodology using the shared data is transparent, objective, and repeatable relative to any privacy controls that are implemented.
Research results can be acted upon meaningfully.
Research results can be integrated with business processes or security operations, such as situational awareness of critical infrastructure.
Research that could satisfy the above criteria include:
information and network security questions regarding system threats, including characterizing baseline and anomalous workloads, modeling malware, developing effective strategies to deal with threats.
macroscopic analysis of Internet topology; understanding the how the evolution of the network is affecting the efficiency and capabilities of the underlying routing, transport, and naming protocols.
understanding the effect of the prevalence and growth of new applications on Internet workload, topology, and infrastructure economics.
validation of traffic, congestion control, and performance assumptions, models, and analyses, both for current and proposed new technologies.
development and evaluation of new technology, including measurement and sampling techniques.
4 PS2 Framework: Elements, Execution, and Evaluation
We describe the Privacy-Sensitive Sharing Framework and then evaluate the model's ability to address the privacy risks outlined in 3.2 and the utility criteria in 3.3. Recognizing that privacy risk management is a collective action problem, our PS2 framework contains this risk by replicating the collection, use, disclosure and disposition controls over to the DS. This framework contemplates that the privacy risks associated with shared data are contagious - if the data is transferred, responsibility for containing the risk lies with both provider and seeker of data. In other words, there is no automatic detachment of control or ownership by the DP when the data is shared.
4.1 Elements of PS2
While not framed around specific legislation, The components of our framework are rooted in principles and practices that underlie privacy laws and policies on both the national and global levels. The Fair Information Practices (FIPS) are considered de facto, international standards for information privacy and address collection, maintenance, use, disclosure, and processing of personal information. The FIPs have spawned a series of authoritative reports, guidelines, and model codes that implement these principles [13]. The PS2 is an attempt to apply these principles to the context of Internet measurement and sharing, aiming to build a touchstone for ethically defensible sharing scenarios.
Authorization - Internal authorization to share requires explicit consent of the DP and DS, and may require consent of individuals identifiable in network traffic, which can often be implicit via proxy consent with the DP.
Oversight - The DP and DS should obtain some external oversight of the proposed sharing, such an Institutional Review Boards (IRB).
Transparency - The DP and DS should agree on the objectives and obligations associated with shared data. Data-sharing terms might require that the algorithms be public but that the data and or conclusions remain protected, or vice versa [15].
Compliance with applicable law(s) - Collection and use of data should comport to a reasonable if not case-law precedented interpretation of laws that speak directly and clearly to sharing risks about proscribed behaviors or mandated obligations.
Purpose adherence - The data should be used to try to achieve the documented goal of sharing.
Access limitations - The shared data should be restricted from those who do not have a need and right to access the shared data.
Use specification and limitation - Unless otherwise agreed, The DP should deny merging or linking identifiable data contained in the traffic data.
Collection and Disclosure Minimization - The DS should apply privacy-sensitive techniques to stewardship of the network traffic such as:
Deleting sensitive data.
Deleting part(s) of the sensitive data.
Anonymizing Hashing De-Identifying all or parts of the sensitive data.
Aggregating or sampling.
Mediation analysis Human Proxy - a sandbox approach that involves "sending the code to the data" rather than releasing sensitive data for analyses.
Aging the data - such that traffic that contains sensitive data that is non-current, i.e., no longer a direct or indirect identifier.
Size quantity limitation - minimizing the quantity of traces shared.
Multiple layers of anonymization.
Audit tools - Techniques for provable compliance with policies for data use and disclosure, e.g., secure audit logging via a tamper-resistant, cryptographically protected device connected to but separate from the protected data, accounting policies to enforce access rules on protected data.
Redress mechanisms - Procedures to address harms from inappropriate data use or disclosure, including a feedback mechanism to support correction of datasets and or erroneous conclusions.
Quality data and analyses assurances - Awareness by the DS and DP of inference confidence levels associated with the data.
Security - Controls should reasonably ensure that sensitive PII is protected from unauthorized collection, use, disclosure, and destruction.
Training - some level of education and awareness of the privacy controls and principles by those who are authorized to engage the data.
Impact assessment - Research design should consider potential collateral effects on affected parties, and seeks methods that do no further harm.
Transfer to third parties - prohibited unless the same data control obligations are transferred, relative to the disclosure risks associated with that data.
4.2 Execution of PS2
To navigate the legal and ethical ambiguity around disclosure and use of network measurement data discussed in Section 2.1, we propose Memoranda of Understanding (MOUs), Memoranda of Agreement (MOAs), model contracts, and binding organizational policy as enforceable vehicles for addressing privacy risk both proactively and reactively. For less privacy-sensitive data, a unidirectional Acceptable Use Policy AUP may be cost-preferential to negotiating bilateral agreements. Explicit consent about controls for shared data provides an enforceable standard and certainty that can serve as a safe harbor for liability under data privacy laws.
4.3 Evaluation of PS2
The PS2 framework facilitates a rigorous examination of whether the proposed research balances privacy risks and utility rewards. For an oversight committee, it helps determine whether possible risks are justified, by specifically asking the user to assess sharing risks against technical and policy controls, as well as to assess the achievement of utility goals against those controls. For the prospective DP, the assessment will assist the determination whether or not to participate.
Table 1 assesses whether the privacy risks are mitigated by the primary components of PS2. The X's indicate that the particular PS2 policy component in the row fails to mitigate against the privacy risk enumerated in the corresponding column. The table starkly shows that purely policy components of PS2 still leave wide gaps in addressing the full range of privacy risks. Further, it suggests that technical minimization techniques (Section 4.1) can address all privacy risks, implying the sufficiency of a purely technical sharing framework in lieu of a policy control backdrop. However, evaluating minimization techniques against the utility goals in Table 2 show the weakness of this one-dimensional technical approach. This weakness is unsurprising, since data minimization techniques intentionally obfuscate information often essential to most Internet research. These utility gaps can be modulated ("dialed down") with the policy components of PS2. In short, a purely technical approach breaks down along the utility dimension, and the pure policy approach may leave too high privacy risk exposure, justifying a hybrid framework that covers both privacy risks and utility goals. We note that evaluation of a framework must also consider practical issues such as education costs, whether new privacy risk(s) are introduced, whether control(s) are forward-looking or also address legacy privacy risks, and free rider problems created by DPs who choose not to share.
PS2 Privacy Risk Public Disclosure Compelled Disclosure Malicious Disclosure Government Disclosure Misuse Inference Risk Re-ID Risk
Authorization X X X X X
Transparency X X X X X
Law Compliance X X X
Access Limitation X X X X
Use Specification X X X X
Minimization X
Audit Tools X X X X X X X
Redress X X X X X X X
Oversight X X X X
Data Quality X X X X X
Security X X X
Training/Education X X X X
Impact Assessment X X X X X
Table 1: Privacy risks evaluated against the PS2 privacy protection components. The X's indicate that the particular PS2 component in the row fails to mitigate against the privacy risk enumerated in the corresponding column. (Minimization refers to the techniques in Table 2.)
Minimiz.Tech. Is Purpose Worthwhile? Is there a need? Is it already being done? Are there alternatives? Is there a scientific basis? Can results be acted upon? Can DS & DP implement? Reasonable education costs? Forward & backward controls? No new privacy risks created? No free rider problem created?
Not Sharing X X X X X X X
Delete All X X X X X X X X
Delete Part X X X X X X X
Anonymize X X X X X X X X X
Aggregate X X X X X X X
Mediate (SC2D) X X X X
Age Data X X X X X X X
Limit Quantity X X X X X X X X X
Layer Anonymization X X X X X X X X
Table 2: PS2 minimization (of collection and disclosure) techniques evaluated against utility.
Allman, M., and Paxson, V. Issues and etiquette concerning use of shared measurement data. In IMC (2007).
Barbaro, M., and T. Zeller, J. A Face is Exposed for AOL Searcher No. 4417749. New York Times (Aug 2006).
Burkhart, M., Schatzmann, D., Trammel, B., Boschi, E., and Plattner, B. The role of network trace anonymization under attack. ACM SIGCOMM Comp. Comm. Rev. (2009).
Burstein, A. Amending the ECPA to Enable a Culture of Cybersecurity Research. Harvard Journal of Law & Technology 22, 1 (2008), 167-222.
C. B. Duke, et al., Ed. Network Science. The National Academies Press, Washington, 2006.
Cauley, L. NSA has massive database of Americans' phone calls. USA Today (May 2006).
Center, E. P. I. The U.S. First Circuit Court of Appeals upheld a New Hampshire law that bans the sale of prescriber-identifiable prescription drug data for marketing purposes. http://epic.org/privacy/imshealth/11_18_08_order.pdf.
Center for Democracy and Technology. CDT's Guide to Online Privacy, 2009.
Clabby, C. DNA Research Commons Scales Back. American Scientist 97, 3 (May 2009).
Claffy, K. Ten Things Lawyers should know about Internet research, August 2008. https://catalog.caida.org/paper/2008_lawyers_top_ten.
Crovella, M., and Krishnamurthy, B. Internet Measurement: Infrastructure, Traffic and Applications. John Wiley and Sons, Inc., 2006.
Narayanan, A., and Shmatikov, V. Robust De-anonymization of Large Sparse Datasets. IEEE Symposium on Security and Privacy (2008).
OECD. Guidelines on the protection of privacy and transborder flows of personal data, 1980.
Porter, C. De-Identified Data and Third Party Data Mining: The Risk of Re-Identification of Personal Information. Shilder Journal of Law, Communication, and Technology, 3 (2008).
Swire, P. A Theory of Disclosure for Security and Competitive Reasons: Open Source, Proprietary Software, and Government Agencies. Houston Law Review 42, 5 (January 2006).
See https://catalog.caida.org/search?query=types=dataset%20caida to explore related objects to this document in the CAIDA Resource Catalog.


In addition to Service Bulletin 18-02-02 for the stabilator (recently completed), Van's also released Service Bulletin 18-02-03 the very next day calling for an inspection of the anti-servo tabs that mount onto the stabilator. Service Bulletin 18-02-03 addresses potential cracking on the forward edge of anti-servo skins adjacent to the HS-1220-R & L control horns and between the spar and the control horn. This is another service bulletin that was introduced due to a very small number of RV-12's developing cracks adjacent to the HS-1220 control horns. Fortunately, the DOG Aviation RV-12 did not have any cracks on the anti-servo tab skins … but decided to complete Service Bulletin 18-02-03 anyway, so there won't be the need to mess with it later after the airplane is painted should a crack develop.
My finger is pointing to the area on the anti-servo tab skin that needs to be inspected for cracks.
1. - No cracks are discovered (that's good) fly on and reinspect during the next annual condition inspection.
2. - No cracks are discovered ... however, the builder/owner decides to go ahead and perform the steps in Service Bulletin 18-02-03 and just replace the inboard ribs to prevent cracks from forming.
3. - Cracks are found - This will involve not only replacing the inboard ribs on the anti-servo tabs and but also installing doublers to cover the cracks and strengthen the weak area.
Builders/ owners take note …. the DOG Aviation RV-12 was constructed using flush rivets, so you will see dimpled holes and flush rivets on the anti-servo tab's skins and ribs in most of the photos in this post. Those of you using the photos found here as a general reference, please disregard the skin and rib dimples and the use of flush rivets attaching the skins to the ribs … your anti-servo tab's will not be dimpled so you will be using the standard Van's LP4-3 pop rivets that come with the service bulletin to secure the skins to the new ribs.
Performing the work on the service bulletin (via either path 2 or 3) involves removal of the HS-1222 inboard rib on each anti-servo tab (the rib that the HS-1220 control horns are riveted onto) and replacement with a new HS-1230 rib included with the service bulletin parts. In addition to the two new HS-1230 ribs, Service Bulletin 18-02-03 also contains additional parts used to reinforce the anti-servo tabs if cracks are discovered … those parts are HS-1226A & HS-1226B (upper and lower doublers which come as one piece and require separating) and the HS-1228 spacer.
Parts included with Service Bulletin 18-02-03 consist of two HS-1230 ribs, HS-1226A upper doubler (on the left with the slot), HS-1226B lower doubler and the HS-1228 spacer.
Because the DOG Aviation RV-12 had no cracking on either anti-servo tab, the doublers will not be used …. however, the drawing and photo below shows where the parts will end up being installed if cracks are discovered.
Figure 7 from Service Bulletin 18-02-03 showing the placement of parts if cracks are discovered on the anti-servo tab skins.
This is how the parts will be installed should cracking be discovered on the anti-servo tab's skins. The spacer bar will be installed between the anti-servo tab and the HS-1226B lower doubler.
So what does replacing the inboard ribs of each anti-servo tab accomplish? It makes the assembly much stronger and will greatly reduce skin flexing adjacent to where the control horn attaches to the anti-servo tab's inboard ribs. By looking at the close-up below, one can clearly see that the original HS-1222 inboard rib ends just forward of the control horn leaving an open gap between it and the spar that allows the thin skin to flex between the HS-1222 rib and the spar. The HS-1230 replacement rib now goes all the way forward and has tabs which will be riveted to the anti-servo tab's spar … which can be seen in the photo below on the far right in the photo below.
Side view close-up of the inboard portion of the left anti-servo tab. As one can clearly see, the HS-1222 rib that the control horn is riveted onto ends just forward of control horn leaving a large gap between it and the anti-servo tab's spar on the far right.
The replacement HS-1230 rib sitting adjacent to the inboard side of the anti-servo tab … one can see it is longer and will extend further forward to the anti-servo tab's spar. Also noteworthy, there are now two additional flanges on the HS-1230 rib that will be riveted onto the anti-servo tab's spar. The new flange my finger is pointing towards to will be riveted to the anti-servo tab's spar at the rivet location my other finger is pointing towards. Extending the rib forward to tie into the spar should eliminate the skin flexing.
Following the steps in the service bulletin, if no cracking is found, the builder/owner is instructed to skip to Step 4 which is the removal of eight rivets holding the HS-1222 inboard rib in place and two spar rivets. Step 5 instructs the builder/owner to drill out the rivets holding the HS-1220 control horn onto the HS-1222 rib.
I would strongly suggest reversing the order of Steps 4 & 5 because the offset of the HS-1220 control horn makes it very difficult to safely pound out the mandrels of the rivets adjacent to the control horn prior to drilling. For me it was much easier to carefully remove the rivets securing the HS-1220 control horns to the HS-1222 inboard rib first. Once the control horn was removed pounding out the mandrels in the rivets adjacent to the control horn was a piece of cake.
To remove the rivets from the HS-1220 control horns the center of each rivet was marked and then center punched followed by very carefully drilling a tiny lead hole. This was followed by using a #40 drill bit with tape on it to denote the depth I wanted to drill which was just shy of getting all the way through the rivet head. After drilling most of the way through the head a nail set was used to carefully pop the head of the rivet away from the HS-1220 control horn. The HS-1220 control will be reused and riveted onto the new HS-1230 rib … so once the HS-1220 control horns are removed, mark them and set them aside.
After a tiny lead hole was drilled into the center of each rivet, a #40 drill bit was covered with masking tape to denote the depth to drill … which was not quite all the way through the head of the rivet.
My finger is pointing to the right anti-servo tab's HS-1220-R control horn removed from the anti-servo tab assembly. After removing both control horns from the anti-servo tabs mark them and set the control horns aside … they will be reused.
With the control horns removed, pounding out the rivet mandrels is now an easy task. After the mandrels were removed from the pop rivets, the rivets were removed using a #30 drill bit with a metal collar on it that only allowed the drill bit to go down just deep enough to almost go completely through the head of the rivets … but not quite. Then a nail set was used to remove the head of the rivets. After all the rivets are removed from the HS-1222 ribs, the ribs are removed … and discarded.
Carefully pounding out the mandrel from one of the rivets that would have been very difficult to access if the control horn were not removed first.
The original HS-1222 rib on the right will be replaced with the new HS-1230 rib on the left.
Here one can see a close up of the old HS-1222 rib on the right and the design of the new HS-1230 rib on the left. The new HS-1230 rib is longer and also has two flanges that will be riveted onto the anti-servo tab's spar.
After removing the old HS-1222 ribs, the new HS-1230 ribs are inserted in each anti-servo tab and secured with Clecos. There are four rivet holes that need to be match drilled. Two holes on the aft end of the HS-1230 ribs (one on each side of the anti-servo tab). The remaining two holes will be drilled through the anti-servo tab's spar into the flanges on the new HS-1230 ribs.
Using a #30 drill bit to match drill the anti-servo tab's spar to the flange on the new HS-1230 rib.
After the four holes have been drilled, the HS-1230 rib is removed from the anti-servo tab and the freshly drilled holes were deburred. Preparations for the final assembly begin by securing the HS-1220 control horns onto the new HS-1230 ribs with Clecos … then riveting each HS-1220 control horn onto the new HS-1230 ribs using five AN426AD3-3.5 rivets.
The right HS-1220 control horn secured on the new HS-1230 rib with Clecos and ready for riveting.
Riveting a HS-1220 control horn onto one of the new HS-1230 ribs using AD426AD3-3.5 rivets.
Both HS-1220-L&R control horns riveted onto the new HS-1230 ribs and ready for final riveting onto the anti-servo tabs. Here again, disregard the dimples in the ribs ... they are unique to the DOG Aviation RV-12.
Final assembly is straight forward … insert each HS-1230 rib/ HS-1220 control horn assembly under the anti-servo tab's skins and secure with Clecos. Eight rivets are used to rivet the skins to the ribs and two more rivets are used to rivet the spar to the flanges on the new HS-1230 ribs. The offset on the HS-1220 control horn does create a small access issue for the two rivets adjacent to it. The easiest way to get around the issue is to place a slight bend on the rivet's mandrel with a pair of pliers and then use a small piece of AEX wedge slid over the rivet. Doing this will place the hand rivet puller at a small enough of an angle to clear the HS-1220 control horn and still allow the rivet to be kept square to the skin while the rivet is set. Long time readers of the DOG Aviation Blog have seen this method used before when setting rivets in tight spots.
Riveting the anti-servo tab's skin to the new HS-1230 rib and HS-1220 control horn assembly using a pneumatic rivet puller. The pneumatic rivet puller can be used on all the rivets except the two adjacent to the HS-1220 control horn.
In this photo, one can see the PRG-26A close quarters hand rivet puller is outfitted with an AEX wedge between the rivet and the nose of the rivet puller. The use of a piece of AEX wedge as shown in this photo is a great way to access tight spots. The mandrel of the rivet is bent a little with pliers and the AEX wedge allows the rivet puller to be placed at a slight angle while pulling the rivet.
Completed Service Bulletin 18-02-03 (rib replacement only) … both anti-servo tabs now have the new HS-1230 ribs installed and are now ready to be placed back in service.
In the grand scheme of things, Service Bulletin 18-02-03 is not very time consuming and quite easy to accomplish. The only task that one really needs to perform as careful as possible is drilling out the HS-1220 control horn rivets … because one does not want the rivet holes to become elongated. As mentioned above, carefully mark and center punch the rivets, then drill a tiny lead hole #60 or smaller to minimize drill bit drift when drilling with the #40 drill bit.


Deep into life with GDPR compliance already, and things already seem to have settled down on a very clear pattern. Businesses big and small struggled throughout the months of April and May as most left actionable items until very very late.
It took all of 48 minutes for the first GDPR complaint to come in, and major lawsuits worth €9 bn.
Ahrefs.com can pretty much confirm the chaos that surrounded the online world with businesses hectically searching for keywords like GDPR compliance, GDPR checklist and GDPR for dummies showing immense spikes for the month of May, some showing over 4 times the volume they had in April.
General concerns that GDPR won't be well understood have been totally confirmed. Surveys conducted at the start of the year that only 21% of organizations are concerned about GDPR and have a plan in place, while 27% of respondents are concerned about GDPR, yet have no plan in place, wrote the script for a general world panic in the month of May.
How long did it take for the first GDPR complaint to come in? In case you haven't read, you can probably guess.
Yeah I know, you'll say you totally saw this coming, and that the latest Facebook scandals were clearly pointing to this happening.
Wait – it gets worse. The first month registered a whooping number of GDPR complaints – almost 2000. Now that's probably going to surprise a few more of you. We used this really awesome breakdown provided by Iapp to map out the number of complaints received by country.
How did GDPR compliance affect small businesses?
As Thycotic reports, and our survey (keep on reading…) confirms, several companies elected to completely shut down their service to EU states at the end of a month they very funnily called GDPR email Spam Month. Every business big and small struggled to get email consent from their marketing lists. The first couple of emails were fun, but receiving 2 rounds of consent emails from everybody you had a subscription at (and at least as many from companies you never heard of) were key factors to creating a huge mess in your inbox.
Why was it such a mess? Well, for starters, the general public didn't really know what GDPR was before it all started to happen, and in all honesty email marketers didn't always do a very good job at informing them before asking them for their consent.
One of the worst GDPR consent examples was a renowned hit & miss by Premier League club Manchester United, who thought it would be a good idea to use the sideline ad billboards to remind fans they have to send their consent during a matchday. To confirm the general confusion, a survey showed that only 54% of people actually understood what GDPR was about.
Thankfully, there were some who did it like absolute pro's and set high standards.
After small business owners searched for GDPR for dummies keywords online, then looked into hundreds of other searches and examples, they finally crafted their emails and, with high hopes and big hearts waited for consent. Some got it better than others obviously, but even so, we can have a high-level picture of what happened.
We surveyed 25 US-based businesses and the results were not all that promising to tell the truth.
What did we find out after surveying 25 US-based businesses?
We already said this but it's worth repeating. The results were far from promising as we somewhat expected. The case here is probably that EU contacts were so fed up being bombarded by spam that they couldn't tell the difference between them anymore – or didn't care to do so.
A whopping 14 businesses out of these 25 decided to completely stop sending emails to EU's. After deliberating and seeing results from some of their peers, they decided that saving 10-15% of their EU mailing lists won't be worth the investment and hustle. I can definitely see what they were thinking though. Continue reading and we'll provide some stats on how did the 11 businesses that chose to send the consent emails do.
Only 4 of the 25 businesses (less than 20%) felt that the first email consent was satisfying enough to mandate a second series of email consent messaging.
Unfortunately, I'll have to repeat myself. I don't condemn their decision as it's totally backed up by data.
I need to make myself clear. These were all businesses that were actively doing business with EU clients.
Okay, but what were the stats on the rest of the GDPR compliance sends?
We also compiled a statistic showing what the results were for those 11 businesses that decided to send consent requests to their subscribers. Now, you'll be able to see the impact GDPR had for US businesses and analyze for yourself. First touch emails got between 3.7% and 28.3% open rates, and as little as 0,8% and 4.9% click rates.
When looking into standard email engagement, being on the high end of these statistics wouldn't look bad at all, right? Well – when the stake is being able to retain that contact in your list or having to completely remove it, the stats don't look that good anymore. In laymen's terms, it means that most US businesses were able to keep between 8 and 49 contacts out of each 1000 contact lists.
Sure – but there must have been a second round, right? Well yeah – in some cases businesses sent second-round consent request. Click rates ranged from 0,8% to 16%, so much better this time. Even so, statistics were pretty worrisome.
How does this translate to businesses impacted by the new California GDPR law?
I can only imagine that California's new law of data protection is going to provide nothing but more of the same. I think the statistics will most likely be better than what we've presented above, but not to a wild extent.
US businesses should definitely brace for some sort of impact, especially the smaller ones. If your list usually gets a good amount of engagement, say around 40% open rates, you should probably expect to lose about half of your California-based email contacts. You should also plan ahead for some expenses, as you'll definitely need to send at least 2 series of email consent request.
As you could see from the above stats, the first series won't get you too many results. People are busy, so they will probably not act until the very last moment.
1. For starters – try getting the consent as soon as you possibly can. This will help your brand stand out. If you wait until the very last moment like some other companies did with EU's GDPR, you'll just look like another desperate business in California's version of 'Spam month'. Sending consent requests nice & early will help you stay ahead of your competition.
2. Build a nice email HTML design. You can have a look at our drag & drop email builder in case you haven't yet. Use a big and clear CTA. You don't care about selling your product when asking for GDPR consent, so DON'T include any unnecessary info that doesn't pertain to the task at hand.
3. Inform your client. Read this again – Inform YOUR client! Don't send him to another link to read on some other website full of info he's more or less interested in knowing. Make an effort to summarize the information in a bitesize text that is easily digestable for anybody. This will boost your chances to get consent considerably.
4. This one's important. Don't send over a text that looks like a law citation. Don't use 2000 words in your content! Explain what this is and why they should opt-in using 3-4 rows of clear easy to read text.
Hopefully this article provided you with enough actionable information and statistics to help guide your path. There's loads of gdpr checklist articles out there, so we wanted to focus on bringing you with a real unique case study that should provide you with enough knowledge to help get the most out of this change, and retain as many contacts as you possibly can.
Feel free to reach out if you have any insights you'd like to share with us – or need any help building your campaign out!


text:
package com.missionhub.application;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OnAccountsUpdateListener;
import android.accounts.OperationCanceledException;
import android.os.Bundle;
import android.util.Log;

import com.facebook.Request;
import com.facebook.Response;
import com.facebook.Session.StatusCallback;
import com.facebook.SessionLoginBehavior;
import com.facebook.model.GraphUser;
import com.missionhub.BuildConfig;
import com.missionhub.R;
import com.missionhub.api.Api;
import com.missionhub.api.Api.Include;
import com.missionhub.api.ApiException;
import com.missionhub.api.ApiOptions;
import com.missionhub.api.ApiRequest;
import com.missionhub.api.InvalidFacebookTokenException;
import com.missionhub.authenticator.Authenticator;
import com.missionhub.authenticator.AuthenticatorActivity;
import com.missionhub.event.FacebookEvent;
import com.missionhub.event.OnOrganizationChangedEvent;
import com.missionhub.event.SessionEvent;
import com.missionhub.model.Organization;
import com.missionhub.model.Permission;
import com.missionhub.model.Person;
import com.missionhub.model.UserDao;
import com.missionhub.util.ErrbitReportSender;
import com.missionhub.util.SafeAsyncTask;
import com.missionhub.util.TaskUtils;

import org.acra.ACRA;
import org.apache.commons.lang3.StringUtils;
import org.holoeverywhere.widget.Toast;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.FutureTask;

public class Session implements OnAccountsUpdateListener {

    /**
     * the logging tag
     */
    public static final String TAG = Session.class.getSimpleName();
    /**
     * the person id
     */
    private long mPersonId = -1;
    /**
     * the organization id
     */
    private long mOrganizationId = -1;
    /**
     * the android account manager
     */
    private AccountManager mAccountManager;
    /**
     * the account being used
     */
    private Account mAccount;
    /**
     * task used to update the current person data
     */
    private SafeAsyncTask<Person> mUpdatePersonTask;
    /**
     * task used to update current organization
     */
    private SafeAsyncTask<Void> mUpdateOrganizationTask;
    /**
     * task used to update permissions
     */
    private SafeAsyncTask<Void> mUpdatePermissionsTask;

    private StatusCallback mFacebookStatusCallback;
    private SafeAsyncTask<Void> mOpenTask;
    private SafeAsyncTask<Void> mCloseTask;
    private SessionState mState = SessionState.CLOSED;

    /**
     * Creates a new session object and sets up the account manager
     */
    protected Session() {
    }

    /**
     * @return the system account manager
     */
    public AccountManager getAccountManager() {
        if (mAccountManager == null) {
            mAccountManager = AccountManager.get(Application.getContext());
            mAccountManager.addOnAccountsUpdatedListener(this, null, false);
        }
        return mAccountManager;
    }

    public Account[] getAllAccounts() {
        return getAccountManager().getAccountsByType(Authenticator.ACCOUNT_TYPE);
    }

    public Account getAccount() {
        if (mAccount != null) {
            return mAccount;
        }
        return findAccount(mPersonId, 0);
    }

    public void open() {
        long lastPersonId = SettingsManager.getSessionLastPersonId();
        if (lastPersonId > 0) {
            open(findAccount(lastPersonId, 0));
        } else {
            open(null);
        }
    }

    public long getAccountPersonId(Account account) {
        if (account != null) {
            String id = getAccountManager().getUserData(account, Authenticator.KEY_PERSON_ID);
            if (id != null) {
                return Long.parseLong(id);
            }
        }
        return -1;
    }

    public long getAccountFacebookId(Account account) {
        if (account != null) {
            String id = getAccountManager().getUserData(account, Authenticator.KEY_FACEBOOK_ID);
            if (id != null) {
                return Long.parseLong(id);
            }
        }
        return -1;
    }

    public void open(final Account account) {
        if (mOpenTask != null) return;

        mOpenTask = new SafeAsyncTask<Void>() {
            public FutureTask<Void> organziationTask;
            public FutureTask<Person> personTask;
            public FutureTask<Void> permissionTask;

            @Override
            public Void call() throws Exception {
                setAndPostState(SessionState.OPENING);

                // allow logging in with a hardcoded token
                if (Application.isDebug() && StringUtils.isNotEmpty(Configuration.getLoginAs())) {
                    try {
                        Person person = Api.getPersonMe(Configuration.getLoginAs(), ApiOptions.builder().include(Include.user).build()).get();
                        addSystemAccount(person, Configuration.getLoginAs());
                    } catch (ApiException exception) {
                        setAndPostException(exception);
                        return null;
                    }
                }

                // if we have a facebook id but no person id, grab the person's data
                long personId = getAccountPersonId(account);
                long facebookId = getAccountFacebookId(account);
                if (facebookId > 0 && personId <= 0) {
                    final String authToken = getAccountManager().peekAuthToken(account, Authenticator.ACCOUNT_TYPE);
                    try {
                        Person person = Api.getPersonMe(authToken, ApiOptions.builder().include(Include.user).build()).get();
                        addSystemAccount(person, authToken);
                        personId = person.getId();
                    } catch (InvalidFacebookTokenException exception) {
                        personId = -1;
                    }
                }

                // setup data for the account
                if (account != null) {
                    mAccount = account;
                    mPersonId = personId;
                    mOrganizationId = SettingsManager.getSessionOrganizationId(personId);
                    if (mOrganizationId <= 0 && personId > 0) {
                        Person person = Application.getDb().getPersonDao().load(personId);
                        if (person != null) {
                            mOrganizationId = person.getPrimaryOrganizationId();
                        }
                    }
                } else {
                    mAccount = null;
                    mPersonId = -1;
                    mOrganizationId = -1;
                }

                // send choose or no account message if there is no initialized person
                if (mPersonId <= 0) {
                    if (getAllAccounts().length > 0) {
                        setAndPostState(SessionState.CHOOSE_ACCOUNT);
                    } else {
                        setAndPostState(SessionState.NO_ACCOUNT);
                    }
                    return null;
                }

                // setup ACRA data
                if (Configuration.isACRAEnabled()) {
                    try {
                        ErrbitReportSender.putErrbitData(ErrbitReportSender.ErrbitReportField.USER_ID, String.valueOf(mPersonId));
                        ACRA.getErrorReporter().putCustomData("MISSIONHUB_ORGANIZATION_ID", String.valueOf(mOrganizationId));
                    } catch (Exception e) { /* ignore */ }
                }

                Application.trackNewSession();

                SettingsManager.setSessionLastPersonId(mPersonId);

                // update the permissions
                Application.postEvent(new SessionEvent(mState, Application.getContext().getString(R.string.session_updating_permissions)));
                permissionTask = updatePermissions(false);
                permissionTask.get();

                // update the person
                Application.postEvent(new SessionEvent(mState, Application.getContext().getString(R.string.session_updating_person)));
                personTask = updatePerson(false);
                personTask.get();

                // update the current organization
                Application.postEvent(new SessionEvent(mState, Application.getContext().getString(R.string.session_updating_current_organization)));
                organziationTask = updateCurrentOrganization(false);
                organziationTask.get();

                setAndPostState(SessionState.OPEN);
                return null;
            }

            @Override
            public void onException(Exception e) {
                setAndPostException(e);
            }

            @Override
            public void onFinally() {
                TaskUtils.cancel(permissionTask, personTask, organziationTask);
                mOpenTask = null;
            }
        };
        mOpenTask.execute();
    }

    /**
     * Closes the session
     */
    public void close() {
        if (mCloseTask != null) return;

        mCloseTask = new SafeAsyncTask<Void>() {
            @Override
            public Void call() throws Exception {
                SettingsManager.setSessionLastPersonId(-1);
                mPersonId = -1;
                mOrganizationId = -1;
                mAccount = null;
                TaskUtils.cancel(mOpenTask);
                TaskUtils.cancel(mUpdatePersonTask);
                TaskUtils.cancel(mUpdatePermissionsTask);
                TaskUtils.cancel(mUpdateOrganizationTask);

                if (Configuration.isACRAEnabled()) {
                    try {
                        ErrbitReportSender.removeErrbitData(ErrbitReportSender.ErrbitReportField.USER_ID);
                        ACRA.getErrorReporter().removeCustomData("MISSIONHUB_ORGANIZATION_ID");
                    } catch (Exception e) { /* ignore */ }
                }

                Application.trackEvent("session", "logout", "logout");
                Application.trackNewSession();

                // clear database cache
                Application.getDb().clear();

                setAndPostState(SessionState.CLOSED);
                return null;
            }

            @Override
            public void onException(Exception e) {
                setAndPostException(e);
            }

            @Override
            public void onInterrupted(Exception e) {

            }

            @Override
            public void onFinally() {
                mCloseTask = null;
            }
        };
        mCloseTask.execute();
    }

    public Account addSystemAccount(Person person, String facebookToken) {
        Account account = findAccount(person.getId(), person.getFb_uid());
        if (account != null) {
            getAccountManager().setAuthToken(account, Authenticator.ACCOUNT_TYPE, facebookToken);
        } else {
            account = new Account(person.getName(), Authenticator.ACCOUNT_TYPE);
            final Bundle userdata = new Bundle();
            userdata.putString(Authenticator.KEY_PERSON_ID, String.valueOf(person.getId()));
            userdata.putString(Authenticator.KEY_FACEBOOK_ID, String.valueOf(person.getFb_uid()));
            getAccountManager().addAccountExplicitly(account, facebookToken, userdata);
            getAccountManager().setAuthToken(account, Authenticator.ACCOUNT_TYPE, facebookToken);
        }
        return account;
    }

    public Account addSystemAccount(GraphUser user, String facebookToken) {
        Account account = findAccount(0, Long.parseLong(user.getId()));
        if (account != null) {
            getAccountManager().setAuthToken(account, Authenticator.ACCOUNT_TYPE, facebookToken);
        } else {
            account = new Account(user.getName(), Authenticator.ACCOUNT_TYPE);
            final Bundle userdata = new Bundle();
            userdata.putString(Authenticator.KEY_FACEBOOK_ID, user.getId());
            getAccountManager().addAccountExplicitly(account, facebookToken, userdata);
            getAccountManager().setAuthToken(account, Authenticator.ACCOUNT_TYPE, facebookToken);
        }
        return account;
    }

    private void setAndPostState(SessionState state) {
        mState = state;
        Application.postEvent(new SessionEvent(mState));
    }

    private void setAndPostException(Exception exception) {
        mState = SessionState.CLOSED_ERROR;
        Application.postEvent(new SessionEvent(exception));
    }

    /**
     * Returns the personid for this session
     *
     * @return
     */
    public Long getPersonId() {
        return mPersonId;
    }

    /**
     * Returns the organization the person is currently working under.
     *
     * @return
     */
    public long getOrganizationId() {
        if (mOrganizationId <= 0) {
            return getPrimaryOrganizationId();
        }
        return mOrganizationId;
    }

    public Organization getOrganization() {
        return Application.getDb().getOrganizationDao().load(getOrganizationId());
    }

    /**
     * Sets the user's organization id
     *
     * @param organizationId
     */
    public void setOrganizationId(final long organizationId, boolean updateOrganization, boolean async) throws Exception {
        if (organizationId != getOrganizationId()) {
            if (getPerson().isAdminOrUser(organizationId)) {
                mOrganizationId = organizationId;
            } else {
                Application.showToast(R.string.session_no_longer_admin_or_user, Toast.LENGTH_LONG);
                mOrganizationId = getPrimaryOrganizationId();
            }
            if (updateOrganization) {
                FutureTask<Void> future = updateCurrentOrganization(true);
                if (!async) {
                    future.get();
                }
            }
            SettingsManager.setSessionOrganizationId(getPersonId(), mOrganizationId);
            Application.postEvent(new OnOrganizationChangedEvent(mOrganizationId));
        }
    }

    /**
     * Returns the user's primary organization
     *
     * @return
     */
    public long getPrimaryOrganizationId() {
        try {
            return Application.getDb().getUserDao().queryBuilder().where(UserDao.Properties.Person_id.eq(mPersonId)).unique().getPrimary_organization_id();
        } catch (final Exception e) {
            /* ignore */
        }
        return -1l;
    }

    /**
     * Returns the access token for the missionhub account in the system. This is a blocking method and should not be
     * called from the ui thread.
     *
     * @return
     */
    public String getAccessToken() throws AuthenticatorException, OperationCanceledException, IOException {
        return getAccountManager().blockingGetAuthToken(getAccount(), Authenticator.ACCOUNT_TYPE, true);
    }

    /**
     * Updates the person from the MissionHub Server Posts SessionUpdatePersonEvent
     */
    public FutureTask<Person> updatePerson(final boolean force) {
        if (!force && mUpdatePersonTask != null) return mUpdatePersonTask.future();

        TaskUtils.cancel(mUpdatePersonTask);

        mUpdatePersonTask = new SafeAsyncTask<Person>() {
            private final long mOneDayMillis = 60 * 60 * 24 * 1000;
            public ApiRequest<Person> mApiRequest;

            @Override
            public Person call() throws Exception {
                final long lastUpdated = SettingsManager.getInstance().getUserSetting(getPersonId(), "person_" + getPersonId() + "_updated", 0l);
                final long currentTime = System.currentTimeMillis() - 1000;

                if (!Configuration.isSkipSessionUpdates() && (lastUpdated < System.currentTimeMillis() - mOneDayMillis || force)) {
                    mApiRequest = Api.getPersonMe(ApiOptions.builder() //
                            .include(Include.all_organization_and_children) //
                            .include(Include.all_organizational_permissions) //
                            .include(Include.answer_sheets) //
                            .include(Include.answers) //
                            .include(Include.contact_assignments) //
                            .include(Include.email_addresses) //
                            .include(Include.phone_numbers) //
                            .include(Include.current_address) //
                            .include(Include.interactions) //
                            .include(Include.user) //
                            .include(Include.organizational_labels)
                            .build());
                    mApiRequest.get();

                    SettingsManager.getInstance().setUserSetting(getPersonId(), "person_" + getPersonId() + "_updated", currentTime);
                }

                // update the account with new data to keep it fresh
                getAccountManager().setUserData(getAccount(), Authenticator.KEY_PERSON_ID, String.valueOf(getPerson().getId()));
                getAccountManager().setUserData(getAccount(), Authenticator.KEY_FACEBOOK_ID, String.valueOf(getPerson().getFb_uid()));
                getAccountManager().setUserData(getAccount(), AccountManager.KEY_ACCOUNT_NAME, getPerson().getName());

                return getPerson();
            }

            @Override
            public void onSuccess(final Person person) {
            }

            @Override
            public void onFinally() {
                if (mApiRequest != null) {
                    mApiRequest.disconnect();
                }
                mUpdatePersonTask = null;
            }

            @Override
            public void onException(final Exception e) {
            }
        };
        Application.getExecutor().execute(mUpdatePersonTask.future());
        return mUpdatePersonTask.future();
    }

    public FutureTask<Void> updateCurrentOrganization(final boolean force) {
        return updateOrganization(getOrganizationId(), force);
    }

    public FutureTask<Void> updateOrganization(final long organizationId, final boolean force) {
        TaskUtils.cancel(mUpdateOrganizationTask);

        mUpdateOrganizationTask = new SafeAsyncTask<Void>() {

            private final long mOneWeekMillis = 60 * 60 * 24 * 7 * 1000;
            public ApiRequest<Organization> mApiRequest;

            @Override
            public Void call() throws Exception {
                final long lastUpdated = SettingsManager.getInstance().getUserSetting(getPersonId(), "organization_" + organizationId + "_updated", 0l);
                final long currentTime = System.currentTimeMillis() - 1000;

                if (!Configuration.isSkipSessionUpdates() && (lastUpdated < System.currentTimeMillis() - mOneWeekMillis || force)) {

                    mApiRequest = Api.getOrganization(organizationId, ApiOptions.builder()
                            .include(Include.all_questions)
                            .include(Include.keywords)
                            .include(Include.users)
                            .include(Include.labels)
                            .include(Include.surveys)
                            .include(Include.organizational_permission)
                            .include(Include.interaction_types)
                            .build());

                    mApiRequest.get();

                    getPerson().refreshAll();
                    getPerson().getOrganizationHierarchy();

                    SettingsManager.getInstance().setUserSetting(getPersonId(), "organization_" + organizationId + "_updated", currentTime);
                }

                return null;
            }

            @Override
            public void onFinally() {
                if (mApiRequest != null) {
                    mApiRequest.disconnect();
                }
                mUpdateOrganizationTask = null;
            }

            @Override
            public void onException(final Exception e) {
                Log.e(TAG, e.getMessage(), e);
            }
        };

        Application.getExecutor().execute(mUpdateOrganizationTask.future());
        return mUpdateOrganizationTask.future();
    }

    private FutureTask<Void> updatePermissions(final boolean force) {
        TaskUtils.cancel(mUpdatePermissionsTask);

        mUpdatePermissionsTask = new SafeAsyncTask<Void>() {

            private final long mOneWeekMillis = 60 * 60 * 24 * 7 * 1000;
            public ApiRequest<List<Permission>> mApiRequest;

            @Override
            public Void call() throws Exception {
                final long lastUpdated = SettingsManager.getInstance().getSetting("permissions_updated", 0l);
                final long currentTime = System.currentTimeMillis() - 1000;

                if (!Configuration.isSkipSessionUpdates() && (lastUpdated < System.currentTimeMillis() - mOneWeekMillis || force)) {
                    mApiRequest = Api.listPermissions();
                    mApiRequest.get();

                    SettingsManager.getInstance().setSetting("permissions_updated", currentTime);
                }

                return null;
            }

            @Override
            public void onSuccess(final Void _) {
            }

            @Override
            public void onFinally() {
                if (mApiRequest != null) {
                    mApiRequest.disconnect();
                }
                mUpdatePermissionsTask = null;
            }

            @Override
            public void onException(final Exception e) {
            }

            @Override
            public void onInterrupted(final Exception e) {
            }
        };
        final FutureTask<Void> future = mUpdatePermissionsTask.future();
        Application.getExecutor().submit(future);
        return future;
    }

    /**
     * @return person object associated with the session
     */
    public Person getPerson() {
        return Application.getDb().getPersonDao().load(getPersonId());
    }

    @Override
    public void onAccountsUpdated(final Account[] accounts) {
        if (getPersonId() > 0 && findAccount(getPersonId(), 0) == null) {
            close();
        }
    }

    /**
     * Find the system account by a personId
     *
     * @param personId
     * @param facebookId
     * @return
     */
    private Account findAccount(final long personId, final long facebookId) {
        final Account[] accounts = getAllAccounts();
        for (final Account account : accounts) {
            final String strPersonId = getAccountManager().getUserData(account, Authenticator.KEY_PERSON_ID);
            final String strFacebookId = getAccountManager().getUserData(account, Authenticator.KEY_FACEBOOK_ID);

            if (personId > 0 && strPersonId != null && strPersonId.equalsIgnoreCase(String.valueOf(personId))) {
                return account;
            }
            if (facebookId > 0 && strFacebookId != null && strFacebookId.equalsIgnoreCase(String.valueOf(facebookId))) {
                return account;
            }
        }
        return null;
    }

    /**
     * Deletes an account for the specified person. This should not be called from the main thread.
     */
    public void deleteAccount() {
        if (getAccount() != null) {
            try {
                getAccountManager().removeAccount(getAccount(), null, null).getResult();
                SettingsManager.setSessionLastPersonId(-1);
                close();
            } catch (final Exception e) {
                /* ignore */
            }
        }
    }

    /**
     * Reports the Session as having a bad access token
     */
    public void invalidateAuthToken() {
        getAccountManager().invalidateAuthToken(Authenticator.ACCOUNT_TYPE, null);
        setAndPostState(SessionState.INVALID_TOKEN);
    }

    /*--------------------------------*\
     * Permission Convenience Methods
	\*--------------------------------*/

    /**
     * Returns true if the current person is an admin in the current organization
     *
     * @return
     */
    public boolean isAdmin() {
        Person p = getPerson();
        return p != null && p.isAdmin();
    }

    /**
     * Returns true if the current person is a user in the current organization
     *
     * @return
     */
    public boolean isUser() {
        Person p = getPerson();
        return p != null && p.isUser();
    }

    /**
     * Return true if the current person is an admin or user in the current organization
     */
    public boolean isAdminOrUser() {
        Person p = getPerson();
        return p != null && p.isAdminOrUser();
    }

    public com.facebook.Session openFacebookSession(AuthenticatorActivity activity, boolean sso) {
        closeFacebookSession();

        com.facebook.Session.Builder builder = new com.facebook.Session.Builder(Application.getContext());
        builder.setApplicationId(Configuration.getFacebookAppId());
        com.facebook.Session session = builder.build();
        com.facebook.Session.setActiveSession(session);

        com.facebook.Session.OpenRequest request = new com.facebook.Session.OpenRequest(activity);
        // suppress sso behavior when an account already exists and the request is not a re-auth
        if (!sso) {
            request.setLoginBehavior(SessionLoginBehavior.SUPPRESS_SSO);
        }
        request.setCallback(getFacebookStatusCallback());
        session.openForRead(request);
        return session;
    }

    public StatusCallback getFacebookStatusCallback() {
        if (mFacebookStatusCallback == null) {
            mFacebookStatusCallback = new StatusCallback() {
                @Override
                public void call(com.facebook.Session session, com.facebook.SessionState state, Exception exception) {
                    Application.postEvent(new FacebookEvent(session, state, exception));
                }
            };
        }
        return mFacebookStatusCallback;
    }

    public GraphUser blockingGetFacebookGraphUser() {
        try {
            Response response = Request.newMeRequest(com.facebook.Session.getActiveSession(), null).executeAndWait();
            if (response.getError() != null) {
                throw new RuntimeException(response.getError().getErrorMessage());
            }
            return response.getGraphObjectAs(GraphUser.class);
        } catch (Exception e) { /* ignore */ }
        return null;
    }

    public SessionState getState() {
        return mState;
    }

    public void closeFacebookSession() {
        com.facebook.Session session = com.facebook.Session.getActiveSession();
        if (session != null) {
            session.closeAndClearTokenInformation();
        }
    }

    public boolean isOpen() {
        return mState == SessionState.OPEN;
    }
}

redpajama_set_name: RedPajamaGithub


These natural bathroom accessories all feature neutral colour schemes which make them suitable for a wide range of settings. An ideal choice if you want something a little more understated.
We stock natural accessories from leading industry brands including Wenko and Croydex so each product you receive will have been manufactured to a fantastic standard.
Create a bathroom that's coordinated with our large assortment of accessories. Choose from various bath mats, robe hooks, soap dispensers and more and give your home a trendy, sophisticated feel.
Bought as part of a set along with the loo brush holder to add to the finishing touch of my new shower room.
Thrilled to bits with this toothbrush holder and the other pieces that I purchased that match this. These pieces certainly look good in my new shower room.
As above purchased with the items above and once again it certainly adds something to the finishing touches of my new shower room.


Dog lovers everywhere will be happy to know that there are plenty of jobs and career paths that allow for spending your days with man's best friend. While some of the options are obvious, such as veterinarian or dog trainer, those are just the tip of the iceberg. Here are more than a dozen employment choices for those who want to work alongside Fido. Remember, too, that many companies are also becoming dog-friendly.
Pet psychologists work with animals, typically household pets, which have behavioral issues, seeking to determine why a pet may be stressed or misbehaving. The psychologist's goal is to try to provide solutions based on their understanding of animal behavior and psychology. It's a job that can involve working with far more than just dogs. Some work with fish, reptiles, horses, and livestock. The average salary for "animal psychologist" ranges from approximately $38,700 per year for intervention specialists to $206,575 per year for psychologists, according to Indeed. Animal psychologists often have training as veterinarians or a traditional psychology degree.
Want to spend your time taking pictures of Rover, Fido, and Benji, creating beloved portraits for dog owners everywhere? Look no further than pet photographer. "It's a great career for anyone who loves animals and wants to spend more time around them within their work," says Texas-based pet photographer Jenna Regan. While the Bureau of Labor Statistics does not separate animal photographer salary data from the profession as a whole, photographers earned a median annual wage of $32,490 in 2017.
Want to help dogs, and other animals for that matter, that are suffering from discomfort related to musculoskeletal issues? Becoming an animal chiropractor, which is no easy task, may be the career for you. The American Veterinary Chiropractic Association is the most prominent certification group for this profession. Certification candidates must hold a Doctor of Chiropractic or Doctor of Veterinary Medicine degree, pass a comprehensive written exam, and complete an intensive practical-skills exam. As for the annual salary, it varies from $30,000 to $200,000. Earnings depend on such things as how many clients a chiropractor attracts and the hourly rate charged.
Nearly any frequent flyer is likely to have seen a dog at the airport walking alongside a security officer. The highly trained animal is there to sniff out explosives. Airport security is just one example of where explosive-detection dogs are used. They can also be found patrolling sports stadiums, cargo facilities, and loading docks. In addition to the benefit of working with animals, being an explosive-detection dog handler also pays quite well — anywhere from $47,000 to $98,500 per year.
Calling all artists and creative types who love animals. Why not combine your passions and become a dog artist? Spend your days painting portraits or drawing sketches of someone's beloved canine companion. By some accounts, the salary for such a profession can be as much as $53,000 annually.
Beyond explosive-detection dogs, man's best friend fills a variety of other public service roles, such as working with police in tasks such as drug detection. There are also search and rescue dogs that are trained to find lost people. Many of these dogs typically live with their handlers and then are called out to conduct searches when needed. Police and sheriff's patrol officers averaged $64,490 a year in 2017, according to Bureau of Labor Statistics. And while some police departments require only a high school diploma to be a canine officer, others seek candidates who have an associate's or bachelor's degree.
For those with a passion for pure-bred dogs, shows and competitions are yet another employment option. There's a variety of jobs associated with dog shows, such as working as a dog handler. Paid by dog owners to present their dogs, professional handlers play an important role. A handler must have a detailed understanding of breed standards, be willing to travel regularly, and is responsible for obedience training and grooming. There is no formal training to get your foot in the door, most start by working as a handler's assistant. Elite handlers can make over $100,000, but most make around $61,000.
Dog fashion designer is no joke. It's just as important as a human fashion designer, (at least in some people's minds). Design clothes and accessories for pets and have plenty of furry friends around the office or runway to model your goods. It's a profession that typically involves picking up a fashion degree, naturally. But those who find success with this path can earn more than $100,000 annually.
Explosives detection is not the only example of dogs working at the airport. Many airports have begun introducing dogs to assist travelers with anxiety and stress relief, in which case a handler typically walks the dog around the airport allowing travelers to pet the animal. More than 30 airports around the country have such programs. Similarly, animal-assisted therapy can involve taking a dog to a nursing home, hospital, or schools. To fill such roles, typically the owner and the dog must go through training. And it's best if your dog is housebroken, nonaggressive, and not fearful of strangers. As for pay, there often is little compensation, but that's not always the case, some make as much as $32,100.
In addition to having an affection for dogs, boarding or doggie day care typically requires having some business or management experience, and the space of course to properly house dogs. Your daily duties will typically include supervising play time, cleaning kennels, and interacting with customers. Dog kennel owners average nearly $59,000 annually, according to PayScale.
Dog breeding can be a fulfilling job for those who want to work with four legged friends, says Lazhar Ichir, founder of Breeding Business, an educational platform for ethical dog breeders. But that doesn't mean it's the easiest job. "A dog breeder must become a true expert on the breed they're working with," says Ichir, adding that daily tasks of a breeder include cleaning kennels, training, and playing with dogs. "The best time of each year is when a litter is on the way, and then the puppies are there. It's an incredibly beautiful feeling." Animal breeders averaged $44,650 annually in 2015.
Think of this job as the cosmetologist and hairdresser of the doggie world. Shampooing, trimming nails, and generally primping dogs to look and feel their best is the dog groomer's daily focus. To get started, try working as an assistant for a dog groomer and learn the basics. There are also dog grooming programs and schools. Some people even choose to get certification from the National Dog Groomers Association. Despite all the fun involved in making dogs look beautiful, the pay can be somewhat meager, with annual income hovering around $23,000.
For those who many not want to go through all of the training required to become a veterinarian, another option is veterinary technician. Landing such a job typically requires passing a test to become credentialed and completing two to four years of school in a vet tech program. Expect to earn about $33,800 annually.
One last option to consider for those who want to spend their career side-by-side with Fido. Working in a pet supply store such as Petco or PetSmart allows for contact with animals all day long. And other than learning how to operate a cash register or stock shelves, there's very little training required. As for salaries, retail clerks regardless of what type of store it is, make about $10 per hour. Think of it as a good summer job for a student or perhaps a side job.


In July, the Councils of the Municipality of the District of West Hants and the Town of Windsor passed a motion to enter into negotiations with the Province to consolidate the two municipalities through special legislation. Stemming from the discussions that took place over the past two months, the Councils met with the Department of Municipal Affairs on September 24 and determined that, for the betterment and the sustainability of the region, consolidation of the two municipal units is the best way forward.
The Special legislation to consolidate the Municipality of the District of West Hants and the Town of Windsor was brought forward at the Legislature this morning beginning the process of creating a new regional municipality by April 1, 2020. The legislation consists of transitional matters including the appointment of a transition coordinator and the creation of a transition committee who will oversee the process.
Councils of the Municipality and the Town remain adamant that citizen engagement and communication be at the forefront throughout this process and are looking forward to working with the citizens in both municipal units to build a new, stronger regional government.