File Coverage

blib/lib/Hey/heyPass.pm
Criterion Covered Total %
statement 17 194 8.7
branch 2 54 3.7
condition 1 101 0.9
subroutine 3 13 23.0
pod 11 11 100.0
total 34 373 9.1


line stmt bran cond sub pod time code
1             package Hey::heyPass;
2              
3             our $VERSION = 2.11;
4              
5 1     1   24223 use Storable qw(freeze thaw);
  1         3548  
  1         158  
6              
7             =cut
8              
9             =head1 NAME
10              
11             Hey::heyPass - Client for heyPass Centralized Authentication System
12              
13             =head1 SYNOPSIS
14              
15             use Hey::heyPass;
16              
17             my $hp = Hey::heyPass->new({
18             uuid => '1f0123de58d123ddb4da123851399123',
19             key => 'your-app-password-here',
20             });
21              
22             my $login = $hp->login({
23             return_url => 'http://www.my-super-website-place.com/return.cgi?login_code=%s', # %s replaced with login_code
24             attributes => { # attributes that we want permission to access (heyPass will ask the user for permission)
25             '7990a9de584511dda17e00185139906f/birthdate' => {
26             permission => 'ro', # asking for read-only access to this attribute (user can override this)
27             expires => '3600', # asking for a grant to this attribute that expires in 1 hour (user can override this)
28             },
29             '7990a9de584511dda17e00185139906f/email_address' => {
30             permission => 'ro', # asking for read-only access to this attribute (user can override this)
31             expires => 'never', # asking for a never-expiring grant to this attribute (user can override this)
32             },
33             },
34             });
35             $my_session_blah->{login_code} = $login->{code}; # if you want to put the login_code in your session
36             # system instead of having it in the return_url
37             # (you're in charge of your own session system...)
38             print "Status: 302\nLocation: $login->{url}\n\n";
39              
40             ... user is at heyPass logging in
41              
42             now the user returns ...
43              
44             my $user = $hp->user({
45             login_code => $login_code, # get this from where you stored it, either from the return_url (GET param)
46             # or from your session system.. or whatever.
47             });
48             print "User's UUID is $user." if $user;
49             die "User isn't logged in... login_code is invalid, expired, or whatever." unless $user;
50              
51             my $attributes = $hp->read({
52             user => $user, # $user comes from the output of the 'user' command above.
53             attributes => [
54             '1f0123de58d123ddb4da123851399123/username', # format: 'app_uuid/attribute'
55             '1f0123de58d123ddb4da123851399123/postcount',
56             '7990a9de584511dda17e00185139906f/birthdate', # your app must either own the variable (your app's uuid)
57             '7990a9de584511dda17e00185139906f/email_address', # or have been given permission by the user (via grants)
58             ],
59             });
60              
61             $hp->write({
62             user => $user,
63             attributes => {
64             '1f0123de58d123ddb4da123851399123/username' => 'fred_jones',
65             '1f0123de58d123ddb4da123851399123/postcount' => 123456,
66             },
67             });
68              
69             =head1 DESCRIPTION
70              
71             heyPass is a centralized authentication system made for any web application that
72             needs user authentication. The heyPass system is hosted, maintained, and
73             managed by hey.nu Network Community Services and Megagram Managed Technical
74             Services in a joint venture to help web application developers provide a safe,
75             user privacy-controlled authentication and profile data sharing system.
76              
77             All data stored within heyPass by individual applications can be shared with
78             other heyPass-powered applications based on the user's permission and the
79             application's permission settings. This system makes it easy for an application
80             to store both public and private data for a user and share only permitted data
81             with other applications that have been given permission by the user.
82              
83             Through this system, no heyPass-powered applications have access to the user's
84             login information, email address, or any other identifying data. Upon
85             successful login, the only piece of data that an application is given is a user
86             UUID that identifies that user with that application. Only after the
87             application requests specific profile data and the user has given explicit
88             permission via a web interface will an application be able to access additional
89             data stored within heyPass. This system ensure that a user's privacy is
90             maintained at the levels that they prefer. For example, a user is not required
91             to provide an application their email address or identity information just to
92             login.
93              
94             This system was originally designed as a central authentication system for
95             hey.nu Network Official sites, but it is now being made available to the general
96             public. Any application developer or application owner may use the heyPass
97             system for their applications. Any heyPass user can create their own
98             applications and any application developer can distribute their heyPass-powered
99             applications to others.
100              
101             =head1 INTERFACE
102              
103             =head2 new
104              
105             my $hp = Hey::heyPass->new({
106             uuid => '1f0123de58d123ddb4da123851399123',
107             key => 'your-app-password-here',
108             });
109              
110             Creates the heyPass object to be used later on for requests.
111              
112             =over 4
113              
114             =item uuid [required]
115              
116             Your application's UUID. This is assigned upon creation of your application at
117             the heyPass website. Your UUID is listed on the application detail screen.
118             This is not a secret. In fact, you can freely give this out if you'd like other
119             applications to potentially access your data (if *both* you and your users allow
120             it).
121              
122             =item key [required]
123              
124             Your application's password. This is a secret! Anyone with this password can
125             do anything with your data that your application can. You get to choose this
126             value when you setup your new application in heyPass. It's best to keep this
127             password long and complicated. You can change it at any time at the heyPass
128             website.
129              
130             =item return_url [optional]
131              
132             This is the URL that heyPass will send the user to when the user has either
133             successfully logged with heyPass or if the user hit the cancel button. If you
134             set it here, it becomes the default return_url for all requests. Otherwise, it
135             can be set for any request that sends the user to heyPass.
136              
137             =item access_url [optional]
138              
139             It shouldn't ever be necessary to use this, but if you are in a special
140             situation where the predefined heyPass API URL needs to be set to a different
141             value, you'd do that with this.
142              
143             =back
144              
145             =cut
146              
147             sub new
148             {
149 1     1 1 15 my $class = shift;
150 1         2 my $args = shift; # hashref
151 1         3 my $self = {};
152              
153 1 50       6 $self->{uuid} = $args->{uuid} or die(qq(Hey::heyPass requires UUID.\n));
154 1 50       5 $self->{key} = $args->{key} or die(qq(Hey::heyPass requires KEY.\n));
155 1         3 $self->{return_url} = $args->{return_url};
156 1   50     10 $self->{access_url} = $args->{access_url} || 'https://heypass.megagram.com/api/';
157              
158 1     1   3145 use LWP::UserAgent;
  1         66099  
  1         2706  
159 1         11 $self->{_ua} = LWP::UserAgent->new();
160 1         5064 $self->{_ua}->agent('Hey::heyPass/'.$VERSION.' ('.$self->{uuid}.'; Perl '.join('.', map({ord} split('', $^V))).'; https://heypass.megagram.com/)');
  7         23  
161              
162 1         96 return bless($self, $class);
163             }
164              
165             =cut
166              
167             =head2 login
168              
169             my $login = $hp->login({
170             return_url => 'http://www.my-super-website-place.com/return.cgi?login_code=%s', # %s replaced with login_code
171             attributes => { # attributes that we want permission to access (heyPass will ask the user for permission)
172             '7990a9de584511dda17e00185139906f/birthdate' => {
173             permission => 'ro', # asking for read-only access to this attribute (user can override this)
174             expires => '3600', # asking for a grant to this attribute that expires in 1 hour (user can override this)
175             },
176             '7990a9de584511dda17e00185139906f/email_address' => {
177             permission => 'ro', # asking for read-only access to this attribute (user can override this)
178             expires => 'never', # asking for a never-expiring grant to this attribute (user can override this)
179             },
180             },
181             });
182             $my_session_blah->{login_code} = $login->{code}; # if you want to put the login_code in your session
183             # system instead of having it in the return_url
184             # (you're in charge of your own session system...)
185             print "Status: 302\nLocation: $login->{url}\n\n";
186              
187             Sends a request to heyPass to start the login process for a non-authenticated
188             user. Returns the URL where you will send the user to login as well as the
189             login_code that will be used later to check to see if the user is logged in, to
190             get the logged-in user's UUID, and other things.
191              
192             WARNING! Do *not* use login_code as a session id for your application. At any
193             moment the login_code may need to change (the user logged out and you want them
194             to log back in, etc) and this will mess up your sessions. You should use your
195             own session management system like L or something like
196             that. Don't rely on heyPass to maintain your session ids! That's not what
197             login_code is for.
198              
199             =over 4
200              
201             =item return_url [optional-ish]
202              
203             If you didn't set this when you created your $hp object, you are required to do
204             it here. If you did set it, this will override it for just this one request.
205              
206             =item attributes [optional]
207              
208             A hashref containing the attributes that you'd like to have access to. The
209             attribute is formatted "APP_UUID/ATTRIBUTE". You need to know the UUID of the
210             application that owns the attribute and the name of the attribute that you want
211             access to.
212              
213             When the user logs in with heyPass, they will be prompted to grant access to
214             these attributes to your application. The user can override any of the settings
215             that you provided (permission, expiration). Depending on how the user answers,
216             they may be asked everytime they login, once it expires, or never again.
217              
218             The attributes that you list here must have been permitted to be shared by the
219             application that owns the data. This means they have set either "read-only" or
220             "read-write" access to the data.
221              
222             =back
223              
224             =cut
225              
226             sub login
227             {
228 0     0 1   my $self = shift;
229 0           my $args = shift; # hashref
230 0           my $request = {};
231              
232 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
233 0   0       $request->{key} = $args->{key} || $self->{key};
234 0   0       $request->{return_url} = $args->{return_url} || $self->{return_url};
235 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
236 0           $request->{attributes} = $args->{attributes};
237              
238 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'login');
239 0           $req->content_type('application/x-storable');
240 0           $req->content(freeze($request));
241 0           my $res = $self->{_ua}->request($req);
242              
243 0 0         if ($res->is_success)
244             {
245 0           my $response = thaw($res->content);
246 0           return { url => $response->{login_url}, code => $response->{applogin_id} };
247             }
248              
249 0 0         if ($res->code == 403)
250             {
251 0           die("Application authentication failed.\n");
252             }
253              
254 0           return undef;
255             }
256              
257             =cut
258              
259             =head2 logout
260              
261             # "local" logout, only logs out this one login_code
262             $hp->logout({ # logout based on the login_code. doesn't return anything at all either on success or failure.
263             login_code => $login_code,
264             });
265              
266             # "global" logout, logs out every user session on every computer for this application
267             $hp->logout({ # logout based on the user's uuid. doesn't return anything at all either on success or failure.
268             user => $user,
269             });
270              
271             Performs a logout action. Either logout just this single login_code (leaving all other sessions alone)
272             or logout every session for this user for your app. You'll use either "login_code" or "user", but
273             not both at the same time.
274              
275             =over 4
276              
277             =item login_code [required, without user]
278              
279             If provided, the provided login_code will be logged out. The user will be
280             logged out of your application for the computer that the user is sitting at.
281             Any existing sessions for this user for your application at other computers will
282             not be logged out.
283              
284             =item user [required, without login_code]
285              
286             This is the user's UUID. If provided, the user will be logged out of your
287             application at every computer that was used to log into your application. This
288             is considered a "global application logout".
289              
290             FYI: If a user wants to do a true global logout (logout of all heyPass-powered
291             apps for this user), they must do that through the heyPass website. Your
292             application can direct them there, but the user has to click the button to make
293             it happen.
294              
295             =back
296              
297             =cut
298              
299             sub logout
300             {
301 0     0 1   my $self = shift;
302 0           my $args = shift; # hashref
303 0           my $request = {};
304              
305 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
306 0   0       $request->{key} = $args->{key} || $self->{key};
307 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
308 0           $request->{user} = $args->{user};
309 0           $request->{login_code} = $args->{login_code};
310 0 0 0       return undef unless ($request->{user} || $request->{login_code}); # must use one of them
311              
312 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'logout');
313 0           $req->content_type('application/x-storable');
314 0           $req->content(freeze($request));
315 0           my $res = $self->{_ua}->request($req);
316              
317 0 0         if ($res->is_success)
318             {
319 0           my $response = thaw($res->content);
320 0           return undef;
321             }
322              
323 0 0         if ($res->code == 403)
324             {
325 0           die("Application authentication failed.\n");
326             }
327              
328 0           return undef;
329             }
330              
331             =cut
332              
333             =head2 user
334              
335             my $user = $hp->user({
336             login_code => $login_code, # get this from where you stored it, either from the return_url (GET param)
337             # or from your session system.. or whatever.
338             });
339             print "User's UUID is $user." if $user;
340             die "User isn't logged in... login_code is invalid, expired, or whatever." unless $user;
341              
342             Get the logged in user's UUID. This is the static identifier that lets your
343             application know that this user is who they are. This value never changes for
344             each individual user. You will use this value in your databases or wherever to
345             match against each time this user logs in or wishes to perform an action.
346             According to your application, the user's UUID is their identity. You will use
347             the user's UUID to perform other tasks, like writing to their attributes,
348             logout, requesting attribute grants, and reading attributes.
349              
350             If $user is null, the login_code is not valid. This means the user is not
351             authenticated and should be treated as such. Either the login_code was never
352             authenticated, the authentication expired, or the user was logged out.
353              
354             =over 4
355              
356             =item login_code [required]
357              
358             Provide the login_code that you originally got from login.
359              
360             =back
361              
362             =cut
363              
364             sub user
365             {
366 0     0 1   my $self = shift;
367 0           my $args = shift; # hashref
368 0           my $request = {};
369              
370 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
371 0   0       $request->{key} = $args->{key} || $self->{key};
372 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
373 0 0         $request->{login_code} = $args->{login_code} or return undef;
374              
375 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'user');
376 0           $req->content_type('application/x-storable');
377 0           $req->content(freeze($request));
378 0           my $res = $self->{_ua}->request($req);
379              
380 0 0         if ($res->is_success)
381             {
382 0           my $response = thaw($res->content);
383 0           return $response->{user};
384             }
385              
386 0 0         if ($res->code == 403)
387             {
388 0           die("Application authentication failed.\n");
389             }
390              
391 0           return undef;
392             }
393              
394             =cut
395              
396             =head2 read
397              
398             my $attributes = $hp->read({
399             user => $user, # $user comes from the output of the 'user' command above.
400             attributes => [
401             '1f0123de58d123ddb4da123851399123/username', # format: 'app_uuid/attribute'
402             '1f0123de58d123ddb4da123851399123/postcount',
403             '7990a9de584511dda17e00185139906f/birthdate', # your app must either own the variable (your app's uuid)
404             '7990a9de584511dda17e00185139906f/email_address', # or have been given permission by the user (via grants)
405             ],
406             });
407              
408             Get the specified attributes from the user's profile. For attributes in your
409             application's namespace (starting with your app's UUID), you don't need
410             permission from the user to get the data. For any other namespace, you must
411             have a valid, non-expired grant from the user. You get a grant using "grant" or
412             during the initial "login". If you don't have a valid grant, your request for
413             the denied attributes will be ignored and omitted from the response. If the
414             attribute or application UUID doesn't exist, that too will be ignored and
415             omitted. If you have a valid grant for a piece of data, but the data doesn't
416             exist for this user (it was never written to), it'll be omitted from the
417             response.
418              
419             =over 4
420              
421             =item user [required]
422              
423             The user's UUID that you'd like to get the information from. For any data that
424             you have a valid grant for, the user doesn't need to be present to request the
425             data.
426              
427             =item attributes [required]
428              
429             An array reference containing a list of attributes to get from the user's
430             heyPass profile.
431              
432             =back
433              
434             =cut
435              
436             sub read
437             {
438 0     0 1   my $self = shift;
439 0           my $args = shift; # hashref
440 0           my $request = {};
441              
442 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
443 0   0       $request->{key} = $args->{key} || $self->{key};
444 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
445 0 0         $request->{user} = $args->{user} or return undef;
446 0           $request->{attributes} = $args->{attributes};
447              
448 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'read');
449 0           $req->content_type('application/x-storable');
450 0           $req->content(freeze($request));
451 0           my $res = $self->{_ua}->request($req);
452              
453 0 0         if ($res->is_success)
454             {
455 0           my $response = thaw($res->content);
456 0           return $response;
457             }
458              
459 0 0         if ($res->code == 403)
460             {
461 0           die("Application authentication failed.\n");
462             }
463              
464 0           return undef;
465             }
466              
467             =cut
468              
469             =head2 write
470              
471             $hp->write({
472             user => $user,
473             attributes => {
474             '1f0123de58d123ddb4da123851399123/username' => 'fred_jones', # our attributes, we don't need to get a grant for these
475             '1f0123de58d123ddb4da123851399123/postcount' => 123456,
476             '123aa31e58d1dc7de982375238526727/status_text' => 'Ready to Chat', # only if you have been granted "rw" access to this by the user and the owning app
477             },
478             });
479              
480             Write to the user's profile attributes. If your application owns the
481             attributes, you don't need a grant. Otherwise, you need a read-write grant to
482             the attributes. Any attributes that you don't have read-write access to will be
483             ignored.
484              
485             =over 4
486              
487             =item user [required]
488              
489             The user's UUID that you'd like to write the information to. For any data that
490             you have a valid read-write grant for, the user doesn't need to be present to
491             write the data.
492              
493             =item attributes [required]
494              
495             A hash reference containing the attributes to be written and their values.
496              
497             =back
498              
499             =cut
500              
501             sub write
502             {
503 0     0 1   my $self = shift;
504 0           my $args = shift; # hashref
505 0           my $request = {};
506              
507 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
508 0   0       $request->{key} = $args->{key} || $self->{key};
509 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
510 0 0         $request->{user} = $args->{user} or return undef;
511 0           $request->{attributes} = $args->{attributes};
512              
513 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'write');
514 0           $req->content_type('application/x-storable');
515 0           $req->content(freeze($request));
516 0           my $res = $self->{_ua}->request($req);
517              
518 0 0         if ($res->is_success)
519             {
520 0           my $response = thaw($res->content);
521 0           return $response;
522             }
523              
524 0 0         if ($res->code == 403)
525             {
526 0           die("Application authentication failed.\n");
527             }
528              
529 0           return undef;
530             }
531              
532             =cut
533              
534             =head2 grant
535              
536             # ask for new grants, this method is used when the user is present and the login_code is valid
537             my $grant = $hp->grant({
538             login_code => $login_code,
539             attributes => {
540             '7990a9de584511dda17e00185139906f/email_address' => {
541             permission => 'ro',
542             expires => 'never', # never expire
543             },
544             '7990a9de584511dda17e00185139906f/birthdate' => {
545             permission => 'ro',
546             expires => '3600', # expire in 3600 seconds (1 hour)
547             },
548             },
549             });
550             print "Status: 302\nLocation: $grant->{url}\n\n" if $grant->{url}; # sends the user to heyPass to grant permission
551              
552             # ask for new grants, this method is used when the user is NOT present and/or the login_code is invalid
553             my $grant = $hp->grant({
554             user => $user,
555             attributes => {
556             '7990a9de584511dda17e00185139906f/email_address' => {
557             permission => 'ro',
558             expires => 'never', # never expire
559             },
560             '7990a9de584511dda17e00185139906f/birthdate' => {
561             permission => 'ro',
562             expires => '3600', # expire in 3600 seconds (1 hour)
563             },
564             },
565             });
566             # Doesn't provide a URL. Instead, the user will be prompted at next login.
567              
568             Request a grant for attributes for a user. The application that owns the
569             attributes being requested must have specified that the attributes are either
570             read-only or read-write. The user will need to visit the heyPass site to grant
571             this permission. If you use "login_code", a URL will be provided that you can
572             send the user to. Otherwise, the user will be prompted next time they login.
573              
574             =over 4
575              
576             =item return_url [optional-ish]
577              
578             If you didn't set this when you created your $hp object, you are required to do
579             it here. If you did set it, this will override it for just this one request.
580              
581             =item login_code [required, without user]
582              
583             Provide the login_code for the current user session. Returned will be the URL
584             that the user must visit to grant the requested permissions. The user must
585             be present to use "login_code" for this.
586              
587             =item user [required, without login_code]
588              
589             Provide the user's UUID instead of login_code to request permission to
590             attributes. In doing so, the user doesn't need to be present when you make this
591             request. The next time the user logs into your application, they'll be asked to
592             give permission. You can use this method for user-not-present batch requests.
593              
594             =item attributes [required]
595              
596             A hashref containing the attributes that you'd like to have access to. The
597             attribute is formatted "APP_UUID/ATTRIBUTE". You need to know the UUID of the
598             application that owns the attribute and the name of the attribute that you want
599             access to.
600              
601             When the user logs in with heyPass or is sent to heyPass with the returned URL,
602             they will be prompted to grant access to these attributes to your application.
603             The user can override any of the settings that you provided (permission,
604             expiration). Depending on how the user answers, they may be asked everytime
605             they login, once it expires, or never again.
606              
607             The attributes that you list here must have been permitted to be shared by the
608             application that owns the data. This means they have set either "read-only" or
609             "read-write" access to the data.
610              
611             =back
612              
613             =cut
614              
615             sub grant
616             {
617 0     0 1   my $self = shift;
618 0           my $args = shift; # hashref
619 0           my $request = {};
620              
621 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
622 0   0       $request->{key} = $args->{key} || $self->{key};
623 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
624 0           $request->{attributes} = $args->{attributes};
625 0           $request->{user} = $args->{user};
626 0           $request->{login_code} = $args->{login_code};
627 0 0 0       return undef unless ($request->{user} || $request->{login_code}); # must use one of them
628              
629 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'grant');
630 0           $req->content_type('application/x-storable');
631 0           $req->content(freeze($request));
632 0           my $res = $self->{_ua}->request($req);
633              
634 0 0         if ($res->is_success)
635             {
636 0           my $response = thaw($res->content);
637 0           return { url => $response->{grant_url} };
638             }
639              
640 0 0         if ($res->code == 403)
641             {
642 0           die("Application authentication failed.\n");
643             }
644              
645 0           return undef;
646             }
647              
648             =cut
649              
650             =head2 attrlist
651              
652             my $attributes = $hp->attrlist(); # no arguments
653             use Data::Dumper;
654             print Dumper($attributes);
655              
656             Returns a list of attributes that your application has defined in heyPass.
657              
658             =cut
659              
660             sub attrlist
661             {
662 0     0 1   my $self = shift;
663 0           my $args = shift; # hashref
664 0           my $request = {};
665              
666 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
667 0   0       $request->{key} = $args->{key} || $self->{key};
668 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
669              
670 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'attrlist');
671 0           $req->content_type('application/x-storable');
672 0           $req->content(freeze($request));
673 0           my $res = $self->{_ua}->request($req);
674              
675 0 0         if ($res->is_success)
676             {
677 0           my $response = thaw($res->content);
678 0           return $response;
679             }
680              
681 0 0         if ($res->code == 403)
682             {
683 0           die("Application authentication failed.\n");
684             }
685              
686 0           return undef;
687             }
688              
689             =cut
690              
691             =head2 attrcreate
692              
693             $hp->attrcreate({ # create attributes, doesn't return anything
694             attributes => { # this is what we create. errors are simply ignored.
695             'testcreate_1' => {
696             permission => 'rw',
697             title => 'Test Created Field #1',
698             description => 'This field was created programmatically via the API.',
699             },
700             'testcreate_2' => {
701             permission => 'rw',
702             title => 'Test Created Field #2',
703             description => 'This field was created programmatically via the API.',
704             },
705             'testcreate_3' => {
706             permission => 'rw',
707             title => 'Test Created Field #3',
708             description => 'This field was created programmatically via the API.',
709             },
710             },
711             });
712              
713             Programmatically create new attributes within heyPass in your application's
714             namespace. This is akin to adding a column in a database table.
715              
716             =over 4
717              
718             =item attributes [required]
719              
720             A hash reference containing the attribute, title, description, and permission
721             for the attributes that you'd like to create. If the attribute already exists,
722             it'll be ignored from the request.
723              
724             =back
725              
726             =cut
727              
728             sub attrcreate
729             {
730 0     0 1   my $self = shift;
731 0           my $args = shift; # hashref
732 0           my $request = {};
733              
734 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
735 0   0       $request->{key} = $args->{key} || $self->{key};
736 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
737 0           $request->{attributes} = $args->{attributes};
738              
739 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'attrcreate');
740 0           $req->content_type('application/x-storable');
741 0           $req->content(freeze($request));
742 0           my $res = $self->{_ua}->request($req);
743              
744 0 0         if ($res->is_success)
745             {
746 0           my $response = thaw($res->content);
747 0           return $response;
748             }
749              
750 0 0         if ($res->code == 403)
751             {
752 0           die("Application authentication failed.\n");
753             }
754              
755 0           return undef;
756             }
757              
758             =cut
759              
760             =head2 attrupdate
761              
762             $hp->attrupdate({ # update attributes, doesn't return anything
763             attributes => { # this is what we update. errors are simply ignored. omitted fields will be left alone.
764             'testcreate_1' => {
765             title => 'Test Updated Field #1',
766             description => 'This field was updated programmatically via the API.',
767             },
768             'testcreate_2' => {
769             permission => 'ro', # only changing permission
770             },
771             'testcreate_3' => {
772             title => 'Test Updated Field #3',
773             description => 'This field was updated programmatically via the API.',
774             },
775             },
776             });
777              
778             Alter an existing attribute for your application within heyPass. This is used
779             to make changes to the permission, title, and description of the attribute.
780             This doesn't modify the value of the attribute; it modifies the definition of
781             the attribute. If you want to modify values, you would use "write" instead.
782              
783             =over 4
784              
785             =item attributes [required]
786              
787             A hash reference containing the attribute, title, description, and permission
788             for the attributes that you'd like to update. If the attribute doesn't exist,
789             it'll be ignored from the request. Any fields you omit from this hash will
790             be left untouched. For example, in the code sample above, the title for
791             "testcreate_2" will not be modified since it wasn't supplied in the hash.
792              
793             =back
794              
795             =cut
796              
797             sub attrupdate
798             {
799 0     0 1   my $self = shift;
800 0           my $args = shift; # hashref
801 0           my $request = {};
802              
803 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
804 0   0       $request->{key} = $args->{key} || $self->{key};
805 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
806 0           $request->{attributes} = $args->{attributes};
807              
808 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'attrupdate');
809 0           $req->content_type('application/x-storable');
810 0           $req->content(freeze($request));
811 0           my $res = $self->{_ua}->request($req);
812              
813 0 0         if ($res->is_success)
814             {
815 0           my $response = thaw($res->content);
816 0           return $response;
817             }
818              
819 0 0         if ($res->code == 403)
820             {
821 0           die("Application authentication failed.\n");
822             }
823              
824 0           return undef;
825             }
826              
827             =cut
828              
829             =head2 attrdelete
830              
831             $hp->attrdelete({ # delete attributes, doesn't return anything
832             attributes => [ # this is what we delete. errors are simply ignored.
833             'testcreate_1',
834             'testcreate_2',
835             'testcreate_3',
836             ],
837             });
838              
839             Delete an attribute for your application from heyPass. This deletes the
840             definition of an attribute. This means that it will remove the attribute from
841             every heyPass user profile and from your application's data store on heyPass.
842             This is akin to removing a column from a database table.
843              
844             If you just want to delete a value from an attribute for a single user, use
845             "write" to store a blank value. In the future, we may implement a "delete"
846             API function.
847              
848             WARNING! If you do this, all data stored in that attribute for every heyPass
849             user will be permanently deleted. There is no going back! There is no undo!
850             Make sure you really mean it.
851              
852             =over 4
853              
854             =item attributes [required]
855              
856             An array reference containing a list of attributes to delete. Any attributes
857             that don't exist will be ignored from the request.
858              
859             =back
860              
861             =cut
862              
863             sub attrdelete
864             {
865 0     0 1   my $self = shift;
866 0           my $args = shift; # hashref
867 0           my $request = {};
868              
869 0   0       $request->{uuid} = $args->{uuid} || $self->{uuid};
870 0   0       $request->{key} = $args->{key} || $self->{key};
871 0   0       $request->{access_url} = $args->{access_url} || $self->{access_url};
872 0           $request->{attributes} = $args->{attributes};
873              
874 0           my $req = HTTP::Request->new(POST => $request->{access_url}.'attrdelete');
875 0           $req->content_type('application/x-storable');
876 0           $req->content(freeze($request));
877 0           my $res = $self->{_ua}->request($req);
878              
879 0 0         if ($res->is_success)
880             {
881 0           my $response = thaw($res->content);
882 0           return $response;
883             }
884              
885 0 0         if ($res->code == 403)
886             {
887 0           die("Application authentication failed.\n");
888             }
889              
890 0           return undef;
891             }
892              
893             =cut
894              
895             =head1 TODO
896              
897             =over 4
898              
899             =item delete
900              
901             Add a delete API function to delete an attribute from an individual heyPass
902             user's profile. This isn't the same as "attrdelete" which deletes the attribute
903             from the entire application.
904              
905             =back
906              
907             =head1 THANKS
908              
909             Thanks to Andrew Orner for being our very first guinea pig. He implemented
910             heyPass 1 in his pureBB bulletin board system. Now renamed heyBoard, he is
911             implementing heyPass 2 to take advantage of the enhanced profile storage
912             features. heyPass wouldn't be where it is today without his support.
913              
914             Thanks to Aditya Gaddam for writing the PHP and Ruby versions of this module.
915             His work will help ensure PHP and Ruby developers get to use heyPass for their
916             own applications. We all appreciate your effort.
917              
918             Thanks to the hey.nu Network community for putting up with heyPass 1 while
919             heyPass 2 was in development. Thanks for testing it out, putting up with the
920             bugs, and giving your valuable feedback.
921              
922             =head1 AUTHOR
923              
924             Dusty Wilson
925             Megagram Managed Technical Services
926             hey.nu Network Community Services
927             http://heypass.megagram.com/
928              
929             =head1 COPYRIGHT
930              
931             This program is free software; you can redistribute
932             it and/or modify it under the same terms as Perl itself.
933              
934             The full text of the license can be found in the
935             LICENSE file included with this module.
936              
937             =cut
938              
939             1;