From 878e311c99edb68dd9f3fa597ee99ab9cd298af1 Mon Sep 17 00:00:00 2001 From: plugin-master Date: Thu, 21 Mar 2013 04:47:33 +0000 Subject: [PATCH 01/18] adding oauth2-provider by jgwpk git-svn-id: http://plugins.svn.wordpress.org/oauth2-provider/trunk@685065 b8457f37-d9ea-0310-8a92-e5e31aec5664 From e8774acfe9c445d1fcc36a6f36020d9b25f0c97c Mon Sep 17 00:00:00 2001 From: jgwpk Date: Thu, 21 Mar 2013 12:55:33 +0000 Subject: [PATCH 02/18] INIT Upload git-svn-id: http://plugins.svn.wordpress.org/oauth2-provider/trunk@685195 b8457f37-d9ea-0310-8a92-e5e31aec5664 --- lib/assets/css/ie.css | 43 + lib/assets/css/layout.css | 893 ++++++++++++++++ lib/assets/images/breadcrumb_divider.png | Bin 0 -> 210 bytes lib/assets/images/btn_submit.png | Bin 0 -> 217 bytes lib/assets/images/btn_submit_2.png | Bin 0 -> 214 bytes lib/assets/images/btn_view_site.png | Bin 0 -> 1503 bytes lib/assets/images/header_bg.png | Bin 0 -> 2978 bytes lib/assets/images/header_shadow.png | Bin 0 -> 1137 bytes lib/assets/images/icn_add_user.png | Bin 0 -> 462 bytes lib/assets/images/icn_alert_error.png | Bin 0 -> 386 bytes lib/assets/images/icn_alert_info.png | Bin 0 -> 434 bytes lib/assets/images/icn_alert_success.png | Bin 0 -> 347 bytes lib/assets/images/icn_alert_warning.png | Bin 0 -> 418 bytes lib/assets/images/icn_audio.png | Bin 0 -> 643 bytes lib/assets/images/icn_categories.png | Bin 0 -> 251 bytes lib/assets/images/icn_edit.png | Bin 0 -> 357 bytes lib/assets/images/icn_edit_article.png | Bin 0 -> 467 bytes lib/assets/images/icn_folder.png | Bin 0 -> 309 bytes lib/assets/images/icn_jump_back.png | Bin 0 -> 489 bytes lib/assets/images/icn_logout.png | Bin 0 -> 443 bytes lib/assets/images/icn_new_article.png | Bin 0 -> 290 bytes lib/assets/images/icn_photo.png | Bin 0 -> 336 bytes lib/assets/images/icn_profile.png | Bin 0 -> 485 bytes lib/assets/images/icn_search.png | Bin 0 -> 429 bytes lib/assets/images/icn_security.png | Bin 0 -> 465 bytes lib/assets/images/icn_settings.png | Bin 0 -> 272 bytes lib/assets/images/icn_tags.png | Bin 0 -> 292 bytes lib/assets/images/icn_trash.png | Bin 0 -> 284 bytes lib/assets/images/icn_user.png | Bin 0 -> 489 bytes lib/assets/images/icn_video.png | Bin 0 -> 311 bytes lib/assets/images/icn_view_users.png | Bin 0 -> 528 bytes lib/assets/images/module_footer_bg.png | Bin 0 -> 233 bytes lib/assets/images/post_message.png | Bin 0 -> 1479 bytes lib/assets/images/secondary_bar.png | Bin 0 -> 263 bytes lib/assets/images/secondary_bar_shadow.png | Bin 0 -> 498 bytes lib/assets/images/sidebar.png | Bin 0 -> 1941 bytes lib/assets/images/sidebar_divider.png | Bin 0 -> 203 bytes lib/assets/images/sidebar_shadow.png | Bin 0 -> 204 bytes lib/assets/images/table_sorter_header.png | Bin 0 -> 239 bytes lib/assets/js/hideshow.js | 39 + lib/assets/js/jquery.equalHeight.js | 20 + lib/assets/js/jquery.tablesorter.min.js | 4 + lib/classes/IOAuth2GrantClient.php | 37 + lib/classes/IOAuth2GrantCode.php | 72 ++ lib/classes/IOAuth2GrantExtension.php | 35 + lib/classes/IOAuth2GrantImplicit.php | 20 + lib/classes/IOAuth2GrantUser.php | 46 + lib/classes/IOAuth2RefreshTokens.php | 77 ++ lib/classes/IOAuth2Storage.php | 103 ++ lib/classes/OAuth2.php | 1011 +++++++++++++++++++ lib/classes/OAuth2AuthenticateException.php | 55 + lib/classes/OAuth2Client.php | 692 +++++++++++++ lib/classes/OAuth2Exception.php | 83 ++ lib/classes/OAuth2RedirectException.php | 80 ++ lib/classes/OAuth2ServerException.php | 82 ++ lib/classes/OAuth2_API.php | 451 +++++++++ lib/classes/admin/IOAuth2Storage.php | 250 +++++ lib/classes/admin/OAuthMain.php | 47 + lib/classes/log.txt | 20 + lib/dashbaord.php | 127 +++ readme.txt | 100 ++ wp_oauth2-complete.php | 279 +++++ 62 files changed, 4666 insertions(+) create mode 100644 lib/assets/css/ie.css create mode 100644 lib/assets/css/layout.css create mode 100644 lib/assets/images/breadcrumb_divider.png create mode 100644 lib/assets/images/btn_submit.png create mode 100644 lib/assets/images/btn_submit_2.png create mode 100644 lib/assets/images/btn_view_site.png create mode 100644 lib/assets/images/header_bg.png create mode 100644 lib/assets/images/header_shadow.png create mode 100644 lib/assets/images/icn_add_user.png create mode 100644 lib/assets/images/icn_alert_error.png create mode 100644 lib/assets/images/icn_alert_info.png create mode 100644 lib/assets/images/icn_alert_success.png create mode 100644 lib/assets/images/icn_alert_warning.png create mode 100644 lib/assets/images/icn_audio.png create mode 100644 lib/assets/images/icn_categories.png create mode 100644 lib/assets/images/icn_edit.png create mode 100644 lib/assets/images/icn_edit_article.png create mode 100644 lib/assets/images/icn_folder.png create mode 100644 lib/assets/images/icn_jump_back.png create mode 100644 lib/assets/images/icn_logout.png create mode 100644 lib/assets/images/icn_new_article.png create mode 100644 lib/assets/images/icn_photo.png create mode 100644 lib/assets/images/icn_profile.png create mode 100644 lib/assets/images/icn_search.png create mode 100644 lib/assets/images/icn_security.png create mode 100644 lib/assets/images/icn_settings.png create mode 100644 lib/assets/images/icn_tags.png create mode 100644 lib/assets/images/icn_trash.png create mode 100644 lib/assets/images/icn_user.png create mode 100644 lib/assets/images/icn_video.png create mode 100644 lib/assets/images/icn_view_users.png create mode 100644 lib/assets/images/module_footer_bg.png create mode 100644 lib/assets/images/post_message.png create mode 100644 lib/assets/images/secondary_bar.png create mode 100644 lib/assets/images/secondary_bar_shadow.png create mode 100644 lib/assets/images/sidebar.png create mode 100644 lib/assets/images/sidebar_divider.png create mode 100644 lib/assets/images/sidebar_shadow.png create mode 100644 lib/assets/images/table_sorter_header.png create mode 100644 lib/assets/js/hideshow.js create mode 100644 lib/assets/js/jquery.equalHeight.js create mode 100644 lib/assets/js/jquery.tablesorter.min.js create mode 100644 lib/classes/IOAuth2GrantClient.php create mode 100644 lib/classes/IOAuth2GrantCode.php create mode 100644 lib/classes/IOAuth2GrantExtension.php create mode 100644 lib/classes/IOAuth2GrantImplicit.php create mode 100644 lib/classes/IOAuth2GrantUser.php create mode 100644 lib/classes/IOAuth2RefreshTokens.php create mode 100644 lib/classes/IOAuth2Storage.php create mode 100644 lib/classes/OAuth2.php create mode 100644 lib/classes/OAuth2AuthenticateException.php create mode 100644 lib/classes/OAuth2Client.php create mode 100644 lib/classes/OAuth2Exception.php create mode 100644 lib/classes/OAuth2RedirectException.php create mode 100644 lib/classes/OAuth2ServerException.php create mode 100644 lib/classes/OAuth2_API.php create mode 100644 lib/classes/admin/IOAuth2Storage.php create mode 100644 lib/classes/admin/OAuthMain.php create mode 100644 lib/classes/log.txt create mode 100644 lib/dashbaord.php create mode 100644 readme.txt create mode 100644 wp_oauth2-complete.php diff --git a/lib/assets/css/ie.css b/lib/assets/css/ie.css new file mode 100644 index 0000000..5c88cb9 --- /dev/null +++ b/lib/assets/css/ie.css @@ -0,0 +1,43 @@ +.quick_search { +text-align: center; +padding: 14px 0 0px 0; +} + +.quick_search input[type=text] { +text-align: left; +height: 22px; +width: 88%; +color: #ccc; +padding-left: 2%; +padding-top: 5px; +background: #fff url(../images/icn_search.png) no-repeat; +background-position: 10px 6px; +} + +.toggleLink { +display: inline; +float: none; +margin-left: 2% +} + +html ul.tabs li.active, html ul.tabs li.active a:hover { +background: #ccc; +} + +input[type=submit].btn_post_message { +background: url(../images/post_message.png) no-repeat; +} + +fieldset input[type=text] { +margin-left: -10px; +} + + +fieldset select { +margin-left: -10px +} + +fieldset textarea { +margin-left: -10px; +} + diff --git a/lib/assets/css/layout.css b/lib/assets/css/layout.css new file mode 100644 index 0000000..f4b0780 --- /dev/null +++ b/lib/assets/css/layout.css @@ -0,0 +1,893 @@ +/* Essentials */ + +html, div, map, dt, isindex, form, header, aside, section, section, article, footer { + display: block; +} + +html, body { +height: 100%; +margin: 0; +padding: 0; +font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; +background: #F8F8F8; +font-size: 12px; +} + +.clear { +clear: both; +} + +.spacer { +height: 20px; +} + +a:link, a:visited { +color: #77BACE; +text-decoration: none; +} + +a:hover { +text-decoration: underline; +} + + +/* Header */ + +header#header { +height: 55px; +width: 100%; +background: #222222 url(../images/header_bg.png) repeat-x; +} + +header#header h1.site_title, header#header h2.section_title { +float: left; +margin: 0; +font-size: 22px; +display: block; +width: 23%; +height: 55px; +font-weight: normal; +text-align: left; +text-indent: 1.8%; +line-height: 55px; +color: #fff; +text-shadow: 0 -1px 0 #000; +} + +header#header h1.site_title a { +color: #fff; +text-decoration: none; +} + +header#header h2.section_title { +text-align: center; +width:100% +} + +.btn_view_site { +float: left; +width: 9%; +} + +.btn_view_site a { +display: block; +margin-top: 12px; +width: 91px; +height: 27px; +background: url(../images/btn_view_site.png) no-repeat; +text-align: center; +line-height: 29px; +color: #fff; +text-decoration: none; +text-shadow: 0 -1px 0 #000;} + +.btn_view_site a:hover { +background-position: 0 -27px; +} + +/* Secondary Header Bar */ + +section#secondary_bar { +height: 38px; +width: 100%; +background: #F1F1F4 url(../images/secondary_bar.png) repeat-x; +} + +section#secondary_bar .user { +float: left; +width: 23%; +height: 38px; +} + +.user p { +margin: 0; +padding: 0; +color: #666666; +font-weight: bold; +display: block; +float: left; +width: 85%; +height: 35px; +line-height: 35px; +text-indent: 25px; +text-shadow: 0 1px 0 #fff; +background: url(../images/icn_user.png) no-repeat center left; +margin-left: 6%; +} + +.user a { +text-decoration: none; +color: #666666} + +.user a:hover { +color: #77BACE; +} + +.user a.logout_user { +float: left; +display: block; +width: 16px; +height: 35px; +text-indent: -5000px; +background: url(../images/icn_logout.png) center no-repeat; +} + +/* Breadcrumbs */ + +section#secondary_bar .breadcrumbs_container { +float: left; +width: 77%; +background: url(../images/secondary_bar_shadow.png) no-repeat left top; +height: 38px; +} + +article.breadcrumbs { +float: left; +padding: 0 10px; +border: 1px solid #ccc; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +-webkit-box-shadow: 0 1px 0 #fff; +-moz-box-shadow: 0 1px 0 #fff; +box-shadow: 0 1px 0 #fff; +height: 23px; +margin: 4px 3%; +} + +.breadcrumbs a { +display: inline-block; +float: left; +height: 24px; +line-height: 23px; +} + +.breadcrumbs a.current, .breadcrumbs a.current:hover { +color: #9E9E9E; +font-weight: bold; +text-shadow: 0 1px 0 #fff; +text-decoration: none; +} + +.breadcrumbs a:link, .breadcrumbs a:visited { +color: #44474F; +text-decoration: none; +text-shadow: 0 1px 0 #fff; +font-weight: bold;} + +.breadcrumbs a:hover { +color: #222222; +} + +.breadcrumb_divider { +display: inline-block; +width: 12px; +height: 24px; +background: url(../images/breadcrumb_divider.png) no-repeat; +float: left; +margin: 0 5px; +} + +/* Sidebar */ + +aside#sidebar { +width: 200px; +float: left; +min-height: 500px; +} + +#sidebar hr { +border: none; +outline: none; +background: url(../images/sidebar_divider.png) repeat-x; +display: block; +width: 100%; +height: 2px;} + + +/* Search */ + +.quick_search { +text-align: center; +padding: 14px 0 10px 0; +} + +.quick_search input[type=text] { +-webkit-border-radius: 20px; +-moz-border-radius: 20px; +border-radius: 20px; +border: 1px solid #bbb; +height: 26px; +width: 90%; +color: #ccc; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +text-indent: 30px; +background: #fff url(../images/icn_search.png) no-repeat; +background-position: 10px 6px; +} + +.quick_search input[type=text]:focus { +outline: none; +color: #666666; +border: 1px solid #77BACE; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +} + +/* Sidebar Menu */ + +#sidebar h3 { +color: #1F1F20; +text-transform: uppercase; +text-shadow: 0 1px 0 #fff; +font-size: 13px; +margin: 10px 0 10px 6%; +display: block; +float: left; +width: 90%; +} + +.toggleLink { +color: #999999; +font-size: 10px; +text-decoration: none; +display: block; +float: right; +margin-right: 2% +} + +#sidebar .toggleLink:hover { +color: #77BACE; +text-decoration: none; +} + +#sidebar ul { +clear: both; +margin: 0; padding: 0; +} + +#sidebar li { +list-style: none; +margin: 0 0 0 12%; padding: 0; +} + +#sidebar li a { +color: #666666; +padding-left: 25px; +text-decoration: none; +display: inline-block; +height: 17px; +line-height: 17px; +text-shadow: 0 1px 0 #fff; +margin: 2px 0; +} + +#sidebar li a:hover { +color: #444444; +} + +/* Sidebar Icons */ + +#sidebar li.icn_new_article a { +background: url(../images/icn_new_article.png) no-repeat center left; +} +#sidebar li.icn_edit_article a { +background: url(../images/icn_edit_article.png) no-repeat center left; +} +#sidebar li.icn_categories a { +background: url(../images/icn_categories.png) no-repeat center left; +} +#sidebar li.icn_tags a { +background: url(../images/icn_tags.png) no-repeat center left; +} +#sidebar li.icn_add_user a { +background: url(../images/icn_add_user.png) no-repeat center left; +} +#sidebar li.icn_view_users a { +background: url(../images/icn_view_users.png) no-repeat center left; +} +#sidebar li.icn_profile a { +background: url(../images/icn_profile.png) no-repeat center left; +} +#sidebar li.icn_folder a { +background: url(../images/icn_folder.png) no-repeat center left; +} +#sidebar li.icn_photo a { +background: url(../images/icn_photo.png) no-repeat center left; +} +#sidebar li.icn_audio a { +background: url(../images/icn_audio.png) no-repeat center left; +} +#sidebar li.icn_video a { +background: url(../images/icn_video.png) no-repeat center left; +} +#sidebar li.icn_settings a { +background: url(../images/icn_settings.png) no-repeat center left; +} +#sidebar li.icn_security a { +background: url(../images/icn_security.png) no-repeat center left; +} +#sidebar li.icn_jump_back a { +background: url(../images/icn_jump_back.png) no-repeat center left; +} + +#sidebar p { +color: #666666; +padding-left: 6%; +text-shadow: 0 1px 0 #fff; +margin: 10px 0 0 0;} + +#sidebar a { +color: #666666; +text-decoration: none; +} + +#sidebar a:hover { +text-decoration: underline; +} + +#sidebar footer { +margin-top: 20%; +} + + +/* Main Content */ + + +section#main { +width: 77%; +min-height: 500px; +background: url(../images/sidebar_shadow.png) repeat-y left top; +float: left; +margin-top: -2px; +} + +#main h3 { +color: #1F1F20; +text-transform: uppercase; +text-shadow: 0 1px 0 #fff; +font-size: 13px; +margin: 8px 20px; +} + +/* Modules */ + +.module { +border: 1px solid #9BA0AF; +width: 100%; +margin: 20px 3% 0 3%; +margin-top: 20px; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +background: #ffffff; +} + +#main .module header h3 { +display: block; +width: 90%; +float: left; +} + +.module header { +height: 38px; +width: 100%; +background: #F1F1F4 url(../images/secondary_bar.png) repeat-x; +-webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; +-moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; +border-top-left-radius: 5px; border-top-right-radius: 5px; +} + +.module footer { +height: 32px; +width: 100%; +border-top: 1px solid #9CA1B0; +background: #F1F1F4 url(../images/module_footer_bg.png) repeat-x; +-webkit-border-bottom-left-radius: 5px; -webkit-border-bottom-right-radius: 5px; +-moz-border-radius-bottomleft: 5px; -moz-border-radius-bottomright: 5px; +-webkit-border-bottom-left-radius: 5px; -webkit-border-bottom-right-radius: 5px; +} + +.module_content { +margin: 10px 20px; +color: #666;} + +/* Module Widths */ + +.width_full { +width: 95%; +} + +.width_half { +width: 46%; +margin-right: 0; +float: left; +} + +.width_quarter { +width: 26%; +margin-right: 0; +float: left; +} + +.width_3_quarter { +width: 66%; +margin-right: 0; +float: left; +} + +/* Stats Module */ + +.stats_graph { +width: 580px; +float: left; +height: 150px; +overflow-y: scroll; +} + +.stats_overview { +background: #F6F6F6; +border: 1px solid #ccc; +float: right; +width: 26%; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +} + +.overview_today, .overview_previous { +text-align:center; +} + +.stats_overview p { +margin: 0; padding: 0; +text-align: center; +text-transform: uppercase; +text-shadow: 0 1px 0 #fff; +} + +.stats_overview p.overview_day { +font-size: 12px; +font-weight: bold; +margin: 6px 0; +} + +.stats_overview p.overview_count { +font-size: 26px; +font-weight: bold; +color: #333333;} + +.stats_overview p.overview_type { +font-size: 10px; +color: #999999; +margin-bottom: 8px} + +/* Content Manager */ + +.tablesorter { +width: 100%; +margin: -5px 0 0 0; +} + +.tablesorter td{ +margin: 0; +padding: 0; +border-bottom: 1px dotted #ccc; +} + +.tablesorter thead tr { +height: 34px; +background: url(../images/table_sorter_header.png) repeat-x; +text-align: left; +text-indent: 10px; +cursor: pointer; +} + +.tablesorter td { +padding: 15px 10px; +} + +.tablesorter input[type=image] { +margin-right: 10px;} + +ul.tabs { + margin: 3px 10px 0 0; + padding: 0; + float: right; + list-style: none; + height: 24px; /*--Set height of tabs--*/ + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + -webkit-box-shadow: 0 1px 0 #fff; + -moz-box-shadow: 0 1px 0 #fff; + box-shadow: 0 1px 0 #fff; + border: 1px solid #ccc; + font-weight: bold; + text-shadow: 0 1px 0 #fff; +} +ul.tabs li { + float: left; + margin: 0; + padding: 0; + line-height: 24px; +} +ul.tabs li a { + text-decoration: none; + color: #999; + display: block; + padding: 0 10px; + height: 24px; +} + +ul.tabs li a:hover { + color: #44474F; +} + +html ul.tabs li.active a { + color: #44474F; + } + +html ul.tabs li.active, html ul.tabs li.active a:hover { + background: #F1F2F4; + -webkit-box-shadow: inset 0 2px 3px #818181; + -moz-box-shadow: inset 0 2px 3px #818181; + box-shadow: inset 0 2px 3px #818181; +} + +html ul.tabs li:first-child, html ul.tabs li:first-child a { + -webkit-border-top-left-radius: 5px; -webkit-border-bottom-left-radius: 5px; + -moz-border-radius-topleft: 5px; -moz-border-radius-bottomleft: 5px; + border-top-left-radius: 5px; border-bottom-left-radius: 5px; +} + +html ul.tabs li:last-child, html ul.tabs li:last-child a { + -webkit-border-top-right-radius: 5px; -webkit-border-bottom-right-radius: 5px; + -moz-border-radius-topright: 5px; -moz-border-radius-bottomright: 5px; + border-top-right-radius: 5px; border-bottom-right-radius: 5px; +} + +#main .module header h3.tabs_involved { +display: block; +float: left; +} + +/* Messages */ + +.message { +border-bottom: 1px dotted #cccccc; +} + +input[type=submit] { +background: #D0D1D4 url(../images/btn_submit.png) repeat-x; +border: 1px solid #A8A9A8; +-webkit-box-shadow: 0 1px 0 #fff; +-moz-box-shadow: 0 1px 0 #fff; +box-shadow: 0 1px 0 #fff; +font-weight: bold; +height: 22px; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +padding: 0 10px; +color: #666; +text-shadow: 0 1px 0 #fff; +cursor: pointer; +} + +input[type=submit]:hover { +color: #333333; +} + +input[type=submit].alt_btn { +background: #D0D1D4 url(../images/btn_submit_2.png) repeat-x; +border: 1px solid#30B0C8; +-webkit-box-shadow: 0 1px 0 #fff; +-moz-box-shadow: 0 1px 0 #fff; +box-shadow: 0 1px 0 #fff; +font-weight: bold; +height: 22px; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +padding: 0 10px; +color: #003E49; +text-shadow: 0 1px 0 #6CDCF9; +cursor: pointer; +} + +input[type=submit].alt_btn:hover { +color: #001217; +} + +input[type=submit].btn_post_message { +background: #D0D1D4 url(../images/post_message.png) no-repeat; +display: block; +width: 37px; +border: none; +height: 24px; +cursor: pointer; +text-indent: -5000px; +} + +input[type=submit].btn_post_message:hover { +background-position: 0 -24px; +} + +.post_message { +text-align: left; +padding: 5px 0; +} + +.post_message input[type=text] { +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +border: 1px solid #bbb; +height: 20px; +width: 70%; +color: #ccc; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +text-indent: 10px; +background-position: 10px 6px; +float: left; +margin: 0 3.5%; +} + +.post_message input[type=text]:focus { +outline: none; +border: 1px solid #77BACE; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +color: #666666; +} + +.post_message input[type=image] { +float: left; +} + +.message_list { +height: 250px; +overflow-x:hidden; +overflow-y: scroll; +} + +/* New/Edit Article Module */ + +fieldset { +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +background: #F6F6F6; +border: 1px solid #ccc; +padding: 1% 0%; +margin: 10px 0; +} + +fieldset label { +display: block; +float: left; +width: 200px; +height: 25px; +line-height: 25px; +text-shadow: 0 1px 0 #fff; +font-weight: bold; +padding-left: 10px; +margin: -5px 0 5px 0; +text-transform: uppercase; +} + +fieldset input[type=text] { +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +border: 1px solid #BBBBBB; +height: 20px; +color: #666666; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +padding-left: 10px; +background-position: 10px 6px; +margin: 0; +display: block; +float: left; +width: 96%; +margin: 0 10px; +} + +fieldset input[type=text]:focus { +outline: none; +border: 1px solid #77BACE; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +} + +fieldset select { +width: 96%; +margin: 0 10px; +border: 1px solid #bbb; +height: 20px; +color: #666666; +} + +fieldset textarea { +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +border: 1px solid #BBBBBB; +color: #666666; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +box-shadow: inset 0 2px 2px #ccc, 0 1px 0 #fff; +padding-left: 10px; +background-position: 10px 6px; +margin: 0 0.5%; +display: block; +float: left; +width: 96%; +margin: 0 10px; +} + +fieldset textarea:focus { +outline: none; +border: 1px solid #77BACE; +-webkit-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +-moz-box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +box-shadow: inset 0 2px 2px #ccc, 0 0 10px #ADDCE6; +} + +.submit_link { +float: right; +margin-right: 3%; +padding: 5px 0; +} + +.submit_link select { +width: 150px; +border: 1px solid #bbb; +height: 20px; +color: #666666; +} + +#main .module_content h1 { +color: #333333; +text-transform: none; +text-shadow: 0 1px 0 #fff; +font-size: 22px; +margin: 8px 0px; +} + +#main .module_content h2 { +color: #444444; +text-transform: none; +text-shadow: 0 1px 0 #fff; +font-size: 18px; +margin: 8px 0px; +} + +#main .module_content h3 { +color: #666666; +text-transform: uppercase; +text-shadow: 0 1px 0 #fff; +font-size: 13px; +margin: 8px 0px; +} + +#main .module_content h4 { +color: #666666; +text-transform: none; +text-shadow: 0 1px 0 #fff; +font-size: 13px; +margin: 8px 0px; +} + +#main .module_content li { +line-height: 150%; +} + +/* Alerts */ + +#main h4.alert_info { +display: block; +width: 95%; +margin: 20px 3% 0 3%; +margin-top: 20px; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +background: #B5E5EF url(../images/icn_alert_info.png) no-repeat; +background-position: 10px 10px; +border: 1px solid #77BACE; +color: #082B33; +padding: 10px 0; +text-indent: 40px; +font-size: 14px;} + +#main h4.alert_warning { +display: block; +width: 95%; +margin: 20px 3% 0 3%; +margin-top: 20px; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +background: #F5F3BA url(../images/icn_alert_warning.png) no-repeat; +background-position: 10px 10px; +border: 1px solid #C7A20D; +color: #796616; +padding: 10px 0; +text-indent: 40px; +font-size: 14px;} + +#main h4.alert_error { +display: block; +width: 95%; +margin: 20px 3% 0 3%; +margin-top: 20px; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +background: #F3D9D9 url(../images/icn_alert_error.png) no-repeat; +background-position: 10px 10px; +border: 1px solid #D20009; +color: #7B040F; +padding: 10px 0; +text-indent: 40px; +font-size: 14px;} + +#main h4.alert_success { +display: block; +width: 95%; +margin: 20px 3% 0 3%; +margin-top: 20px; +-webkit-border-radius: 5px; +-moz-border-radius: 5px; +border-radius: 5px; +background: #E2F6C5 url(../images/icn_alert_success.png) no-repeat; +background-position: 10px 10px; +border: 1px solid #79C20D; +color: #32510F; +padding: 10px 0; +text-indent: 40px; +font-size: 14px;} + +/* LOG CLEA BUTTON */ +.clearlog { + float: right; + padding: 5px; +} + +/** + * Edit Client Forms + */ +. client_editor { + display: none; +} diff --git a/lib/assets/images/breadcrumb_divider.png b/lib/assets/images/breadcrumb_divider.png new file mode 100644 index 0000000000000000000000000000000000000000..af777f933dc6d6e5d6bcffae00a7f41ae7da0ec3 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eL!3HGH8OdY;DVAa<&kznEsNqQI0P;BtJR*x3 z7`TN%nDNrxx<5cckrLO466d1S#FEVXJcW?V+*Ae=eG`2{!!;XAj{@aoK=Q%)X(i=} zMX3yqDfvmM3T~N2spa`a*~JRZ!KQ^3F(0hFJ8z?Ow>pV8GLSL7+&QvIQ+e|;j*7Je^2A&B~o%+wpHrqyB>T2>SOS9^>bP0l+XkKQfxmt literal 0 HcmV?d00001 diff --git a/lib/assets/images/btn_submit.png b/lib/assets/images/btn_submit.png new file mode 100644 index 0000000000000000000000000000000000000000..1266df505cff1958f1970b421fb83d8ce673d6c5 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^>_9BR!3HGFE;3=*MM_*FN}P*Q6H7Al^Atidb5j}2^iA{)4IfrsIR})N0m%pFrnr@T&#Se571B0ilpUXO@geCyi CMnLxf literal 0 HcmV?d00001 diff --git a/lib/assets/images/btn_submit_2.png b/lib/assets/images/btn_submit_2.png new file mode 100644 index 0000000000000000000000000000000000000000..3ca184b365c11cd702c83bf80dcb7da38c7b6e96 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^>_9BR!3HGFE;3=*MM_*FN}P*Q6H7Al^Atidb5j}2^iA{)4IfrsIR})N0m%pFrz*L907_Y0lfqEG{UHx3vIVCg!0BSoz A*Z=?k literal 0 HcmV?d00001 diff --git a/lib/assets/images/btn_view_site.png b/lib/assets/images/btn_view_site.png new file mode 100644 index 0000000000000000000000000000000000000000..02ffad8054ac696e661e0f64fe72037c08d5306b GIT binary patch literal 1503 zcmV<51t9u~P)000W>0fLJSS^xk56m&&cbVG7wVRUJ4ZXi@?ZDjy8FElSP zF=y?jU;qFB9CSrkbW?9;ba!ELWdK2BZ(?O2Mrm?ocW-iQb09-gHF34$HUIzxIY~r8 zRCt{2n>|b$Nf?ITZ)W_5|7c8+6ycIUkpfbrOhLpoNJK%1C~bmH=$rykr8p%L5>X1O zh$5jlT^}U{k&tMJ4xO&!iX#$@FA+&igUyNn&dhg(J!HLJTm0BTgn6XV8hcrd9)J3F z?0HRS0ugmaB9Rd(<%pDW06@0c`RA0P)_O&2y-+Tf7npgg(TUXPg;*>$6^TTqiKs>} zQNw>ZcQAAC?_#-Jp5EKrdmeUvojD;ycOsEk5JL1@u|(t~H-VWC#4|IDF>7DGd>Jvu zeDa@HV^B(Yy`!UJg^0351R+F?XeYcLzvQZD`?$EXvooNz{@0#+U{46qol2$Fh$v$v z`(?Z84#!Fk%B~(up6B)33O}%yPN&zTlzmpRf4J%nC#~a4qEUgtZc_ z^}zs8+S=N>q>{H6m&=ue5NRnTlu~u=!*Mi%?d0p%uh3e9nYRUibg*qW(Z?s~(7I*jv}m-Zb{wCe z>;vg;dDlLI!c|z=J5CM}!5CBPRw?DV_R${vUL%o+Cz8qJ>S<5vJkZh6u_|)8+|p@J z({UUFvg@@bsryQh7iKLd-v}6%*@PNBH9P4n+E_uMElIV zc;Ui@v6nAj&h75*_B!E5hnW$N#|!yGy7k>w^;TH`w$&D~`?%lh0J#%w&KY#e};Z~_sx}mjp;sZ!2y-X(a z&(*6}|D2qhTpk=8{Ll92AWi!OGfPxbP+0&e0J8PdJ@aS)9)Kc%A`$81UZ@kmeEwapqq#mh^W7<)B1s7<~3#>0r1KH ze2r`(x=utZ6bMuLUT@zF&nRKL>DA|p%sjx%_3<+z>XuTjF>}T*)d@a4e(}rR={5xp;$%irQ>LWn6M>H}(b7c5S2IDRixu?LTR zZ$zIEV#*lvoQSA18jXIo?ISoEUEEP2th$3k2vOSG+q3oZ#k!RdTxGW&f6q>*)2phht82;gJSV#i_-~S3U0q9JczF0twUwNDOzTG+01$~p zyxX^LSK?;_0|U=Ke*E~ztwvglU5#A1a%Fa9W#wD^tf!~P#m`!ZKYmsy6mAmHrUEMQ zv)S3%{QUg^Q_jIx~|i8UApgE_kBx}hGEcs-#X7z%d*6C zpXX7nRbAJql%m#}a?Wb4sg$DYx|C8<&RNg%sOvhN=c$~tQcAk6OT#cIr4;vW+ZNBu zY=8${*QI^m)%U$>t$LnE&+};8Hl>u5Qi{*_eb;^8Dy8Uo9+gs*Qqpl88pkmX*jm%N zt{R3xlHxuNzVEwo&MKwoI1aVeVh^n~-S-{WZQG{fI8JdzZj!4d%F(Cj1UtO8QdAf=>z-!+b-IHV+v;~0p+TkatOUDqk+ zEJ-@gQ+?mZ_fUq&4a1;PN^FGdT5CZ+B3)~J%Qs&O5HoyFDQVj_4Z{$eh6W(f_kDbR zU6;0P3&e8H$~o)xdIkTT??jXQU=0A-_dV8jV76`3G)>y~{f(-yCqVH;CsBuC2;OoS z5Od{VLtMY_yXJY0&2s&=ZR-18$8iL0P(754yvsQUy^iCE=pqELg71k+h6*4~BJwb# zk{ihtqBM?UY-AkAw`3;DL}H$2rIa+!vwU5IhC^Q0rM~Y~O3|_`0qXNS>ia&ZxGal? zVfgwCbVzv6M?!fxTt3fJDW#wo=<_{NN(~@#r4()3rkr!+9yxFvM?4qs&hw1i1z?`R z!D_9lwf;l|b>Kjt&%!As4a1xyfLrUMMsGThv@Ey4^?#b=eGluQj~MnvMixe-0M}BdkH_5lrx zs|85FaaR!zNS{KkAhA?!Y5*H^Ngu~Cl!(u;7+8^7YefIk=|WBRg7Uarz!2<=wVO1I(tMA0?z>`;|u^ImsotI6L2-!OmPKyi9DNkYPZ zXhYp#Lnxio0_6iV=!iW+#;NiyAV@3`M2PTzIp?o}xUOqBOqWb*2<@c4ep=RB(#Hbry0z_Stb# z6nP$p1_+dso`?J;!6~BWd4453qaL3n5k9ZnXt-B${Ek}=OdknOO+cDkYd_ZF+i`pE zgOG3_x>l&{1seI!gp`dl8uE?dZoQ9o)5d~6HKFE!JU{!s2W{vz+{FSC%DHXZTYIps zYdG-Mnrf|IfY3_zeXsL8Luq{aL6MSvdTpXXALBDB)QCC*NNy0_YOt2~?;dT|KN5 z%ZJr)+DD%xAcux(ZU|8&bl+#XrIZ*hP{)X}kDGk>fpcID??*D`&XI>Q+~^hAM?4@l zO;acvKhN{Tv>bH%zK=seGmOrO)b zne(AD@=OiE1bSB}DaJ|2tKWyJwbsHwLbrm2_nw#sq83P;577__4uThBvQA4tM4%*_ z26-rl3{yM-0vRSB$RQV)Z>Ic=W#A^Y1ch=gdT|3St|wBo!Yqawa!{Xwu%4SkiXh@c zE)roRot$f}eGRcWy&Fb~6z-OK;+M zCKPIre;x&df@lH6r&VHY?<&8q`5DA{F{9$TT}DOdEf{#SAe+M!VsQWiYB4N-1T=C6fE~i zpD2E|v>-&~r3iAL5B-QJE*HA`j2DV?5bu=$#Q$6*dnIRKANxB+KIgTc9iPiJh14Ta zYmEf=KM5iF&T`GH!^uN7)f0o~>E)VB-Ke^Aln7Xj2Pt<)H9!GyR^Dvpbq$(FAhV)g@RE;hhc~}W8T-Z zXIHW8ixCC##*-B8vH;^x@BCb`s4ZA;uh1Y_YkgBTpRTOy8V=d*55V~l@H|fys;>0& zwuv8{rMpZj{^y9F!2%T<;b2(s896_=8&&eh-!fw&eZt7IFrb-v5t7R9{%1%2=E&cb ze?)k*as!xvpzWRw%MA6gZLp Y0J~EJF(f8#>;M1&07*qoM6N<$f+UiFIsgCw literal 0 HcmV?d00001 diff --git a/lib/assets/images/header_shadow.png b/lib/assets/images/header_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..a1b864526c76368e3d5a8995f6e4716fc9d82676 GIT binary patch literal 1137 zcmV-%1djWOP)R9CSrkbW?9;ba!ELWdK2BZ(?O2Mrm?ocW-iQb09-gHF34$HUIzv%Sl8* zR7iE(^kls7S7-+3w&IterA^4&gBi33-DFFc1TE1RH1Zyn- zfSF;8;iu)CeZyKS=A4*wLRB%wfT}7)1R(^dszX&(U#O}GAt1)c6Y+YzpoGiCy?3m& zaGodToG>$#QvCUG9KKHWIsjnKiM5v3z1Bi&%@JMK<-F(^12IN~5HQAgSQJ6;9U%mN zn;B}YNGV~?Np8eh1b{IHLI}t?`=+W2065Rn(Zv`cghoODAjXK^J7SDTDZ$MAG*v}w z4Q2-AP9pL}T+SJ0=H-MC0KhvFV}!6qKAdwRgy5o~KzM5J-JuH`Px!1G094hL#uwMs zS|OAl|3Ia&Mm}fj_#MF0XZ~PYSOO(V61j%$RW2kP_LsF%P3*v0%Oxv>w!@+sxEbpw zf-wfn?1BB4X+EQWMFj8n8zSO7^xhvo^R?S^&iSR|ob$u*N0DO;D0@hlGyqQofYzFG zz%{-1M`3%t6hlf0s`{|{={HLwbgi|1X`3zSy}JSCoH&ld`JiYd=j^>DbZVV(bDpQ4 zx+_pxYya#>E7n^5xm-3XcWiwI(jxbGXSHIL@C*7LoT;;mwxM3QsH zbzLseeO+q}x@Q(y=PA1p#u(mf>R$FR-(!c7%r@=)_juj~LZ%4~ct*^1T`oXM31wW>S|Q9S{+T*nYoU|^Gjpg+9gZX2owJ*Q-%BY@5Is$#YG%F@ z+eVA>`Cq@Y1H!&?O6K7t+gIk@ix$V_x|$a)*9aL_gAkuqm_-PcG+tZ#i$));Prap zx-M_6{51t(lN?5pnYm2q2O)mu@jg8`07xJ^bKm!K37{cp5GskYnJ+%$2)eH8883Cjy1JlZDIeG}HXn}Xa>JVFzUF~0iEIYoVYdVE?s#`wSHU5?c6j)qf% zH=X&2aNoBh@+o=W z_jjm$l4Q4LYwZiRM-`3DsG)zERXJy8*UX*)_xb%FS{pLLG*Sa=00000NkvXXu0mjf D*ANEl literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_add_user.png b/lib/assets/images/icn_add_user.png new file mode 100644 index 0000000000000000000000000000000000000000..ef0e209536f9c14f0af6096b184e3d0af4f1a961 GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^`3~Y#39vM*Zz!{A8nkdeg3W{;-_1r*-@5vuCMs%{>47{PN2$ zg%4P&$MR2Z&pZu_2Z8ox3^vAh@8f_<=$`m4+%n{u}rs3d_UjT6>$x$elPy_ z**m{U(hhn`_YY*(_bP4bdG^|5Cqu*BnY>cnix?gVJuhPO2q|TLb1W_>^z%JsrRzT~ zaVpI8TmC+|;fBlhV1|xWeCZWDT1mKvKG<;ZjtjbEJL!TaFhTInM8 zy`|0D`wmGmG$<~@fIM?v7xX{XBOE@xucl{=H+!}O3#r&&@s((kV< zy?yD=A(17XLB_{EL{~G|tTj9&<>YdILeoc&2^^xA{1kmfI~AQ2&zv)r6|&3W**Hr{ zC6wJdO1W{1&~qSJR6FVL7Qw*P%3IuSuKWCDR$YFqEZfHE4gNj+x`MLbr48Nx-k4WY W(qE^>#OMR`CxfS}pUXO@geCxtj*O`Q literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_alert_info.png b/lib/assets/images/icn_alert_info.png new file mode 100644 index 0000000000000000000000000000000000000000..1be971d44cb72210304d9e27c58c8db6413cf52d GIT binary patch literal 434 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mSQK*5Dp-y;YjHK@;M7UB8wRq zxPc0}g%~g0t@{HM6e)3yC~+=IO)SaG&r=A=%uQu5(>KvKG<;Zj8i(`mI@7gH`bDJFmT<^Q>yd5GC z)c8K8$A!_4bISv+MIjT!wy^{;2<-+mBq;@eK XQpsB6Th4wL7=8?%u6{1-oD!M0H literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_alert_success.png b/lib/assets/images/icn_alert_success.png new file mode 100644 index 0000000000000000000000000000000000000000..d72f09e9a7c923ff2a815745e0f6dc1d314988f1 GIT binary patch literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mSQK*5Dp-y;YjHK@;M7UB8wRq zxPc0}g%~g0t@{HM6e)3yC~+=IO)SaG&r=A=%uQu5(>KvKG<;Zj}&_(!&D}? z#h90EuD{#DaOG$hpF-W6H1-K=UvLFHYER?RPL2scEOv1Rkm+$@I9ic7Ux&| literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_alert_warning.png b/lib/assets/images/icn_alert_warning.png new file mode 100644 index 0000000000000000000000000000000000000000..53f545af1d03271a3091d758833dae803d6e85fc GIT binary patch literal 418 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mSQK*5Dp-y;YjHK@;M7UB8wRq zxPc0}g%~g0t@{HM6e)3yC~+=IO)SaG&r=A=%uQu5(>KvKG<;Zjp$jVYAV`WX`!QIQ*s7jX7b5l)sA;Q4q(!qsPK7vI&8(yYPw>$tDoQR1y*)xh|y0Sy-kxMc3ji{0l^}&{7MF z*@IjvTVs2W`w^^^g#*(F2GRx$ToVkgWGqaMByNO!tKmCuJ`D5bJ%|WI1W{t
QH z@fkDUUE~&ZB7$p?)qjij0h|KZ1As=Oald}0_fPb z9jn!9CzvnVMhFoNMdZLV&7*$54*<|K?XXlT>8|U3A)@;$FaLjEfrwN^-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^RR==bBWPc=S(s=<`Ih9Wi k!kdon+vJyH>V0n~gUo#oMwxW3W}pQOp00i_>zopr02e?|ZvX%Q literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_edit.png b/lib/assets/images/icn_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..be8c68fac4b966d7032abf18e1587e99c783c9e5 GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`Jj2H@M0Oe&s^1=COCFO}l zsSJ)O`AMk?Zka`?<@rU~#R|^BriEJ{n*r7B_jGX#vFM$9X(R6;0|AGN{HGXt930Pc zb#P8_^i)D;4H?t*SNFWUe)^Jwxwxr6 zLwD2U^9Ss_ELK(WN)(*t;$d)*R?XS9_ifGt=I@u?IHhM)Hp@#qSoutQMnyB{oJA}z z#TPfm*hpy{jBgD3Bld&+@E`vq_3VW3wSJYD@<);T3K0RXL;et`f0 literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_edit_article.png b/lib/assets/images/icn_edit_article.png new file mode 100644 index 0000000000000000000000000000000000000000..5a7e2c2ac7f2f4c1108e6a74afc0637281da4ee8 GIT binary patch literal 467 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^?m;Te+d2lPm`KGkl&_jKHS)#-*6jyEC#kIeTlZh>{O6ggn9lTP zF%=(ROn?2=YMzhU_qS!$QY;5Q7Fg6wn|0uH+@G1tUNRf7h1DyGG6ag$io0Hn)|)Q< z-gc^oXhP4ysNak+T_=`(D*wl5Bxor<(Rb#5r!0oXF4dUWOJWqr2aXpUY&<(reh*vE~&6L*nYI zuOyQtc-TsI-xU@-`6^D&d(U^t2aPfn!V}l4HGH2ZU-c~iedD}Y>c9YI@O1TaS?83{ F1OUKlwUYn< literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_folder.png b/lib/assets/images/icn_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..03e4057340aae35266274947c1225ca8765378d2 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^l`yWO|3GO#d&+A_x2Kl^^|)?4WVXB-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^)W~|Ll?p5&8Y)Q0GmfYI5B=eW- z@sF=}Rw$n++r3x$XJ!vjkfEXD4#NzoWVW@6tp^J4^%&;eEHABkal?h-K-J!3NgErs zZtIZeYk%#x+?df|bP0l+XkKOvuBl literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_logout.png b/lib/assets/images/icn_logout.png new file mode 100644 index 0000000000000000000000000000000000000000..ea274c51ae62e48dd4a17e0304c73a774c67feb1 GIT binary patch literal 443 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`Jj2H@M0Oe&s^1=COCFO}l zsSJ)O`AMk?Zka`?<@rU~#R|^BriEJ{n=vpjih8;@hFJ8@on)BTVj$qU-#msTli$Jr zu;1fkoodc^33?l!uxTzdxGAk-+r469P5XXVhaw)epkwAEk9Z1iS` zSGx3vwZlVMDMms=P?MpiD*fK?efr4;4Xdy3nzLr>g{a@p*Huq$*>FwjT9k^{O7@d% z4mV5Z>L0W^DHjtOx_C;J3d4dayCxo)mlFH_fH{N5wcZ&=_PcTX7n1Q^l^3skvt8C8ie_nYmY0?vp zxc4&MJKp~;`aCsxz3->E>!mZ!v8r@$%hgmeIX2-*?dOttMOL`!$gyQ>)$3JgdFPgg&ebxsLQ05(3Xpa1{> literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_new_article.png b/lib/assets/images/icn_new_article.png new file mode 100644 index 0000000000000000000000000000000000000000..23f57f736c0aba6f210462ff568ade3e393474d4 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^d0>! zH|l7y`EKi3U#DT`EojWlFu}%kQ%Gn`zOne?Th{s|{F8WQo2Up~=}`GA8_KJB+1Jgb zS6y;Zst|+5QVGv1F7JCRlwJr{KG>&RXlz@sb*JW}sy)-=ZqL;y_%vI0pC^OQ#0L*g ZGWtpAtUPp(u^Z?P22WQ%mvv4FO#pW0U6=p> literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_photo.png b/lib/assets/images/icn_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..bf27b5263e4d788499b5632633bfa88db5ed30ef GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^PrXZS{o`YE=|{{ByE>7B0*+#C%aF6qx@B$M|T9*fR??^5!Ed+C zW(Pe=nz5zJ`UhH&r<8<3GNrKj5;yNNn?eH~tkh W_Tj;IIb?yZX7F_Nb6Mw<&;$Tnw|2k) literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_profile.png b/lib/assets/images/icn_profile.png new file mode 100644 index 0000000000000000000000000000000000000000..1821d290e961b480735bf4983523bf93ed205bb5 GIT binary patch literal 485 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^lDYsXzGO=l}Gtgs<=s*5?t8AzG!5is1FSpIfQ1=vZ?Kfpu zznn?4>(L^8y%#7LqL4YOY_ra7?h~7OOni6W{yO=S zi;LPStB%J%60PPM%|FlRF{!0&x9=nsX2F?%xEUq*+ABBTtO>o!)4?Ft?P@61dvDt5 z)OX%LuIF64<^6!u4nJ za0`Jj2H@M0Oe&s^1=COCFO}l zsSJ)O`AMk?Zka`?<@rU~#R|^BriEJ{n=vpjvU|EXhFJ8@opd_zZ~%{M|78a!)*Z}; z&fFE&Iw1Q%D2Ayd!D@pwhiuEGl+sk6P{QT1OeAx*YG{$5;z(Sdsnv2AOdo_PM@i1E1Psnr;rz_PC4ZxUOY zS;Z1{Pv5dn^Y;Au7}+{0On_2X3uTo2=1vx$*J}hIx&Pm&Y3ZkJ!7Uij8A} zZNcV%qc0fW87(<6FV5-BQH7PCxA(2*zwW*2Jj4FTwm*^+6#p|>EMaWpFL#Z<@l}99 Q4;XX|p00i_>zopr06g}hn*aa+ literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_security.png b/lib/assets/images/icn_security.png new file mode 100644 index 0000000000000000000000000000000000000000..ac0adbf37d538af41282d9194ab5638b703a4148 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^ zud#PiSZz;@tq|7+0$YJHDmj`B>NY^Bj5a7cNP`SntJoAbMw z8*I+s?_RWF_qyHT3lyKc+GBFrWP757l%v2K$z<(gH?%mKY9-p*GH2cHeyd^X`#ItE zH)FxKZ0}-YuV2rcweN*Ot+nT*8m1i=Su?+gx?WtnZtl*8z&9&oRZnkv@Y{|-;j~dm zsHvw+?3ziBE!5BboK?K9Oy+sX$~B%HQ$9JkxGPzCY4Y!Hnv(i+$!2lKTic#mRSTwc zs>QQ2v|v+y*dB@ literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_settings.png b/lib/assets/images/icn_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..dfb5526061123a498736c4fe36014624ebc68965 GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^axRynLkrNUS#le^>bP0 Hl+XkK;wxTO literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_tags.png b/lib/assets/images/icn_tags.png new file mode 100644 index 0000000000000000000000000000000000000000..544a958cbc20bd11ee274a28df0c4010fb0235a2 GIT binary patch literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^ERKV(ZY{3vk=|+ZXwKyC z|I{~CZxr3&Rpk{G=M|G=3v&#J%clR18z2;w?$>Ba1#b65TzkYhZycw^(r)*F(` cA50O=Z@ZP8Wy$ck9Ox1TPgg&ebxsLQ04{-N82|tP literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_trash.png b/lib/assets/images/icn_trash.png new file mode 100644 index 0000000000000000000000000000000000000000..675820f7b77eef0327d4fac5604749e1d768917e GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~i!3HGN^yhQ|DVAa<&kznEsNqQI0P;BtJR*x3 z7`TN%nDNrxx<5cckrLO466d1S#FEVXJcW?V+*AfreG`2{!}Pby5`gkDAo<|@w370~ zqErUQl>DSr1-Hzi)bjkI>|zDyVAH~_kIjJU$~;{hLo_BPryO8PI~)I9es)Dvl7R9q z31z*bN0`*SOoCYrj0_A6&dl2X&X$?EnW3}Feu*j@=b=3X6M0VS8yP4ZdcYvbCgWiC zfJu@qW5kTFZcKp2MK@4P{R0m;_M&X>W0WEAxW1cVDWJ9g~Y&G--OFK`bW_zzUXQK&=g znhwRSOC21JO9yR9&V;n@_}%k9_uUa1BBi_pkOH{)8@&KXwbsurd(co$0XzWcSH^t+ zH$n)S&1N+THQfBvv{fZn)lFFS#P|LCj`BaICRnXjr(FVxCfn&Jc!933!?^^}{V@%KUyfGu9;lAZfh-gS< fSr+L2xVFsKPOxW&D&cSR00000NkvXXu0mjf@bSkF literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_video.png b/lib/assets/images/icn_video.png new file mode 100644 index 0000000000000000000000000000000000000000..2a06544cdbabc9a22f2966d468333e31f26a6e8e GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^^WPeY`OBoS!v#+97WD|dY-4g_IYx9X8T)(_pDSuJ*VWyean^G xohQ9pmlnwqU{$xDY2uP!|D9%i$k=q0{o2p+XZDGl4nW5-c)I$ztaD0e0s!YfZYcl& literal 0 HcmV?d00001 diff --git a/lib/assets/images/icn_view_users.png b/lib/assets/images/icn_view_users.png new file mode 100644 index 0000000000000000000000000000000000000000..46148d5005c01838a79644ae81b464eddca7b933 GIT binary patch literal 528 zcmV+r0`L8aP)zl)p+VQ4q#|vuu*$YFOA1!sQUGQY~l`NTIm>M!4(jLjy7BpT7iulg8<-#0THj;Ja>mG@%;AoB(1T%6giwf1=xKvmx% z&ba`%0yeYK3$Sag?XH4M*YSP-bOr!W6p`Bu{;8&rG)@06Nm&bmVEfCEk5nucS3>-W z3B!=VV6e~-MbXp5JLgV-7-(B-+Y6J_>-C3uK&@887;}*U_JLzy7dUs$Z7e{l)#_oh z*`!jb5QZU*MuRwx+amG^V2t@U8(@rSOuS?%0dV4Z-obc0{xj2i4IEi(yS-lTtkdaO zfP6krk|cZOa`|%lPFk(jy{bkF2LVK6sH!_jlC1ap{hQ%%NU2nMjiTtUh&-z*k{#=K z8DOGZF4s-d)SnBwv(|2@Dqf*bIQk3#FdB`vmQ1>t9UD~jQNOQ!RYiWySMUZ!aK7eG SLBY-d0000Q?zY$rZ>LEn0z0^lt(Gis3<+G8Iaf_A_0#ts4>cYfn|=Q1+Q{PIXN&=dy^J`f SJ(&tLj=|H_&t;ucLK6U(3`#lx literal 0 HcmV?d00001 diff --git a/lib/assets/images/post_message.png b/lib/assets/images/post_message.png new file mode 100644 index 0000000000000000000000000000000000000000..add5480260fe9e0a9273694368217f260074c9a8 GIT binary patch literal 1479 zcmV;&1vvVNP)f_q?=N)Di5`+`ao<5Ew4Dl!3hnC1F=05k3EkrAZaL+%s3dSQaYB#o0w zrAemJzTrZOf^9pxEC_-uilXnGDwRr%-?@ct+b^8wI1W=&1xlq7Z}h(D3rv* z9zI+M4742Ewo#O|`*?u(7|P#BBoa+SMIsSjJy?PA`N?EnYYHftOcIU7eD(MT%I8iQ z<`M@F9t;GeX&M6q`!LN~z@73uU&FHMgd-|PjvPVLv^9aU*(`&D?=W9_(W z(gIpsC6~)FHa5o9t5;WcMAI}BMIo6?lF4MKH*E3~1suomRoiu4BuPS+BniiMkR-`B zSfyfc{rW8?Cno{eyLT_Wy}ejg1IKZwRt*Y;8S3@A@0tLv;~1H^fvrJ#CtX@58=4IyyQyeE0~n4;Gl2nGL-D zxet<2C7Dc3h`V}v&RABBT5ZYqVe~u?K@j=l&)XP=8ED;q4b!aRI5s;nna>4)d&Od9 zY-}tZi)loo>bk#%pjlcnFwF}6eSMX#t}a~$NT*UmvLt=;`;{wdrLssg8b?vW$Z`l# z6kj@o>pD1&L!)8w_~JNCW z$g+&0gwZsOZQHidy>sU*@SY$DmsV_M6<7#30lW{q4r~I#&z40)%6v{~q6P zO?zKHpXcI*AF(X!h4U=S;JlzPooP0s}3_G!0+x^?|)_`}XZkL#b-KHQu*>|Nf?cve_(p zr`{UxYuGMBLqmapbY17zv3FbReW`SY;o)I)U0)OEz<~ptJb98z)&HwDti$_GpFYj` z^XEBt?%c|b=(_x*3Y?{BW-QH#-|iG=_F002ovPDHLkV1hxEuWkSU literal 0 HcmV?d00001 diff --git a/lib/assets/images/secondary_bar.png b/lib/assets/images/secondary_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..42dbf1f37d55ee92bdde7d56c3226c13f5da1e09 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^AhsF@8<6}P!}S43u@pObhHwBu4M$1`kk47*5m^jW z_Zoy5o4R?UfPx|=t`Q~9MX8A;nfZANA(^?U45s=f`i6$-ZDKNwi`Epdv97FpXQ{Z`)kWc%fntMRWAwoJJZ^yh}+R@-t_8{bHnA`t{@*b=qaW->`76L6}@O1TaS?83{1OS}&S*QR2 literal 0 HcmV?d00001 diff --git a/lib/assets/images/secondary_bar_shadow.png b/lib/assets/images/secondary_bar_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..0f798dfd4c97cba6cf22645571c5abc31b75e5c4 GIT binary patch literal 498 zcmeAS@N?(olHy`uVBq!ia0vp^Ahrqz8<5;EW%X|$#Zv6#8NvYsH5@4&Kt5-IM`STj z-D?nLZ0hEX0t$+hxJHyX7o{eaWaj57gkC#5R5WfrBD=NDxcD>w(67H)lP#=yYn*|(@gLBLJE(VaT`0E#mfb9?hrE>Ntw;vD{9v zo5OaT=SuU*>cTa$3dIjS=2i!9dvAMYL4IlaD@hOT;+k0ff6Y~4t;}ia&$eIjefUf1 zn}ITi@eIqis%Lbv*@G_`u&3}b$?H_&F#}mp8Cq3KNe>m{rl%>=Rm)Ng>vh+ z*@zyga9DL^^V+lLmDhT|wORhIzB&HC)ALh%vlo7sdw$38n?yng^QxwfO|?(Vtz)>8 z#RLMKI96!g6MruE?DK2s_pE`ArU?fpOmcMbdwBf!ZM6*zH=AaPrr+lZW#Mj2jf}`i lj1@0)c>h=ad$@c&N literal 0 HcmV?d00001 diff --git a/lib/assets/images/sidebar.png b/lib/assets/images/sidebar.png new file mode 100644 index 0000000000000000000000000000000000000000..7079be4447260491317534e9c31cf36c1c8d41d2 GIT binary patch literal 1941 zcmV;G2Wt3f5aoXTM;qQh+)^h`+!Zohv0x8L`z=Xt8CYVZAd z<-7NNYwump^VC|a#uzo{tT9Ht?^~_4zt;1f?^~`B1;XW@mUjg-nhq6_94$%<8O>{vYi1u zi6CND%wi&qlR%9Kf_aPsZnqjhDYA*O3)*rBPBsrR14<6G5b8*$5xl2sISXL;ecunh z*IGv@hOrOq(;N&LGHBRggGYpG@_C->PA#uii~wiE3jB)k2={k9$N*Nr)y6sV*x|-O zKn=g19;e~%OwQabdcF4=V|>~$ijiTt#<}a$?cc-Fa}67DYGlFjN`#Ez%z%fe@r@#i z)Ln?jZ8h7popS)N%lbZ(9d8kKYmZ4?_@8y`Go(Cw5-yIgYscAiLor1{moq!;0K%-y z(epYKVZ5Vf8es?Ak+L0QaMBxWVz!wsp=0m;(|!@+^xFVx$Huy4x?S1`;MIFS#t>y> zX3(HMh8VAN&Z7$_&csKM2Vn{)dI<&75zBEsIWR;tv+C%h*Y)7QStbs()_&Trqf3O1 zOBXU71>AcgyV!385hK}aDnq1iL(*`kIRUS~GD5nE8X33=bo)y($l5t)%{fmX+yv4| z2M*}n=84y$1LwPm7aJXQ4A1t5k*uc7JM9{RLHj#T_8fc|nlmT)l^aVp{+%+S?pNd2#M0fz z(E|5wB8Hyl5APjm*MqyK1Av3wLoJV?=2R|r^C#BfgfAqV`ZO9y10}PF8~l>Ul2IHV z9&Ql38u&KRQxDJ6n=wc|l&SYSXyRsK?ky`{+tkPC@CeK^mv@`ph}@`46LP3_ufC(L8R7744H(FuUT)clE-yoXkZBV_F>62=xlmO-cGKa7Un1?Wxw@u z7pkd-2Np)rYTeav&~Uz8Sh(%mro8MPZksMPqG$*h+XTRMlgjhP#BjXP4I7*s&nR)m zkG$+0Zlh+bZlPwlZnutihLee`yhpIvq~4Wqhfrr*UtE~=&plfzQ{0%L^n=C#((Pj4u_)rS=)@x!SsTxbo!K(1%P=hr5k zNNuDU#f_7hzl~4jD}6bT+*!QG#c%pxBisoy!eokQ+Xf$|oDqWMd3JszbelABbXIbD z+bGiwXUXRYWTnoeKDT3qn~_QA$xh-B4?UEU?>K`9@(-tGv*vM`b@X~L^4|4hXgP!I zKgDjscx$IFV7Ea!k8edhaWiYlzwB(*oU?u-k;pg!PQIV?yy|-kZf-=1{fn@(nLLfh z4*MqyM?H_;KB49fW>x*LUs{@t@DTJsV@-aaWD{ukekyCvv&$XG*!Qv40neHA&wDLX zWV28U1ZoUGU&@)G=C4d%m#0m4Ca!ko;|GGJ{@u3=+AAOhb6Mm>uXgX b#Q6Synp!z*k9Buu00000NkvXXu0mjfm9DUZ literal 0 HcmV?d00001 diff --git a/lib/assets/images/sidebar_divider.png b/lib/assets/images/sidebar_divider.png new file mode 100644 index 0000000000000000000000000000000000000000..e92be130715da11b004cf776010442ed9eb3b06b GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ!0V1no*Tw-UmSQK*5Dp-y;YjHK@;M7UB8wRq zxP?HN@zUM8KR`i|64!_l=c3falFa-(g^4nJ za0`Jj{(+%k(&%kzt}ixr%MO$)a^HUp|t^K@|x;h346l8|7Kn2_+}(18OV8XFrQ op8o&;|9*o6MxPrj>@%Gh3}3Lbm;2|619dQXy85}Sb4q9e01MtcbN~PV literal 0 HcmV?d00001 diff --git a/lib/assets/images/table_sorter_header.png b/lib/assets/images/table_sorter_header.png new file mode 100644 index 0000000000000000000000000000000000000000..381778d31e2c00f83f42504ef1748936c534b45c GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^{6MV4!3HE7o-{84QY^(zo*^7SP{WbZ0pxQQctjR6 zFmMZjFyp1Wb$@_@A|5@hUssYB>?4RK=Q%)X(i=} zMX3yqDfvmM3T~N2spa`a*~JRZ!KQ^'+hideText+''); + +// hide all of the elements with a class of 'toggle' +$('.toggle').show(); + +// capture clicks on the toggle links +$('a.toggleLink').click(function() { + +// switch visibility +is_visible = !is_visible; + +// change the link text depending on whether the element is shown or hidden +if ($(this).text()==showText) { +$(this).text(hideText); +$(this).parent().next('.toggle').slideDown('slow'); +} +else { +$(this).text(showText); +$(this).parent().next('.toggle').slideUp('slow'); +} + +// return false so any link destination is not followed +return false; + +}); +}); \ No newline at end of file diff --git a/lib/assets/js/jquery.equalHeight.js b/lib/assets/js/jquery.equalHeight.js new file mode 100644 index 0000000..35c0b9a --- /dev/null +++ b/lib/assets/js/jquery.equalHeight.js @@ -0,0 +1,20 @@ +// make sure the $ is pointing to JQuery and not some other library + (function($){ + // add a new method to JQuery + + $.fn.equalHeight = function() { + // find the tallest height in the collection + // that was passed in (.column) + tallest = 0; + this.each(function(){ + thisHeight = $(this).height(); + if( thisHeight > tallest) + tallest = thisHeight; + }); + + // set each items height to use the tallest value found + this.each(function(){ + $(this).height(tallest); + }); + } + })(jQuery); \ No newline at end of file diff --git a/lib/assets/js/jquery.tablesorter.min.js b/lib/assets/js/jquery.tablesorter.min.js new file mode 100644 index 0000000..b8605df --- /dev/null +++ b/lib/assets/js/jquery.tablesorter.min.js @@ -0,0 +1,4 @@ + +(function($){$.extend({tablesorter:new +function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((ab)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.4 + */ +interface IOAuth2GrantClient extends IOAuth2Storage { + + /** + * Required for OAuth2::GRANT_TYPE_CLIENT_CREDENTIALS. + * + * @param $client_id + * Client identifier to be check with. + * @param $client_secret + * (optional) If a secret is required, check that they've given the right one. + * + * @return + * TRUE if the client credentials are valid, and MUST return FALSE if it isn't. + * When using "client credentials" grant mechanism and you want to + * verify the scope of a user's access, return an associative array + * with the scope values as below. We'll check the scope you provide + * against the requested scope before providing an access token: + * @code + * return array( + * 'scope' => , + * ); + * @endcode + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.4.2 + * + * @ingroup oauth2_section_4 + */ + public function checkClientCredentialsGrant($client_id, $client_secret); +} \ No newline at end of file diff --git a/lib/classes/IOAuth2GrantCode.php b/lib/classes/IOAuth2GrantCode.php new file mode 100644 index 0000000..109849e --- /dev/null +++ b/lib/classes/IOAuth2GrantCode.php @@ -0,0 +1,72 @@ + + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1 + */ +interface IOAuth2GrantCode extends IOAuth2Storage { + + /** + * The Authorization Code grant type supports a response type of "code". + * + * @var string + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-1.4.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2 + */ + const RESPONSE_TYPE_CODE = OAuth2::RESPONSE_TYPE_AUTH_CODE; + + /** + * Fetch authorization code data (probably the most common grant type). + * + * Retrieve the stored data for the given authorization code. + * + * Required for OAuth2::GRANT_TYPE_AUTH_CODE. + * + * @param $code + * Authorization code to be check with. + * + * @return + * An associative array as below, and NULL if the code is invalid: + * - client_id: Stored client identifier. + * - redirect_uri: Stored redirect URI. + * - expires: Stored expiration in unix timestamp. + * - scope: (optional) Stored scope values in space-separated string. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1 + * + * @ingroup oauth2_section_4 + */ + public function getAuthCode($code); + + /** + * Take the provided authorization code values and store them somewhere. + * + * This function should be the storage counterpart to getAuthCode(). + * + * If storage fails for some reason, we're not currently checking for + * any sort of success/failure, so you should bail out of the script + * and provide a descriptive fail message. + * + * Required for OAuth2::GRANT_TYPE_AUTH_CODE. + * + * @param $code + * Authorization code to be stored. + * @param $client_id + * Client identifier to be stored. + * @param $user_id + * User identifier to be stored. + * @param $redirect_uri + * Redirect URI to be stored. + * @param $expires + * Expiration to be stored. + * @param $scope + * (optional) Scopes to be stored in space-separated string. + * + * @ingroup oauth2_section_4 + */ + public function setAuthCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = NULL); + +} \ No newline at end of file diff --git a/lib/classes/IOAuth2GrantExtension.php b/lib/classes/IOAuth2GrantExtension.php new file mode 100644 index 0000000..b14e979 --- /dev/null +++ b/lib/classes/IOAuth2GrantExtension.php @@ -0,0 +1,35 @@ + + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.5 + */ +interface IOAuth2GrantExtension extends IOAuth2Storage { + + /** + * Check any extended grant types. + * + * @param string $uri + * URI of the grant type definition + * @param array $inputData + * Unfiltered input data. The source is *not* guaranteed to be POST (but + * is likely to be). + * @param array $authHeaders + * Authorization headers + * @return + * FALSE if the authorization is rejected or not support. + * TRUE or an associative array if you wantto verify the scope: + * @code + * return array( + * 'scope' => , + * ); + * @endcode + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-1.4.5 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2 + */ + public function checkGrantExtension($uri, array $inputData, array $authHeaders); +} \ No newline at end of file diff --git a/lib/classes/IOAuth2GrantImplicit.php b/lib/classes/IOAuth2GrantImplicit.php new file mode 100644 index 0000000..1b806c7 --- /dev/null +++ b/lib/classes/IOAuth2GrantImplicit.php @@ -0,0 +1,20 @@ + + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2 + */ +interface IOAuth2GrantImplicit extends IOAuth2Storage { + + /** + * The Implicit grant type supports a response type of "token". + * + * @var string + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-1.4.2 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2 + */ + const RESPONSE_TYPE_TOKEN = OAuth2::RESPONSE_TYPE_ACCESS_TOKEN; +} \ No newline at end of file diff --git a/lib/classes/IOAuth2GrantUser.php b/lib/classes/IOAuth2GrantUser.php new file mode 100644 index 0000000..8373348 --- /dev/null +++ b/lib/classes/IOAuth2GrantUser.php @@ -0,0 +1,46 @@ + + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.3 + */ +interface IOAuth2GrantUser extends IOAuth2Storage { + + /** + * Grant access tokens for basic user credentials. + * + * Check the supplied username and password for validity. + * + * You can also use the $client_id param to do any checks required based + * on a client, if you need that. + * + * Required for OAuth2::GRANT_TYPE_USER_CREDENTIALS. + * + * @param $client_id + * Client identifier to be check with. + * @param $username + * Username to be check with. + * @param $password + * Password to be check with. + * + * @return + * TRUE if the username and password are valid, and FALSE if it isn't. + * Moreover, if the username and password are valid, and you want to + * verify the scope of a user's access, return an associative array + * with the scope values as below. We'll check the scope you provide + * against the requested scope before providing an access token: + * @code + * return array( + * 'scope' => , + * ); + * @endcode + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.3 + * + * @ingroup oauth2_section_4 + */ + public function checkUserCredentials($client_id, $username, $password); +} \ No newline at end of file diff --git a/lib/classes/IOAuth2RefreshTokens.php b/lib/classes/IOAuth2RefreshTokens.php new file mode 100644 index 0000000..6dedc1c --- /dev/null +++ b/lib/classes/IOAuth2RefreshTokens.php @@ -0,0 +1,77 @@ + + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-6 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-1.5 + */ +interface IOAuth2RefreshTokens extends IOAuth2Storage { + + /** + * Grant refresh access tokens. + * + * Retrieve the stored data for the given refresh token. + * + * Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN. + * + * @param $refresh_token + * Refresh token to be check with. + * + * @return + * An associative array as below, and NULL if the refresh_token is + * invalid: + * - client_id: Stored client identifier. + * - expires: Stored expiration unix timestamp. + * - scope: (optional) Stored scope values in space-separated string. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-6 + * + * @ingroup oauth2_section_6 + */ + public function getRefreshToken($refresh_token); + + /** + * Take the provided refresh token values and store them somewhere. + * + * This function should be the storage counterpart to getRefreshToken(). + * + * If storage fails for some reason, we're not currently checking for + * any sort of success/failure, so you should bail out of the script + * and provide a descriptive fail message. + * + * Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN. + * + * @param $refresh_token + * Refresh token to be stored. + * @param $client_id + * Client identifier to be stored. + * @param $expires + * expires to be stored. + * @param $scope + * (optional) Scopes to be stored in space-separated string. + * + * @ingroup oauth2_section_6 + */ + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = NULL); + + /** + * Expire a used refresh token. + * + * This is not explicitly required in the spec, but is almost implied. + * After granting a new refresh token, the old one is no longer useful and + * so should be forcibly expired in the data store so it can't be used again. + * + * If storage fails for some reason, we're not currently checking for + * any sort of success/failure, so you should bail out of the script + * and provide a descriptive fail message. + * + * @param $refresh_token + * Refresh token to be expirse. + * + * @ingroup oauth2_section_6 + */ + public function unsetRefreshToken($refresh_token); +} \ No newline at end of file diff --git a/lib/classes/IOAuth2Storage.php b/lib/classes/IOAuth2Storage.php new file mode 100644 index 0000000..c25f819 --- /dev/null +++ b/lib/classes/IOAuth2Storage.php @@ -0,0 +1,103 @@ + + */ + + /** + * Storage Interface + */ +interface IOAuth2Storage { + + /** + * Make sure that the client credentials is valid. + * + * @param $client_id + * Client identifier to be check with. + * @param $client_secret + * (optional) If a secret is required, check that they've given the right one. + * + * @return TRUE if the client credentials are valid, and MUST return FALSE if it isn't. + * @endcode + * + * + */ + public function checkClientCredentials($client_id, $client_secret); + + /** + * Get client details corresponding client_id. + * + * OAuth says we should store request URIs for each registered client. + * Implement this function to grab the stored URI for a given client id. + * + * @param $client_id + * Client identifier to be check with. + * + * @return array + * Client details. Only mandatory item is the "registered redirect URI", and MUST + * return FALSE if the given client does not exist or is invalid. + * + * @ingroup oauth2_section_4 + */ + public function getClientDetails($client_id); + + /** + * Look up the supplied oauth_token from storage. + * + * We need to retrieve access token data as we create and verify tokens. + * + * @param $oauth_token + * oauth_token to be check with. + * + * @return + * An associative array as below, and return NULL if the supplied oauth_token + * is invalid: + * - client_id: Stored client identifier. + * - expires: Stored expiration in unix timestamp. + * - scope: (optional) Stored scope values in space-separated string. + * + * @ingroup oauth2_section_7 + */ + public function getAccessToken($oauth_token); + + /** + * Store the supplied access token values to storage. + * + * We need to store access token data as we create and verify tokens. + * + * @param $oauth_token + * oauth_token to be stored. + * @param $client_id + * Client identifier to be stored. + * @param $user_id + * User identifier to be stored. + * @param $expires + * Expiration to be stored. + * @param $scope + * (optional) Scopes to be stored in space-separated string. + * + * @ingroup oauth2_section_4 + */ + public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = NULL); + + /** + * Check restricted grant types of corresponding client identifier. + * + * If you want to restrict clients to certain grant types, override this + * function. + * + * @param $client_id + * Client identifier to be check with. + * @param $grant_type + * Grant type to be check with, would be one of the values contained in + * OAuth2::GRANT_TYPE_REGEXP. + * + * @return + * TRUE if the grant type is supported by this client identifier, and + * FALSE if it isn't. + * + * @ingroup oauth2_section_4 + */ + public function checkRestrictedGrantType($client_id, $grant_type); +} \ No newline at end of file diff --git a/lib/classes/OAuth2.php b/lib/classes/OAuth2.php new file mode 100644 index 0000000..2773b81 --- /dev/null +++ b/lib/classes/OAuth2.php @@ -0,0 +1,1011 @@ + + * @license http://www.gnu.org/licenses/gpl.html + * @link http://justin-greer.com + */ +class OAuth2 { + + /** + * Array of persistent variables stored. + */ + protected $conf = array(); + + /** + * Storage engine for authentication server + * + * @var IOAuth2Storage + */ + protected $storage; + + /** + * Keep track of the old refresh token. So we can unset + * the old refresh tokens when a new one is issued. + * + * @var string + */ + protected $oldRefreshToken; + + /** + * Default values for configuration options. + */ + const DEFAULT_ACCESS_TOKEN_LIFETIME = 3600; + const DEFAULT_REFRESH_TOKEN_LIFETIME = 1209600; + const DEFAULT_AUTH_CODE_LIFETIME = 30; + const DEFAULT_WWW_REALM = 'Service'; + + /** + * Configurable options. + */ + const CONFIG_ACCESS_LIFETIME = 'access_token_lifetime'; + const CONFIG_REFRESH_LIFETIME = 'refresh_token_lifetime'; + const CONFIG_AUTH_LIFETIME = 'auth_code_lifetime'; + const CONFIG_SUPPORTED_SCOPES = 'supported_scopes'; + const CONFIG_TOKEN_TYPE = 'token_type'; + const CONFIG_WWW_REALM = 'realm'; + const CONFIG_ENFORCE_INPUT_REDIRECT = 'enforce_redirect'; + const CONFIG_ENFORCE_STATE = 'enforce_state'; + const CLIENT_ID_REGEXP = '/^[a-z0-9-_]{3,40}$/i'; + const TOKEN_PARAM_NAME = 'access_token'; + const TOKEN_BEARER_HEADER_NAME = 'Bearer'; + const RESPONSE_TYPE_AUTH_CODE = 'code'; + const RESPONSE_TYPE_ACCESS_TOKEN = 'token'; + + /** + * Grant Types + */ + const GRANT_TYPE_AUTH_CODE = 'authorization_code'; + const GRANT_TYPE_IMPLICIT = 'code'; + const GRANT_TYPE_USER_CREDENTIALS = 'password'; + const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials'; + const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token'; + const GRANT_TYPE_EXTENSIONS = 'extensions'; + const GRANT_TYPE_REGEXP = '#^(authorization_code|token|password|client_credentials|refresh_token|http://.*)$#'; + const TOKEN_TYPE_BEARER = 'bearer'; + + /** + * Not Supported Yet + */ + const TOKEN_TYPE_MAC = 'mac'; + + /** + * HTTP Status Code + */ + const HTTP_FOUND = '302 Found'; + const HTTP_BAD_REQUEST = '400 Bad Request'; + const HTTP_UNAUTHORIZED = '401 Unauthorized'; + const HTTP_FORBIDDEN = '403 Forbidden'; + const HTTP_UNAVAILABLE = '503 Service Unavailable'; + + const ERROR_INVALID_REQUEST = 'invalid_request'; + + /** + * The client identifier provided is invalid. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + */ + const ERROR_INVALID_CLIENT = 'invalid_client'; + + /** + * The client is not authorized to use the requested response type. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2.2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + */ + const ERROR_UNAUTHORIZED_CLIENT = 'unauthorized_client'; + + /** + * The redirection URI provided does not match a pre-registered value. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-3.1.2.4 + */ + const ERROR_REDIRECT_URI_MISMATCH = 'redirect_uri_mismatch'; + + /** + * The end-user or authorization server denied the request. + * This could be returned, for example, if the resource owner decides to reject + * access to the client at a later point. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2.2.1 + */ + const ERROR_USER_DENIED = 'access_denied'; + + /** + * The requested response type is not supported by the authorization server. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2.2.1 + */ + const ERROR_UNSUPPORTED_RESPONSE_TYPE = 'unsupported_response_type'; + + /** + * The requested scope is invalid, unknown, or malformed. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2.2.1 + */ + const ERROR_INVALID_SCOPE = 'invalid_scope'; + + /** + * The provided authorization grant is invalid, expired, + * revoked, does not match the redirection URI used in the + * authorization request, or was issued to another client. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + */ + const ERROR_INVALID_GRANT = 'invalid_grant'; + + /** + * The authorization grant is not supported by the authorization server. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + */ + const ERROR_UNSUPPORTED_GRANT_TYPE = 'unsupported_grant_type'; + + /** + * The request requires higher privileges than provided by the access token. + * The resource server SHOULD respond with the HTTP 403 (Forbidden) status + * code and MAY include the "scope" attribute with the scope necessary to + * access the protected resource. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2.2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + */ + const ERROR_INSUFFICIENT_SCOPE = 'invalid_scope'; + + /** + * @} + */ + + /** + * Creates an OAuth2.0 server-side instance. + * + * @param $config - An associative array as below of config options. See CONFIG_* constants. + */ + public function __construct(IOAuth2Storage $storage, $config = array()) { + $this->storage = $storage; + + // Configuration options + $this->setDefaultOptions(); + foreach ( $config as $name => $value ) { + $this->setVariable($name, $value); + } + } + + /** + * Default configuration options are specified here. + */ + protected function setDefaultOptions() { + $this->conf = array( + self::CONFIG_ACCESS_LIFETIME => self::DEFAULT_ACCESS_TOKEN_LIFETIME, + self::CONFIG_REFRESH_LIFETIME => self::DEFAULT_REFRESH_TOKEN_LIFETIME, + self::CONFIG_AUTH_LIFETIME => self::DEFAULT_AUTH_CODE_LIFETIME, + self::CONFIG_WWW_REALM => self::DEFAULT_WWW_REALM, + self::CONFIG_TOKEN_TYPE => self::TOKEN_TYPE_BEARER, + self::CONFIG_ENFORCE_INPUT_REDIRECT => FALSE, + self::CONFIG_ENFORCE_STATE => TRUE, + self::CONFIG_SUPPORTED_SCOPES => array() // This is expected to be passed in on construction. Scopes can be an aribitrary string. + ); + } + + /** + * Returns a persistent variable. + * + * @param $name + * The name of the variable to return. + * @param $default + * The default value to use if this variable has never been set. + * + * @return + * The value of the variable. + */ + public function getVariable($name, $default = NULL) { + $name = strtolower($name); + + return isset($this->conf[$name]) ? $this->conf[$name] : $default; + } + + /** + * Sets a persistent variable. + * + * @param $name + * The name of the variable to set. + * @param $value + * The value to set. + */ + public function setVariable($name, $value) { + $name = strtolower($name); + + $this->conf[$name] = $value; + return $this; + } + + // Resource protecting (Section 5). + + + /** + * Check that a valid access token has been provided. + * The token is returned (as an associative array) if valid. + * + * The scope parameter defines any required scope that the token must have. + * If a scope param is provided and the token does not have the required + * scope, we bounce the request. + * + * Some implementations may choose to return a subset of the protected + * resource (i.e. "public" data) if the user has not provided an access + * token or if the access token is invalid or expired. + * + * The IETF spec says that we should send a 401 Unauthorized header and + * bail immediately so that's what the defaults are set to. You can catch + * the exception thrown and behave differently if you like (log errors, allow + * public access for missing tokens, etc) + * + * @param $scope + * A space-separated string of required scope(s), if you want to check + * for scope. + * @return array + * Token + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-7 + * + * @ingroup oauth2_section_7 + */ + public function verifyAccessToken($token_param, $scope = NULL) { + $tokenType = $this->getVariable(self::CONFIG_TOKEN_TYPE); + $realm = $this->getVariable(self::CONFIG_WWW_REALM); + + if (!$token_param) { // Access token was not provided + throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.', $scope); + } + + // Get the stored token data (from the implementing subclass) + $token = $this->storage->getAccessToken($token_param); + + if ($token === NULL) { + throw new OAuth2AuthenticateException(self::HTTP_UNAUTHORIZED, $tokenType, $realm, self::ERROR_INVALID_GRANT, 'The access token provided is invalid.', $scope); + } + + // Check we have a well formed token + if (!isset($token["expires"]) || !isset($token["client_id"])) { + throw new OAuth2AuthenticateException(self::HTTP_UNAUTHORIZED, $tokenType, $realm, self::ERROR_INVALID_GRANT, 'Malformed token (missing "expires" or "client_id")', $scope); + } + + // Check token expiration (expires is a mandatory paramter) + if (isset($token["expires"]) && time() > $token["expires"]) { + throw new OAuth2AuthenticateException(self::HTTP_UNAUTHORIZED, $tokenType, $realm, self::ERROR_INVALID_GRANT, 'The access token provided has expired.', $scope); + } + + // Check scope, if provided + // If token doesn't have a scope, it's NULL/empty, or it's insufficient, then throw an error + if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->checkScope($scope, $token["scope"]))) { + throw new OAuth2AuthenticateException(self::HTTP_FORBIDDEN, $tokenType, $realm, self::ERROR_INSUFFICIENT_SCOPE, 'The request requires higher privileges than provided by the access token.', $scope); + } + + return $token; + } + + /** + * This is a convenience function that can be used to get the token, which can then + * be passed to verifyAccessToken(). The constraints specified by the draft are + * attempted to be adheared to in this method. + * + * As per the Bearer spec (draft 8, section 2) - there are three ways for a client + * to specify the bearer token, in order of preference: Authorization Header, + * POST and GET. + * + * NB: Resource servers MUST accept tokens via the Authorization scheme + * (http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08#section-2). + * + * @todo Should we enforce TLS/SSL in this function? + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08#section-2.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08#section-2.2 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08#section-2.3 + * + * Old Android version bug (at least with version 2.2) + * @see http://code.google.com/p/android/issues/detail?id=6684 + * + * We don't want to test this functionality as it relies on superglobals and headers: + * @codeCoverageIgnoreStart + */ + public function getBearerToken() { + if (isset($_SERVER['HTTP_AUTHORIZATION'])) { + $headers = trim($_SERVER["HTTP_AUTHORIZATION"]); + } elseif (function_exists('apache_request_headers')) { + $requestHeaders = apache_request_headers(); + + // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization) + $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders)); + + if (isset($requestHeaders['Authorization'])) { + $headers = trim($requestHeaders['Authorization']); + } + } + + $tokenType = $this->getVariable(self::CONFIG_TOKEN_TYPE); + $realm = $this->getVariable(self::CONFIG_WWW_REALM); + + // Check that exactly one method was used + $methodsUsed = !empty($headers) + isset($_GET[self::TOKEN_PARAM_NAME]) + isset($_POST[self::TOKEN_PARAM_NAME]); + if ($methodsUsed > 1) { + throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'Only one method may be used to authenticate at a time (Auth header, GET or POST).'); + } elseif ($methodsUsed == 0) { + throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'The access token was not found.'); + } + + // HEADER: Get the access token from the header + if (!empty($headers)) { + if (!preg_match('/' . self::TOKEN_BEARER_HEADER_NAME . '\s(\S+)/', $headers, $matches)) { + throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'Malformed auth header'); + } + + return $matches[1]; + } + + // POST: Get the token from POST data + if (isset($_POST[self::TOKEN_PARAM_NAME])) { + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'When putting the token in the body, the method must be POST.'); + } + + // IETF specifies content-type. NB: Not all webservers populate this _SERVER variable + if (isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] != 'application/x-www-form-urlencoded') { + throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'The content type for POST requests must be "application/x-www-form-urlencoded"'); + } + + return $_POST[self::TOKEN_PARAM_NAME]; + } + + // GET method + return $_GET[self::TOKEN_PARAM_NAME]; + } + + /** @codeCoverageIgnoreEnd */ + + /** + * Check if everything in required scope is contained in available scope. + * + * @param $required_scope + * Required scope to be check with. + * + * @return + * TRUE if everything in required scope is contained in available scope, + * and False if it isn't. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-7 + * + * @ingroup oauth2_section_7 + */ + private function checkScope($required_scope, $available_scope) { + // The required scope should match or be a subset of the available scope + if (!is_array($required_scope)) { + $required_scope = explode(' ', trim($required_scope)); + } + + if (!is_array($available_scope)) { + $available_scope = explode(' ', trim($available_scope)); + } + + return (count(array_diff($required_scope, $available_scope)) == 0); + } + + // Access token granting (Section 4). + + + /** + * Grant or deny a requested access token. + * This would be called from the "/token" endpoint as defined in the spec. + * Obviously, you can call your endpoint whatever you want. + * + * @param $inputData - The draft specifies that the parameters should be + * retrieved from POST, but you can override to whatever method you like. + * @throws OAuth2ServerException + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-21#section-10.6 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-21#section-4.1.3 + * + * @ingroup oauth2_section_4 + */ + public function grantAccessToken(array $inputData = NULL, array $authHeaders = NULL) { + $filters = array( + "grant_type" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => self::GRANT_TYPE_REGEXP), "flags" => FILTER_REQUIRE_SCALAR), + "scope" => array("flags" => FILTER_REQUIRE_SCALAR), + "code" => array("flags" => FILTER_REQUIRE_SCALAR), + "redirect_uri" => array("filter" => FILTER_SANITIZE_URL), + "username" => array("flags" => FILTER_REQUIRE_SCALAR), + "password" => array("flags" => FILTER_REQUIRE_SCALAR), + "refresh_token" => array("flags" => FILTER_REQUIRE_SCALAR), + ); + + // Input data by default can be either POST or GET + if (!isset($inputData)) { + $inputData = ($_SERVER['REQUEST_METHOD'] == 'POST') ? $_POST : $_GET; + } + + // Basic authorization header + $authHeaders = isset($authHeaders) ? $authHeaders : $this->getAuthorizationHeader(); + + // Filter input data + $input = $inputData; + + // Grant Type must be specified. + if (!$input["grant_type"]) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing'); + } + + // Authorize the client + $client = $this->getClientCredentials($inputData, $authHeaders); + + if ($this->storage->checkClientCredentials($client[0], $client[1]) === FALSE) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client credentials are invalid'); + } + + if (!$this->storage->checkRestrictedGrantType($client[0], $input["grant_type"])) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNAUTHORIZED_CLIENT, 'The grant type is unauthorized for this client_id'); + } + + // Do the granting + switch ($input["grant_type"]) { + case self::GRANT_TYPE_AUTH_CODE: + if (!($this->storage instanceof IOAuth2GrantCode)) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + } + + if (!$input["code"]) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Missing parameter. "code" is required'); + } + + if ($this->getVariable(self::CONFIG_ENFORCE_INPUT_REDIRECT) && !$input["redirect_uri"]) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, "The redirect URI parameter is required."); + } + + $stored = $this->storage->getAuthCode($input["code"]); + + // Check the code exists + if ($stored === NULL || $client[0] != $stored["client_id"]) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "Refresh token doesn't exist or is invalid for the client"); + } + + // Validate the redirect URI. If a redirect URI has been provided on input, it must be validated + if ($input["redirect_uri"] && !$this->validateRedirectUri(urldecode($input["redirect_uri"]), $stored["redirect_uri"])) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, "The redirect URI is missing or do not match"); + } + + if ($stored["expires"] < time()) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "The authorization code has expired"); + } + break; + + case self::GRANT_TYPE_USER_CREDENTIALS: + if (!($this->storage instanceof IOAuth2GrantUser)) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + } + + if (!$input["username"] || !$input["password"]) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Missing parameters. "username" and "password" required'); + } + + $stored = $this->storage->checkUserCredentials($client[0], $input["username"], $input["password"]); + + if ($stored === FALSE) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT); + } + break; + + case self::GRANT_TYPE_CLIENT_CREDENTIALS: + if (!($this->storage instanceof IOAuth2GrantClient)) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + } + + if (empty($client[1])) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client_secret is mandatory for the "client_credentials" grant type'); + } + // NB: We don't need to check for $stored==false, because it was checked above already + $stored = $this->storage->checkClientCredentialsGrant($client[0], $client[1]); + break; + + case self::GRANT_TYPE_REFRESH_TOKEN: + if (!($this->storage instanceof IOAuth2RefreshTokens)) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + } + + if (!$input["refresh_token"]) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'No "refresh_token" parameter found'); + } + + $stored = $this->storage->getRefreshToken($input["refresh_token"]); + + if ($stored === NULL || $client[0] != $stored["client_id"]) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, 'Invalid refresh token'); + } + + if ($stored["expires"] < time()) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, 'Refresh token has expired'); + } + + // store the refresh token locally so we can delete it when a new refresh token is generated + $this->oldRefreshToken = $stored["refresh_token"]; + break; + + case self::GRANT_TYPE_IMPLICIT: + /* TODO: NOT YET IMPLEMENTED */ + throw new OAuth2ServerException('501 Not Implemented', 'This OAuth2 library is not yet complete. This functionality is not implemented yet.'); + if (!($this->storage instanceof IOAuth2GrantImplicit)) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + } + + break; + + // Extended grant types: + case filter_var($input["grant_type"], FILTER_VALIDATE_URL): + if (!($this->storage instanceof IOAuth2GrantExtension)) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + } + $uri = filter_var($input["grant_type"], FILTER_VALIDATE_URL); + $stored = $this->storage->checkGrantExtension($uri, $inputData, $authHeaders); + + if ($stored === FALSE) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT); + } + break; + + default : + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing'); + } + + if (!isset($stored["scope"])) { + $stored["scope"] = NULL; + } + + // Check scope, if provided + if ($input["scope"] && (!is_array($stored) || !isset($stored["scope"]) || !$this->checkScope($input["scope"], $stored["scope"]))) { + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_SCOPE, 'An unsupported scope was requested.'); + } + + $user_id = isset($stored['user_id']) ? $stored['user_id'] : null; + $token = $this->createAccessToken($client[0], $user_id, $stored['scope']); + + // Send response + $this->sendJsonHeaders(); + echo json_encode($token); + } + + /** + * Internal function used to get the client credentials from HTTP basic + * auth or POST data. + * + * According to the spec (draft 20), the client_id can be provided in + * the Basic Authorization header (recommended) or via GET/POST. + * + * @return + * A list containing the client identifier and password, for example + * @code + * return array( + * CLIENT_ID, + * CLIENT_SECRET + * ); + * @endcode + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-2.4.1 + * + * @ingroup oauth2_section_2 + */ + protected function getClientCredentials(array $inputData, array $authHeaders) { + + // Basic Authentication is used + if (!empty($authHeaders['PHP_AUTH_USER'])) { + return array($authHeaders['PHP_AUTH_USER'], $authHeaders['PHP_AUTH_PW']); + } elseif (empty($inputData['client_id'])) { // No credentials were specified + throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'Client id was not found in the headers or body'); + } else { + // This method is not recommended, but is supported by specification + return array($inputData['client_id'], $inputData['client_secret']); + } + } + + // End-user/client Authorization (Section 2 of IETF Draft). + + + /** + * Pull the authorization request data out of the HTTP request. + * - The redirect_uri is OPTIONAL as per draft 20. But your implementation can enforce it + * by setting CONFIG_ENFORCE_INPUT_REDIRECT to true. + * - The state is OPTIONAL but recommended to enforce CSRF. Draft 21 states, however, that + * CSRF protection is MANDATORY. You can enforce this by setting the CONFIG_ENFORCE_STATE to true. + * + * @param $inputData - The draft specifies that the parameters should be + * retrieved from GET, but you can override to whatever method you like. + * @return + * The authorization parameters so the authorization server can prompt + * the user for approval if valid. + * + * @throws OAuth2ServerException + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-21#section-10.12 + * + * @ingroup oauth2_section_3 + */ + public function getAuthorizeParams($inputData) { + + if (!isset($inputData)) { + $inputData = $_GET; + } + $input = $inputData; + + // THROW ERROR IF client_id IS MISSING + if (!$input["client_id"]) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'Client id required')); + echo $error; + exit; + } + + // GET CLIENT DETAILS + $stored = $this->storage->getClientDetails($input["client_id"]); + //print_r($inputData); + + if ($stored === FALSE) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'Client not found or not provided')); + echo $error; + exit; + } + + // Make sure a valid redirect_uri was supplied. If specified, it must match the stored URI. + // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-3.1.2 + // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.2.1 + // @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.2.2.1 + if (!$input["redirect_uri"] && !$stored["redirect_uri"]) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'redirect_uri is require')); + echo $error; + exit; + } + if ($this->getVariable(self::CONFIG_ENFORCE_INPUT_REDIRECT) && !$input["redirect_uri"]) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'redirect_uri is require by the OAuth API')); + echo $error; + exit; + } + // Only need to validate if redirect_uri provided on input and stored. + if ($stored["redirect_uri"] && $input["redirect_uri"] && !$this->validateRedirectUri($input["redirect_uri"], $stored["redirect_uri"])) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'The redirect_uri is missing or does not match allowed uri')); + echo $error; + exit; + } + + // Select the redirect URI + $input["redirect_uri"] = isset($input["redirect_uri"]) ? $input["redirect_uri"] : $stored["redirect_uri"]; + + // type and client_id are required + if (!$input["response_type"]) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'Invalid or missing response_type')); + echo $error; + exit; + } + + if ($input['response_type'] != self::RESPONSE_TYPE_AUTH_CODE && $input['response_type'] != self::RESPONSE_TYPE_ACCESS_TOKEN) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'response_type invalid')); + echo $error; + exit; + } + + // Validate that the requested scope is supported + if ($input["scope"] && !$this->checkScope($input["scope"], $this->getVariable(self::CONFIG_SUPPORTED_SCOPES))) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'Unsuportted scope')); + echo $error; + exit; + } + + // Validate state parameter exists (if configured to enforce this) + if ($this->getVariable(self::CONFIG_ENFORCE_STATE) && !$input["state"]) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => 'state is required')); + echo $error; + exit; + } + + // Return retrieved client details together with input + return ($input + $stored); + } + + /** + * Redirect the user appropriately after approval. + * + * After the user has approved or denied the access request the + * authorization server should call this function to redirect the user + * appropriately. + * + * @param $is_authorized + * TRUE or FALSE depending on whether the user authorized the access. + * @param $user_id + * Identifier of user who authorized the client + * @param $params + * An associative array as below: + * - response_type: The requested response: an access token, an + * authorization code, or both. + * - client_id: The client identifier as described in Section 2. + * - redirect_uri: An absolute URI to which the authorization server + * will redirect the user-agent to when the end-user authorization + * step is completed. + * - scope: (optional) The scope of the access request expressed as a + * list of space-delimited strings. + * - state: (optional) An opaque value used by the client to maintain + * state between the request and callback. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4 + * + * @ingroup oauth2_section_4 + */ + public function finishClientAuthorization($is_authorized, $user_id = NULL, $params = array()) { + + // WTF - NEED BREAK POINT HERE TO BEGUG + // + // + list($redirect_uri, $result) = $this->getAuthResult($is_authorized, $user_id, $params); + + //print_r($result); + $this->doRedirectUriCallback($redirect_uri, $result); + } + + // same params as above + public function getAuthResult($is_authorized, $user_id = NULL, $params = array()) { + + // We repeat this, because we need to re-validate. In theory, this could be POSTed + // by a 3rd-party (because we are not internally enforcing NONCEs, etc) + $params = $this->getAuthorizeParams($params); + + $params += array('scope' => NULL, 'state' => NULL); + extract($params); + + if ($state !== NULL) { + $result["query"]["state"] = $state; + } + + if ($is_authorized === FALSE) { + throw new OAuth2RedirectException($redirect_uri, self::ERROR_USER_DENIED, "The user denied access to your application", $state); + } else { + if ($response_type == self::RESPONSE_TYPE_AUTH_CODE) { + $result["query"]["code"] = $this->createAuthCode($client_id, $user_id, $redirect_uri, $scope); + } elseif ($response_type == self::RESPONSE_TYPE_ACCESS_TOKEN) { + $result["fragment"] = $this->createAccessToken($client_id, $user_id, $scope); + } + } + + return array($redirect_uri, $result); + } + + // Other/utility functions. + + + /** + * Redirect the user agent. + * + * Handle both redirect for success or error response. + * + * @param $redirect_uri + * An absolute URI to which the authorization server will redirect + * the user-agent to when the end-user authorization step is completed. + * @param $params + * Parameters to be pass though buildUri(). + * + * @ingroup oauth2_section_4 + */ + private function doRedirectUriCallback($redirect_uri, $params) { + header("HTTP/1.1 " . self::HTTP_FOUND); + header("Location: " . $this->buildUri($redirect_uri, $params)); + exit(); + } + + /** + * Build the absolute URI based on supplied URI and parameters. + * + * @param $uri + * An absolute URI. + * @param $params + * Parameters to be append as GET. + * + * @return + * An absolute URI with supplied parameters. + * + * @ingroup oauth2_section_4 + */ + private function buildUri($uri, $params) { + $parse_url = parse_url($uri); + + // Add our params to the parsed uri + foreach ( $params as $k => $v ) { + if (isset($parse_url[$k])) { + $parse_url[$k] .= "&" . http_build_query($v); + } else { + $parse_url[$k] = http_build_query($v); + } + } + + // Put humpty dumpty back together + return + ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "") + . ((isset($parse_url["user"])) ? $parse_url["user"] + . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "") + . ((isset($parse_url["host"])) ? $parse_url["host"] : "") + . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "") + . ((isset($parse_url["path"])) ? $parse_url["path"] : "") + . ((isset($parse_url["query"])) ? "?" . $parse_url["query"] : "") + . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "") + ; + } + + /** + * Handle the creation of access token, also issue refresh token if support. + * + * This belongs in a separate factory, but to keep it simple, I'm just + * keeping it here. + * + * @param $client_id + * Client identifier related to the access token. + * @param $scope + * (optional) Scopes to be stored in space-separated string. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5 + * @ingroup oauth2_section_5 + */ + protected function createAccessToken($client_id, $user_id, $scope = NULL) { + + $token = array( + "access_token" => $this->genAccessToken(), + "expires_in" => $this->getVariable(self::CONFIG_ACCESS_LIFETIME), + "token_type" => $this->getVariable(self::CONFIG_TOKEN_TYPE), + "scope" => $scope + ); + + $this->storage->setAccessToken($token["access_token"], $client_id, $user_id, time() + $this->getVariable(self::CONFIG_ACCESS_LIFETIME), $scope); + + // Issue a refresh token also, if we support them + if ($this->storage instanceof IOAuth2RefreshTokens) { + $token["refresh_token"] = $this->genAccessToken(); + $this->storage->setRefreshToken($token["refresh_token"], $client_id, $user_id, time() + $this->getVariable(self::CONFIG_REFRESH_LIFETIME), $scope); + + // If we've granted a new refresh token, expire the old one + if ($this->oldRefreshToken) { + $this->storage->unsetRefreshToken($this->oldRefreshToken); + unset($this->oldRefreshToken); + } + } + + return $token; + } + + /** + * Handle the creation of auth code. + * + * This belongs in a separate factory, but to keep it simple, I'm just + * keeping it here. + * + * @param $client_id + * Client identifier related to the access token. + * @param $redirect_uri + * An absolute URI to which the authorization server will redirect the + * user-agent to when the end-user authorization step is completed. + * @param $scope + * (optional) Scopes to be stored in space-separated string. + * + * @ingroup oauth2_section_4 + */ + private function createAuthCode($client_id, $user_id, $redirect_uri, $scope = NULL) { + $code = $this->genAuthCode(); + $this->storage->setAuthCode($code, $client_id, $user_id, $redirect_uri, time() + $this->getVariable(self::CONFIG_AUTH_LIFETIME), $scope); + return $code; + } + + /** + * Generates an unique access token. + * + * Implementing classes may want to override this function to implement + * other access token generation schemes. + * + * @return + * An unique access token. + * + * @ingroup oauth2_section_4 + * @see OAuth2::genAuthCode() + */ + protected function genAccessToken() { + $tokenLen = 40; + if (file_exists('/dev/urandom')) { // Get 100 bytes of random data + $randomData = file_get_contents('/dev/urandom', false, null, 0, 100) . uniqid(mt_rand(), true); + } else { + $randomData = mt_rand() . mt_rand() . mt_rand() . mt_rand() . microtime(true) . uniqid(mt_rand(), true); + } + return substr(hash('sha512', $randomData), 0, $tokenLen); + } + + /** + * Generates an unique auth code. + * + * Implementing classes may want to override this function to implement + * other auth code generation schemes. + * + * @return + * An unique auth code. + * + * @ingroup oauth2_section_4 + * @see OAuth2::genAccessToken() + */ + protected function genAuthCode() { + return $this->genAccessToken(); // let's reuse the same scheme for token generation + } + + /** + * Pull out the Authorization HTTP header and return it. + * According to draft 20, standard basic authorization is the only + * header variable required (this does not apply to extended grant types). + * + * Implementing classes may need to override this function if need be. + * + * @todo We may need to re-implement pulling out apache headers to support extended grant types + * + * @return + * An array of the basic username and password provided. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-2.4.1 + * @ingroup oauth2_section_2 + */ + protected function getAuthorizationHeader() { + return array( + 'PHP_AUTH_USER' => isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '', + 'PHP_AUTH_PW' => isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '' + ); + } + + /** + * Send out HTTP headers for JSON. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + * + * @ingroup oauth2_section_5 + */ + private function sendJsonHeaders() { + if (php_sapi_name() === 'cli' || headers_sent()) { + return; + } + + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + } + + /** + * Internal method for validating redirect URI supplied + * @param string $inputUri + * @param string $storedUri + */ + protected function validateRedirectUri($inputUri, $storedUri) { + if (!$inputUri || !$storedUri) { + return false; // if either one is missing, assume INVALID + } + return strcasecmp(substr($inputUri, 0, strlen($storedUri)), $storedUri) === 0; + } +} diff --git a/lib/classes/OAuth2AuthenticateException.php b/lib/classes/OAuth2AuthenticateException.php new file mode 100644 index 0000000..b428da3 --- /dev/null +++ b/lib/classes/OAuth2AuthenticateException.php @@ -0,0 +1,55 @@ +errorData['scope'] = $scope; + } + + // Build header + $this->header = sprintf('WWW-Authenticate: %s realm="%s"', ucwords($tokenType), $realm); + foreach ( $this->errorData as $key => $value ) { + $this->header .= ", $key=\"$value\""; + } + } + + /** + * Send out HTTP headers for JSON. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + * + * @ingroup oauth2_section_5 + */ + protected function sendHeaders() { + header($this->header); + } +} \ No newline at end of file diff --git a/lib/classes/OAuth2Client.php b/lib/classes/OAuth2Client.php new file mode 100644 index 0000000..db3a68d --- /dev/null +++ b/lib/classes/OAuth2Client.php @@ -0,0 +1,692 @@ +. + * @author Update to draft v10 by Edison Wong . + * + * @sa Facebook PHP SDK. + */ +abstract class OAuth2Client { + + /** + * The default Cache Lifetime (in seconds). + */ + const DEFAULT_EXPIRES_IN = 3600; + + /** + * The default Base domain for the Cookie. + */ + const DEFAULT_BASE_DOMAIN = ''; + + /** + * Array of persistent variables stored. + */ + protected $conf = array(); + + /** + * Returns a persistent variable. + * + * To avoid problems, always use lower case for persistent variable names. + * + * @param $name + * The name of the variable to return. + * @param $default + * The default value to use if this variable has never been set. + * + * @return + * The value of the variable. + */ + public function getVariable($name, $default = NULL) { + return isset($this->conf[$name]) ? $this->conf[$name] : $default; + } + + /** + * Sets a persistent variable. + * + * To avoid problems, always use lower case for persistent variable names. + * + * @param $name + * The name of the variable to set. + * @param $value + * The value to set. + */ + public function setVariable($name, $value) { + $this->conf[$name] = $value; + return $this; + } + + // Stuff that should get overridden by subclasses. + // + // I don't want to make these abstract, because then subclasses would have + // to implement all of them, which is too much work. + // + // So they're just stubs. Override the ones you need. + + + /** + * Initialize a Drupal OAuth2.0 Application. + * + * @param $config + * An associative array as below: + * - base_uri: The base URI for the OAuth2.0 endpoints. + * - code: (optional) The authorization code. + * - username: (optional) The username. + * - password: (optional) The password. + * - client_id: (optional) The application ID. + * - client_secret: (optional) The application secret. + * - authorize_uri: (optional) The end-user authorization endpoint URI. + * - access_token_uri: (optional) The token endpoint URI. + * - services_uri: (optional) The services endpoint URI. + * - cookie_support: (optional) TRUE to enable cookie support. + * - base_domain: (optional) The domain for the cookie. + * - file_upload_support: (optional) TRUE if file uploads are enabled. + */ + public function __construct($config = array()) { + // We must set base_uri first. + $this->setVariable('base_uri', $config['base_uri']); + unset($config['base_uri']); + + // Use predefined OAuth2.0 params, or get it from $_REQUEST. + foreach ( array('code', 'username', 'password') as $name ) { + if (isset($config[$name])) { + $this->setVariable($name, $config[$name]); + } else if (isset($_REQUEST[$name]) && !empty($_REQUEST[$name])) { + $this->setVariable($name, $_REQUEST[$name]); + } + unset($config[$name]); + } + + // Endpoint URIs. + foreach ( array('authorize_uri', 'access_token_uri', 'services_uri') as $name ) { + if (isset($config[$name])) + if (substr($config[$name], 0, 4) == "http") { + $this->setVariable($name, $config[$name]); + } else { + $this->setVariable($name, $this->getVariable('base_uri') . $config[$name]); + } + unset($config[$name]); + } + + // Other else configurations. + foreach ( $config as $name => $value ) { + $this->setVariable($name, $value); + } + } + + /** + * Try to get session object from custom method. + * + * By default we generate session object based on access_token response, or + * if it is provided from server with $_REQUEST. For sure, if it is provided + * by server it should follow our session object format. + * + * Session object provided by server can ensure the correct expires and + * base_domain setup as predefined in server, also you may get more useful + * information for custom functionality, too. BTW, this may require for + * additional remote call overhead. + * + * You may wish to override this function with your custom version due to + * your own server-side implementation. + * + * @param $access_token + * (optional) A valid access token in associative array as below: + * - access_token: A valid access_token generated by OAuth2.0 + * authorization endpoint. + * - expires_in: (optional) A valid expires_in generated by OAuth2.0 + * authorization endpoint. + * - refresh_token: (optional) A valid refresh_token generated by OAuth2.0 + * authorization endpoint. + * - scope: (optional) A valid scope generated by OAuth2.0 + * authorization endpoint. + * + * @return + * A valid session object in associative array for setup cookie, and + * NULL if not able to generate it with custom method. + */ + protected function getSessionObject($access_token = NULL) { + $session = NULL; + + // Try generate local version of session cookie. + if (!empty($access_token) && isset($access_token['access_token'])) { + $session['access_token'] = $access_token['access_token']; + $session['base_domain'] = $this->getVariable('base_domain', self::DEFAULT_BASE_DOMAIN); + $session['expires'] = isset($access_token['expires_in']) ? time() + $access_token['expires_in'] : time() + $this->getVariable('expires_in', self::DEFAULT_EXPIRES_IN); + $session['refresh_token'] = isset($access_token['refresh_token']) ? $access_token['refresh_token'] : ''; + $session['scope'] = isset($access_token['scope']) ? $access_token['scope'] : ''; + $session['secret'] = md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid()))); + + // Provide our own signature. + $sig = self::generateSignature($session, $this->getVariable('client_secret')); + $session['sig'] = $sig; + } + + // Try loading session from $_REQUEST. + if (!$session && isset($_REQUEST['session'])) { + $session = json_decode(get_magic_quotes_gpc() ? stripslashes($_REQUEST['session']) : $_REQUEST['session'], TRUE); + } + + return $session; + } + + /** + * Make an API call. + * + * Support both OAuth2.0 or normal GET/POST API call, with relative + * or absolute URI. + * + * If no valid OAuth2.0 access token found in session object, this function + * will automatically switch as normal remote API call without "oauth_token" + * parameter. + * + * Assume server reply in JSON object and always decode during return. If + * you hope to issue a raw query, please use makeRequest(). + * + * @param $path + * The target path, relative to base_path/service_uri or an absolute URI. + * @param $method + * (optional) The HTTP method (default 'GET'). + * @param $params + * (optional The GET/POST parameters. + * + * @return + * The JSON decoded response object. + * + * @throws OAuth2Exception + */ + public function api($path, $method = 'GET', $params = array()) { + if (is_array($method) && empty($params)) { + $params = $method; + $method = 'GET'; + } + + // json_encode all params values that are not strings. + foreach ( $params as $key => $value ) { + if (!is_string($value)) { + $params[$key] = json_encode($value); + } + } + + $result = json_decode($this->makeOAuth2Request($this->getUri($path), $method, $params), TRUE); + + // Results are returned, errors are thrown. + if (is_array($result) && isset($result['error'])) { + $e = new OAuth2Exception($result); + switch ($e->getType()) { + // OAuth 2.0 Draft 10 style. + case 'invalid_token': + $this->setSession(NULL); + default : + $this->setSession(NULL); + } + throw $e; + } + return $result; + } + + // End stuff that should get overridden. + + + /** + * Default options for cURL. + */ + public static $CURL_OPTS = array( + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_HEADER => TRUE, + CURLOPT_TIMEOUT => 60, + CURLOPT_USERAGENT => 'oauth2-draft-v10', + CURLOPT_HTTPHEADER => array("Accept: application/json") + ); + + /** + * Set the Session. + * + * @param $session + * (optional) The session object to be set. NULL if hope to frush existing + * session object. + * @param $write_cookie + * (optional) TRUE if a cookie should be written. This value is ignored + * if cookie support has been disabled. + * + * @return + * The current OAuth2.0 client-side instance. + */ + public function setSession($session = NULL, $write_cookie = TRUE) { + $this->setVariable('_session', $this->validateSessionObject($session)); + $this->setVariable('_session_loaded', TRUE); + if ($write_cookie) { + $this->setCookieFromSession($this->getVariable('_session')); + } + return $this; + } + + /** + * Get the session object. + * + * This will automatically look for a signed session via custom method, + * OAuth2.0 grant type with authorization_code, OAuth2.0 grant type with + * password, or cookie that we had already setup. + * + * @return + * The valid session object with OAuth2.0 infomration, and NULL if not + * able to discover any cases. + */ + public function getSession() { + if (!$this->getVariable('_session_loaded')) { + $session = NULL; + $write_cookie = TRUE; + + // Try obtain login session by custom method. + $session = $this->getSessionObject(NULL); + $session = $this->validateSessionObject($session); + + // grant_type == authorization_code. + if (!$session && $this->getVariable('code')) { + $access_token = $this->getAccessTokenFromAuthorizationCode($this->getVariable('code')); + $session = $this->getSessionObject($access_token); + $session = $this->validateSessionObject($session); + } + + // grant_type == password. + if (!$session && $this->getVariable('username') && $this->getVariable('password')) { + $access_token = $this->getAccessTokenFromPassword($this->getVariable('username'), $this->getVariable('password')); + $session = $this->getSessionObject($access_token); + $session = $this->validateSessionObject($session); + } + + // Try loading session from cookie if necessary. + if (!$session && $this->getVariable('cookie_support')) { + $cookie_name = $this->getSessionCookieName(); + if (isset($_COOKIE[$cookie_name])) { + $session = array(); + parse_str(trim(get_magic_quotes_gpc() ? stripslashes($_COOKIE[$cookie_name]) : $_COOKIE[$cookie_name], '"'), $session); + $session = $this->validateSessionObject($session); + // Write only if we need to delete a invalid session cookie. + $write_cookie = empty($session); + } + } + + $this->setSession($session, $write_cookie); + } + + return $this->getVariable('_session'); + } + + /** + * Gets an OAuth2.0 access token from session. + * + * This will trigger getSession() and so we MUST initialize with required + * configuration. + * + * @return + * The valid OAuth2.0 access token, and NULL if not exists in session. + */ + public function getAccessToken() { + $session = $this->getSession(); + return isset($session['access_token']) ? $session['access_token'] : NULL; + } + + /** + * Get access token from OAuth2.0 token endpoint with authorization code. + * + * This function will only be activated if both access token URI, client + * identifier and client secret are setup correctly. + * + * @param $code + * Authorization code issued by authorization server's authorization + * endpoint. + * + * @return + * A valid OAuth2.0 JSON decoded access token in associative array, and + * NULL if not enough parameters or JSON decode failed. + */ + private function getAccessTokenFromAuthorizationCode($code) { + if ($this->getVariable('access_token_uri') && $this->getVariable('client_id') && $this->getVariable('client_secret')) { + return json_decode($this->makeRequest( + $this->getVariable('access_token_uri'), + 'POST', + array( + 'grant_type' => 'authorization_code', + 'client_id' => $this->getVariable('client_id'), + 'client_secret' => $this->getVariable('client_secret'), + 'code' => $code, + 'redirect_uri' => $this->getCurrentUri() + ) + ), TRUE); + } + return NULL; + } + + /** + * Get access token from OAuth2.0 token endpoint with basic user + * credentials. + * + * This function will only be activated if both username and password + * are setup correctly. + * + * @param $username + * Username to be check with. + * @param $password + * Password to be check with. + * + * @return + * A valid OAuth2.0 JSON decoded access token in associative array, and + * NULL if not enough parameters or JSON decode failed. + */ + private function getAccessTokenFromPassword($username, $password) { + if ($this->getVariable('access_token_uri') && $this->getVariable('client_id') && $this->getVariable('client_secret')) { + return json_decode($this->makeRequest( + $this->getVariable('access_token_uri'), + 'POST', + array( + 'grant_type' => 'password', + 'client_id' => $this->getVariable('client_id'), + 'client_secret' => $this->getVariable('client_secret'), + 'username' => $username, + 'password' => $password + ) + ), TRUE); + } + return NULL; + } + + /** + * Make an OAuth2.0 Request. + * + * Automatically append "oauth_token" in query parameters if not yet + * exists and able to discover a valid access token from session. Otherwise + * just ignore setup with "oauth_token" and handle the API call AS-IS, and + * so may issue a plain API call without OAuth2.0 protection. + * + * @param $path + * The target path, relative to base_path/service_uri or an absolute URI. + * @param $method + * (optional) The HTTP method (default 'GET'). + * @param $params + * (optional The GET/POST parameters. + * + * @return + * The JSON decoded response object. + * + * @throws OAuth2Exception + */ + protected function makeOAuth2Request($path, $method = 'GET', $params = array()) { + if ((!isset($params['oauth_token']) || empty($params['oauth_token'])) && $oauth_token = $this->getAccessToken()) { + $params['oauth_token'] = $oauth_token; + } + return $this->makeRequest($path, $method, $params); + } + + /** + * Makes an HTTP request. + * + * This method can be overriden by subclasses if developers want to do + * fancier things or use something other than cURL to make the request. + * + * @param $path + * The target path, relative to base_path/service_uri or an absolute URI. + * @param $method + * (optional) The HTTP method (default 'GET'). + * @param $params + * (optional The GET/POST parameters. + * @param $ch + * (optional) An initialized curl handle + * + * @return + * The JSON decoded response object. + */ + protected function makeRequest($path, $method = 'GET', $params = array(), $ch = NULL) { + if (!$ch) + $ch = curl_init(); + + $opts = self::$CURL_OPTS; + if ($params) { + switch ($method) { + case 'GET': + $path .= '?' . http_build_query($params, NULL, '&'); + break; + // Method override as we always do a POST. + default : + if ($this->getVariable('file_upload_support')) { + $opts[CURLOPT_POSTFIELDS] = $params; + } else { + $opts[CURLOPT_POSTFIELDS] = http_build_query($params, NULL, '&'); + } + } + } + $opts[CURLOPT_URL] = $path; + + // Disable the 'Expect: 100-continue' behaviour. This causes CURL to wait + // for 2 seconds if the server does not support this header. + if (isset($opts[CURLOPT_HTTPHEADER])) { + $existing_headers = $opts[CURLOPT_HTTPHEADER]; + $existing_headers[] = 'Expect:'; + $opts[CURLOPT_HTTPHEADER] = $existing_headers; + } else { + $opts[CURLOPT_HTTPHEADER] = array('Expect:'); + } + + curl_setopt_array($ch, $opts); + $result = curl_exec($ch); + + if (curl_errno($ch) == 60) { // CURLE_SSL_CACERT + error_log('Invalid or no certificate authority found, using bundled information'); + curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/fb_ca_chain_bundle.crt'); + $result = curl_exec($ch); + } + + if ($result === FALSE) { + $e = new OAuth2Exception(array('code' => curl_errno($ch), 'message' => curl_error($ch))); + curl_close($ch); + throw $e; + } + curl_close($ch); + + // Split the HTTP response into header and body. + list($headers, $body) = explode("\r\n\r\n", $result); + $headers = explode("\r\n", $headers); + + // We catch HTTP/1.1 4xx or HTTP/1.1 5xx error response. + if (strpos($headers[0], 'HTTP/1.1 4') !== FALSE || strpos($headers[0], 'HTTP/1.1 5') !== FALSE) { + $result = array('code' => 0, 'message' => ''); + + if (preg_match('/^HTTP\/1.1 ([0-9]{3,3}) (.*)$/', $headers[0], $matches)) { + $result['code'] = $matches[1]; + $result['message'] = $matches[2]; + } + + // In case retrun with WWW-Authenticate replace the description. + foreach ( $headers as $header ) { + if (preg_match("/^WWW-Authenticate:.*error='(.*)'/", $header, $matches)) { + $result['error'] = $matches[1]; + } + } + + return json_encode($result); + } + + return $body; + } + + /** + * The name of the cookie that contains the session object. + * + * @return + * The cookie name. + */ + private function getSessionCookieName() { + return 'oauth2_' . $this->getVariable('client_id'); + } + + /** + * Set a JS Cookie based on the _passed in_ session. + * + * It does not use the currently stored session - you need to explicitly + * pass it in. + * + * @param $session + * The session to use for setting the cookie. + */ + protected function setCookieFromSession($session = NULL) { + if (!$this->getVariable('cookie_support')) + return; + + $cookie_name = $this->getSessionCookieName(); + $value = 'deleted'; + $expires = time() - 3600; + $base_domain = $this->getVariable('base_domain', self::DEFAULT_BASE_DOMAIN); + if ($session) { + $value = '"' . http_build_query($session, NULL, '&') . '"'; + $base_domain = isset($session['base_domain']) ? $session['base_domain'] : $base_domain; + $expires = isset($session['expires']) ? $session['expires'] : time() + $this->getVariable('expires_in', self::DEFAULT_EXPIRES_IN); + } + + // Prepend dot if a domain is found. + if ($base_domain) + $base_domain = '.' . $base_domain; + + // If an existing cookie is not set, we dont need to delete it. + if ($value == 'deleted' && empty($_COOKIE[$cookie_name])) + return; + + if (headers_sent()) + error_log('Could not set cookie. Headers already sent.'); + else + setcookie($cookie_name, $value, $expires, '/', $base_domain); + } + + /** + * Validates a session_version = 3 style session object. + * + * @param $session + * The session object. + * + * @return + * The session object if it validates, NULL otherwise. + */ + protected function validateSessionObject($session) { + // Make sure some essential fields exist. + if (is_array($session) && isset($session['access_token']) && isset($session['sig'])) { + // Validate the signature. + $session_without_sig = $session; + unset($session_without_sig['sig']); + + $expected_sig = self::generateSignature($session_without_sig, $this->getVariable('client_secret')); + + if ($session['sig'] != $expected_sig) { + error_log('Got invalid session signature in cookie.'); + $session = NULL; + } + } else { + $session = NULL; + } + return $session; + } + + /** + * Since $_SERVER['REQUEST_URI'] is only available on Apache, we + * generate an equivalent using other environment variables. + */ + function getRequestUri() { + if (isset($_SERVER['REQUEST_URI'])) { + $uri = $_SERVER['REQUEST_URI']; + } else { + if (isset($_SERVER['argv'])) { + $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0]; + } elseif (isset($_SERVER['QUERY_STRING'])) { + $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING']; + } else { + $uri = $_SERVER['SCRIPT_NAME']; + } + } + // Prevent multiple slashes to avoid cross site requests via the Form API. + $uri = '/' . ltrim($uri, '/'); + + return $uri; + } + + /** + * Returns the Current URL. + * + * @return + * The current URL. + */ + protected function getCurrentUri() { + $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https://' : 'http://'; + $current_uri = $protocol . $_SERVER['HTTP_HOST'] . $this->getRequestUri(); + $parts = parse_url($current_uri); + + $query = ''; + if (!empty($parts['query'])) { + $params = array(); + parse_str($parts['query'], $params); + $params = array_filter($params); + if (!empty($params)) { + $query = '?' . http_build_query($params, NULL, '&'); + } + } + + // Use port if non default. + $port = ''; + if (isset($parts['port']) && (($protocol === 'http://' && $parts['port'] !== 80) || ($protocol === 'https://' && $parts['port'] !== 443))) { + $port = ':' . $parts['port']; + } + + + // Rebuild. + return $protocol . $parts['host'] . $port . $parts['path'] . $query; + } + + /** + * Build the URL for given path and parameters. + * + * @param $path + * (optional) The path. + * @param $params + * (optional) The query parameters in associative array. + * + * @return + * The URL for the given parameters. + */ + protected function getUri($path = '', $params = array()) { + $url = $this->getVariable('services_uri') ? $this->getVariable('services_uri') : $this->getVariable('base_uri'); + + if (!empty($path)) + if (substr($path, 0, 4) == "http") + $url = $path; + else + $url = rtrim($url, '/') . '/' . ltrim($path, '/'); + + if (!empty($params)) + $url .= '?' . http_build_query($params, NULL, '&'); + + return $url; + } + + /** + * Generate a signature for the given params and secret. + * + * @param $params + * The parameters to sign. + * @param $secret + * The secret to sign with. + * + * @return + * The generated signature + */ + protected function generateSignature($params, $secret) { + // Work with sorted data. + ksort($params); + + // Generate the base string. + $base_string = ''; + foreach ( $params as $key => $value ) { + $base_string .= $key . '=' . $value; + } + $base_string .= $secret; + + return md5($base_string); + } +} diff --git a/lib/classes/OAuth2Exception.php b/lib/classes/OAuth2Exception.php new file mode 100644 index 0000000..26846fb --- /dev/null +++ b/lib/classes/OAuth2Exception.php @@ -0,0 +1,83 @@ +. + * @author Update to draft v10 by Edison Wong . + * + * @sa Facebook PHP SDK. + */ +class OAuth2Exception extends Exception { + + /** + * The result from the API server that represents the exception information. + */ + protected $result; + + /** + * Make a new API Exception with the given result. + * + * @param $result + * The result from the API server. + */ + public function __construct($result) { + $this->result = $result; + + $code = isset($result['code']) ? $result['code'] : 0; + + if (isset($result['error'])) { + // OAuth 2.0 Draft 10 style + $message = $result['error']; + } elseif (isset($result['message'])) { + // cURL style + $message = $result['message']; + } else { + $message = 'Unknown Error. Check getResult()'; + } + + parent::__construct($message, $code); + } + + /** + * Return the associated result object returned by the API server. + * + * @returns + * The result from the API server. + */ + public function getResult() { + return $this->result; + } + + /** + * Returns the associated type for the error. This will default to + * 'Exception' when a type is not available. + * + * @return + * The type for the error. + */ + public function getType() { + if (isset($this->result['error'])) { + $message = $this->result['error']; + if (is_string($message)) { + // OAuth 2.0 Draft 10 style + return $message; + } + } + return 'Exception'; + } + + /** + * To make debugging easier. + * + * @returns + * The string representation of the error. + */ + public function __toString() { + $str = $this->getType() . ': '; + if ($this->code != 0) { + $str .= $this->code . ': '; + } + return $str . $this->message; + } +} diff --git a/lib/classes/OAuth2RedirectException.php b/lib/classes/OAuth2RedirectException.php new file mode 100644 index 0000000..1840645 --- /dev/null +++ b/lib/classes/OAuth2RedirectException.php @@ -0,0 +1,80 @@ +redirectUri = $redirect_uri; + if ($state) { + $this->errorData['state'] = $state; + } + + } + + /** + * Redirect the user agent. + * + * @ingroup oauth2_section_4 + */ + protected function sendHeaders() { + $params = array('query' => $this->errorData); + header("Location: " . $this->buildUri($this->redirectUri, $params)); + exit(); // No point in printing out data if we're redirecting + } + + /** + * Build the absolute URI based on supplied URI and parameters. + * + * @param $uri + * An absolute URI. + * @param $params + * Parameters to be append as GET. + * + * @return + * An absolute URI with supplied parameters. + * + * @ingroup oauth2_section_4 + */ + protected function buildUri($uri, $params) { + $parse_url = parse_url($uri); + + // Add our params to the parsed uri + foreach ( $params as $k => $v ) { + if (isset($parse_url[$k])) + $parse_url[$k] .= "&" . http_build_query($v); + else + $parse_url[$k] = http_build_query($v); + } + + // Put humpty dumpty back together + return ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "") . ((isset($parse_url["user"])) ? $parse_url["user"] . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "") . ((isset($parse_url["host"])) ? $parse_url["host"] : "") . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "") . ((isset($parse_url["path"])) ? $parse_url["path"] : "") . ((isset($parse_url["query"])) ? "?" . $parse_url["query"] : "") . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : ""); + } +} \ No newline at end of file diff --git a/lib/classes/OAuth2ServerException.php b/lib/classes/OAuth2ServerException.php new file mode 100644 index 0000000..8d28e0b --- /dev/null +++ b/lib/classes/OAuth2ServerException.php @@ -0,0 +1,82 @@ +httpCode = $http_status_code; + + $this->errorData['error'] = $error; + if ($error_description) { + $this->errorData['error_description'] = $error_description; + } + } + + /** + * @return string + */ + public function getDescription() { + return isset($this->errorData['error_description']) ? $this->errorData['error_description'] : null; + } + + /** + * @return string + */ + public function getHttpCode() { + return $this->httpCode; + } + + /** + * Send out error message in JSON. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + * + * @ingroup oauth2_error + */ + public function sendHttpResponse() { + header("HTTP/1.1 " . $this->httpCode); + $this->sendHeaders(); + echo (string) $this; + exit(); + } + + /** + * Send out HTTP headers for JSON. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.1 + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + * + * @ingroup oauth2_section_5 + */ + protected function sendHeaders() { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + } + + /** + * @see Exception::__toString() + */ + public function __toString() { + return json_encode($this->errorData); + } +} \ No newline at end of file diff --git a/lib/classes/OAuth2_API.php b/lib/classes/OAuth2_API.php new file mode 100644 index 0000000..f673e9d --- /dev/null +++ b/lib/classes/OAuth2_API.php @@ -0,0 +1,451 @@ +get('oauth'); + +$allowed = array( + 'authorize', // Authorize a user + 'request_token', // Request a Token + 'request_access', // Request Access + 'login' // This is for the authorization login screen + ); + + /** + * Log each request coming in trying to use OAuth2 Provider + * + * @since 1.0.0 + */ + $file = dirname(__FILE__).'/log.txt'; + $log = "Incomming Connection:".date("D F j")." at ".date("g:i:s a")."\n"; + $log .= "Method Being Called: ". $method ."\n"; + $log .= $_SERVER['HTTP_REFERER']."\n"; + foreach ($_GET as $name => $value) { + $log .= "$name: $value\n"; + } + $log .= "=========================\n"; + file_put_contents($file,$log, FILE_APPEND | LOCK_EX); + +/** + * Check to make sure only parameters defined are used and nothing else + */ +if (!in_array($method,$allowed)){ + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('error' => 'Paramter method', 'error_description' => 'The method parameter is required and seems to be missing')); + echo $error; + exit; + } + +/** +* Check and run the right method based on the method passed in the query +*/ +switch($method){ + + case 'authorize': + + /** + * Prevention check + */ + header('X-Frame-Options: DENY'); + + /** + *Check for client_id + */ + if (!isset($_GET['client_id']) || empty($_GET['client_id'])){ + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('error' => 'Parameter client_id', 'error_description' => 'The client_id parameter is required and seems to be missing')); + echo $error; + exit; + } + + /** + * Check for state + */ + if(!isset($_GET['state']) || empty($_GET['state'])){ + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('error' => 'Parameter state', 'error_description' => 'The state parameter is required and seems to be missing')); + echo $error; + exit; + } + + /** + * If the user is not logged in then redirect them to the OAuth Login + */ + if (!is_user_logged_in()) { + wp_redirect('/oauth/login?sso_redirect='.$_GET['client_id'].'&state='.$_GET['state']); + exit(); + } + + /** + * @var Get the current user + */ + $current_user = wp_get_current_user(); + + /** + * @var Set the current users ID + */ + $userId = $current_user->ID; + + // JUST IN CASE ONLY RUN IF $user_id HAS BEEN SET + if($userId != ''){ + $oauth->finishClientAuthorization(TRUE, $userId, $_GET); // AUTO AUTHORIZE + } + + try { + $auth_params = $oauth->getAuthorizeParams(); + } catch (OAuth2ServerException $oauthError) { + $oauthError->sendHttpResponse(); + } + + break; + + case 'request_token': + + header('X-Frame-Options: DENY'); + + try { + $oauth->grantAccessToken(); + } catch (OAuth2ServerException $oauthError) { + $oauthError->sendHttpResponse(); + } + + break; + + case 'request_access': + + try { + $token = $oauth->getBearerToken(); + $data = $oauth->verifyAccessToken($token); + + // GET THE USER ID FROM THE TOKEN AND NOT THE REQUESTING PARTY + $user_id = $data['user_id']; + + global $wpdb; + $info = $wpdb->get_row("SELECT * FROM wp_users WHERE ID = ".$user_id.""); + header('Cache-Control: no-cache, must-revalidate'); + header('Content-type: application/json'); + print_r(json_encode($info)); + + } catch (OAuth2ServerException $oauthError) { + $oauthError->sendHttpResponse(); + } + + break; + // RETURN EVERYTHING ABOUT THE CURRENT USER + + /** + * Login Redirect + */ + case 'login': + oauth2LoginLayout(); + exit(); + + break; + + + + +}// END SWITCH OF METHOD + +/** + * Contains the HTML layout for the authorization login page + */ +function oauth2LoginLayout(){?> + + + + + + + + <?php print bloginfo('name'); ?> - Authorization Login + + + + +
+
+
+

Authorization Login

+
+ +
+
+ +
+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/lib/classes/admin/IOAuth2Storage.php b/lib/classes/admin/IOAuth2Storage.php new file mode 100644 index 0000000..cc66e3e --- /dev/null +++ b/lib/classes/admin/IOAuth2Storage.php @@ -0,0 +1,250 @@ + + * @license http://www.gnu.org/licenses/gpl.html + * @link http://justin-greer.com + * @copyright 2013 Justin Greer + */ +class IOAuth2StorageWP implements IOAuth2GrantCode, IOAuth2RefreshTokens{ + + /** + * Custom Error Handle + * + * @param string $e Message that will be displayed as the error + */ + private function handleException($e) { + header("Content-Type: application/json"); + header("Cache-Control: no-store"); + $error = json_encode(array('Error' => $e)); + echo $error; + exit; + } + + /** + * Adds a new Client to the WP OAuth2 Complete Database + * Client_id and Client Secret are created un this function as well + * + * @param string $name Clients name as a slug + * @param string $client_redirect Client redirect URL that will be used after authorization + * + * @return Bool - True if successfull + * + * @todo Add Parameter "$client_name" to be more friendly in the WP OAuth2 Complete Dashboard + */ + public function addClient($mdop_name, $client_redirect) { + $client_id = $this->generateKey(); + $client_secret = $this->generateSecret(); + + global $wpdb; + $addClient = $wpdb->insert('oauth2_clients',array('name'=>$mdop_name, 'client_id' => trim(rtrim($client_id)), 'client_secret' => $client_secret, 'redirect_uri' => $client_redirect)); + if (!$addClient){ + $this->handleException('Could not add Client'); + }else{ + return TRUE; + } + } + + /** + * Checks if client_id and secret match in the database + * + * @param int $client_id Client ID to be checked + * @param int $client_secret Clinet Secret to be checked + * + * @return Bool - True if there is a match + * + * @todo Add SHA1 Encryption to this feature for better security + */ + public function checkClientCredentials($client_id, $client_secret) { + global $wpdb; + $wpdb->query("SELECT client_id, client_secret FROM oauth2_clients WHERE client_id = '$client_id' AND client_secret = '$client_secret'"); + if ($wpdb->num_rows > 0){ + return TRUE; + }else{ + return FALSE; + } + } + + /** + * Pulls the client details from the database + * + * @param int $client_id Client ID to lookup + * + * @return Array/Bool - If found then it will return a Array and if not found then will return false + */ + public function getClientDetails($client_id) { + global $wpdb; + $info = $wpdb->get_results("SELECT * FROM oauth2_clients WHERE client_id = '$client_id'", ARRAY_A ); + if ($wpdb->num_rows > 0){ + return $info[0]; + }else{ + return FALSE; + } + } + + /** + * Returns Access Token of a authorization token + * + * @param string $oauth_token + * + * @return AccessToken + */ + public function getAccessToken($oauth_token) { + return $this->getToken($oauth_token, FALSE); + } + + /** + * Sets Access Token + */ + public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = NULL) { + $this->setToken($oauth_token, $client_id, $user_id, $expires, $scope, FALSE); + } + + /** + * Gets Refresh Token + */ + public function getRefreshToken($refresh_token) { + return $this->getToken($refresh_token, TRUE); + } + + /** + * Sets refresh token + */ + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = NULL) { + return $this->setToken($refresh_token, $client_id, $user_id, $expires, $scope, TRUE); + } + + /** + * Deletes RefreshToken + * + * @param string $refresh_token Refresh Token to delete from database + * + * @return JSON error if failed + */ + public function unsetRefreshToken($refresh_token) { + global $wpdb; + $deleteToken = $wpd->query("DELETE FROM oauth2_refresh_tokens WHERE refresh_token = '$refresh_token'"); + if (!$deleteToken){ + $this->handleException('Could not delete refresh token'); // THROW ERROR + } + } + + /** + * Implements IOAuth2Storage::getAuthCode(). + */ + public function getAuthCode($code) { + global $wpdb; + $select = $wpdb->get_results("SELECT code, client_id, user_id, redirect_uri, expires, scope FROM oauth2_auth_codes WHERE code = '$code'", ARRAY_A ); + + if ($wpdb->num_rows > 0){ + return $select[0]; + }else{ + $this->handleException('Auth Code not found'); // THROW ERROR + } + } + + /** + * Sets Authorization code on a good user log in + * + * @param string $code Token that was returned for authorization + * @param int $client_id Client ID + * @param int $user_id User id that autorized the log in. + * @param string $redirect_uri Redirect URI is required for security needs even if it is in the database already + * @param int $expires timestamp set by default settings + * @param $scope We are currently not using this + */ + public function setAuthCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = NULL) { + global $wpdb; + $set = $wpdb->insert('oauth2_auth_codes',array('code' => $code, 'client_id' => $client_id, 'user_id' => $user_id, 'redirect_uri' => $redirect_uri, 'expires' => $expires, 'scope' => $scope )); + if (!$set){ + $this->handleException('Failed to set token'); + } + } + + /** + * Check Grant type fo the request + */ + public function checkRestrictedGrantType($client_id, $grant_type) { + return TRUE; // Not implemented + } + + /** + * Creates a refresh or access token + * + * @param string $token - Access or refresh token id + * @param string $client_id + * @param mixed $user_id + * @param int $expires + * @param string $scope + * @param bool $isRefresh + */ + protected function setToken($token, $client_id, $user_id, $expires, $scope, $isRefresh = TRUE) { + if ($isRefresh == TRUE){ + $tablename = 'oauth2_refresh_tokens'; + }else{ + $tablename = 'oauth2_access_tokens'; + } + global $wpdb; + $set = $wpdb->insert($tablename,array('oauth_token' => $token, 'client_id' => $client_id, 'user_id' => $user_id, 'expires' => $expires, 'scope' => $scope )); + if ($set){ + return TRUE; + } + } + + /** + * Retrieves an access or refresh token. + * + * @param string $token + * @param bool $refresh + */ + protected function getToken($token, $isRefresh) { + global $wpdb; + if ($isRefresh == TRUE){ + $tablename = 'oauth2_refresh_tokens'; + }else{ + $tablename = 'oauth2_access_tokens'; + } + $token = $wpdb->get_results("SELECT * FROM $tablename WHERE oauth_token = '$token'", ARRAY_A ); + return $token[0]; + } + + /** + * String Encryption + * + * @param string $client_secret + * @param string $client_id + * @return string + */ + protected function hash($client_secret, $client_id) { + return hash('blowfish', $client_id . $client_secret); + } + + /** + * Creates a unquie key + * + * @return 40 Char string + */ + protected function generateKey (){ + return substr(sha1(microtime()),0,40); + } + + /** + * Creates a unquie secret + * + * @return 20 Char string + */ + protected function generateSecret (){ + return substr(sha1(microtime().time()),0,20); + } +} diff --git a/lib/classes/admin/OAuthMain.php b/lib/classes/admin/OAuthMain.php new file mode 100644 index 0000000..9193a9f --- /dev/null +++ b/lib/classes/admin/OAuthMain.php @@ -0,0 +1,47 @@ +query("SELECT * FROM oauth2_clients"); + return $wpdb->num_rows; + } + + /** + * Formatted list of consumers + * + * @return string Formatted list of the registered consumers + */ + public function listConsumers(){ + global $wpdb; + $results = $wpdb->get_results("SELECT * FROM oauth2_clients"); + foreach($results as $single){ + print ''; + print ''.$single->name.''; + print ''.$single->client_id.''; + print ''.$single->client_secret.''; + print ''.$single->redirect_uri.''; + print 'Delete'; + print ''; + + } + } + +} + +?> \ No newline at end of file diff --git a/lib/classes/log.txt b/lib/classes/log.txt new file mode 100644 index 0000000..5643e8f --- /dev/null +++ b/lib/classes/log.txt @@ -0,0 +1,20 @@ +Incomming Connection:Wed March 20 at 10:42:59 pm +Method Being Called: ogin + +========================= +Incomming Connection:Wed March 20 at 10:43:05 pm +Method Being Called: login + +========================= +Incomming Connection:Wed March 20 at 10:43:05 pm +Method Being Called: login/style.css +http://oauth2-plugin.dev/oauth/login/ +========================= +Incomming Connection:Wed March 20 at 10:43:15 pm +Method Being Called: login +http://oauth2-plugin.dev/oauth/login/ +========================= +Incomming Connection:Wed March 20 at 10:43:15 pm +Method Being Called: login/style.css +http://oauth2-plugin.dev/oauth/login/ +========================= diff --git a/lib/dashbaord.php b/lib/dashbaord.php new file mode 100644 index 0000000..a933514 --- /dev/null +++ b/lib/dashbaord.php @@ -0,0 +1,127 @@ + + * @version 1.0.0 + */ +function wp_oauth2_complete_init_dashboard() { + + require_once(plugin_dir_path( __FILE__ )."classes/admin/IOAuth2Storage.php"); // INCLUDE OAuth 2.0 STORAGE + require_once(plugin_dir_path( __FILE__ )."classes/admin/OAuthMain.php"); // INCLUDE THE OAuth ADMIN OBJECT + $oauthStorage = new IOAuth2StorageWP(); // STORAGE OBJECT + $admin = new oauthAdmin(); // ADMIN OB]JECT + + $messageType; // MESSAGE TYPE HOLDER + $messagetext; // MESSAGE TEXT HOLDER + + wp_enqueue_style('wp_oauth2_provider_stylesheet'); + + if(isset($_POST['op2action']) && $_POST['op2action'] == 'Add Client'){ + $oauthStorage->addClient($_POST['mdop_name'], $_POST['mdop_redirect_uri']); // REGISTER A CONSUMER WITH NAME TEST + } + if(isset($_GET['delete']) && $_GET['delete'] != ''){ + global $wpdb; + $wpdb->delete('oauth2_clients', array('client_id'=> $_GET['delete'])); + } +?> + +

WordPress OAuth2 Provider

+
+ + + +
+ +

+ + +
+
+

Consumer Manager ConsumerCount(); ?> Consumers

+
+ +
+
+ + + + + + + + + + + + listConsumers(); ?> + +
NameKeySecretRedirect URIActions
+
+ +
+ +
+ + +
+ + +
+

Add Client

+
+ +
+ + + + + + + + + + + + + +
Name:Redirect URI
   
+
+
+
+
+ + + +
+
+ + + \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..5dedd71 --- /dev/null +++ b/readme.txt @@ -0,0 +1,100 @@ +=== OAuth2 Complete For WordPress === +Contributors: jgwpk +Donate link: http://justin-greer.com/donate +Tags: oauth2, oath provider, provider, oauth, oauth client, signle sign on, sso +Requires at least: 3.4.2 +Tested up to: 3.5.1 +Stable tag: 1.0.0 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +Allows WordPress to use OAuth 2 and become a SSO - Data provider. Your site will be able provided Single Sign On and also deliver authorized user data. + +== Description == + +OAuth2 Complete is a ONE OF A KIND plugin that instanly turns your WordPress webste into a valid OAuth v2 Provider. The plugin is built using OAuth2 Draft 20 standards. The backend is designed or extremly easy use for any level of experience. OAuth is a great tool but leaves most developers behind since it a bit technical. +The plugin has aleady done the hard part for you. + +Current Features Features: + +* Allows for Single Sign On Abilities +* Backend Panel for adding Apps/Clients +* 3 Methods pre built in to allow for a plug and play system + +== Installation == + +1. Upload `ouath2-complete` to the `/wp-content/plugins/` directory or use the built in plugin install by WordPress +1. Activate the plugin through the 'Plugins' menu in WordPress +1. Click 'Settings' and then 'permalinks'. Then simply click 'Save Changes' to flush the rewrite rules so that OAuth2 Provider +1. Your Ready to Rock + +== Frequently Asked Questions == + += How do I add a APP/Client? = + +Visit the OAuth2 Complete dashboard by clicking `Provider` in the WordPress admin panel. Once you are in teh dashboard there is a form to label `Add a Client`. Give your client a name and a redirect URI. The redirect URI is the HTTP location where the user will be returned to after authinicating (Your client should provide this for you). Click `Add Client` and you will that the client has been added to your Client Manager. + += How does a client connect to my website to use the Single Sign On? = + +Currently there is 3 Methods that OAuth2 Provider has built in: + +1. http://example.com/oauth/authorize + +1. http://example.com/oauth/request_token + +1. http://example.com/oauth/request_access + +Authorize requires only 3 parameters: + +* client_id +* response_type - Supported value's = `code` +* state +* Example call `http://example.com/oauth/authorize?client_id=the_client_id&state=anything_you_want&response_type=code` + +Request Token Requires only 4 parameters + +* code - This is auth code returned from the authorize call +* grant_type - Supported value's = `authorization_code` +* client_id +* client_secret +* Example call `http://example.com/oauth/request_token?code=the_auth_key_sent_back_from_the_authorize_call&grant_typ=authorization_code&client_id=the_client_id&client_secret=the_client_secret` + +Request Access Requires only 1 parmeter + +* access_token - This is the access_token provided from the Request Token call +* Example Call `http://example.com/oauth/request_access?access_token=the_token_from_the_request_call` + + +NOTE: All returns will be in JSON format. + += Is there support for this plugin? Can you help me? = + +You can visit our support forum for support. Although it takes the hard part away from dealing with OAuth it will require some knowledge on your behalf. We are glad to help as much as resonibily possible but there has to be a line drawn somewhere. + += Can you set this up for me on my current website? = + +Can we? "YES". But thats a different story. You are more than welcome to contact us with if you should ever need assistance. + += What information does the a authorized client have access to? = + +By default OAuth2 Provider delivers ALL the information about the user that logged in. We are planning on adding a easy to use dashboard to limit data. + += Do you have a tutorial I can follow ? = + +Yes we do. You can view a video titorial here. If you prefer readin then you may have to wait until the full documentation is complete. We are working hard to make it as easy and painless as possible for you to have a premium feature. + += Where can I download the SDK's for OAuth2 Provider = + +You can visit our websit Here + +== Upgrade Notice == + +When Upgrading OAuth2 Provider we serioulsy recommend creating a backup of your site. We will try to create updates that will be flawless. Hopefully any future updates will not chnage to the point where it will stop working. All updates will be ran through multiple tests before being released. In the event that the a upgrade of OAuth2 Provider is realeased and you decide to update, we (balackbird Interactive) can not and will not be held responsible +for any damages done to your website, business, or part that pertains to your website. Upgrade at your OWN RISK! + +== Screenshots == + +== Changelog == + += 1.0.0 = +*INITIAL BUILD \ No newline at end of file diff --git a/wp_oauth2-complete.php b/wp_oauth2-complete.php new file mode 100644 index 0000000..9075396 --- /dev/null +++ b/wp_oauth2-complete.php @@ -0,0 +1,279 @@ +insert( 'oauth2_options', array( 'version' => $wp_oath2_complete_version, 'enabled' => 1, 'draft'=> '20') ); +} + + +/** + * Run the install of the tables + */ +register_activation_hook(__FILE__,'wp_oauth2_complete_install'); // REGISTER THE CREATION OF THE TABLE +register_activation_hook(__FILE__,'wp_oauth2_complete_install_data'); // REGISTER THE INSTALLATION OF THE INTIAL DATA + + +/** + * OAuth2 Provider WordPress Rewrite rules class + * DON NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING + * + * @since 1.0.0 + */ +class OAuth2Rewrites { + + /** + * Activates the rewrite rules + * + * @todo Run this function only when the plugin is acivated + */ + function activate() { + + /** + * Rewrite Hook + * @global object $wp_rewrite WordPress hook for rewriting pretty URLs + */ + global $wp_rewrite; + + /** + * Flush the rewrites so the changes can take effect + */ + $this->flush_rewrite_rules(); + } + + /** + * Creates the rewrite rules that the plugin needs to function + * @return void + */ + function create_rewrite_rules($rules) { + global $wp_rewrite; + $newRule = array('oauth/(.+)' => 'index.php?oauth='.$wp_rewrite->preg_index(1)); + $newRules = $newRule + $rules; + return $newRules; + } + + /** + * Tell WordPress that we want to include "oauth" as something to look for in the permalinks + * @since 1.0.0 + */ + function add_query_vars($qvars) { + $qvars[] = 'oauth'; + return $qvars; + } + + /** + * Flushes the permalink rules and resets them. + * This adds any new rules into the mix + * + * @since 1.0.0 + */ + function flush_rewrite_rules() { + global $wp_rewrite; + $wp_rewrite->flush_rules(); + } + + /** + * Tells WordPress that when oauth is being called that we want stop and start using the OAuth2 Provider API hook + * + * @since 1.0.0 + */ + function template_redirect_intercept() { + + /** + * @global $wp_query Hooks into WordPress Queries + */ + global $wp_query; + + /** + * Check if "oauth" is found and is so than use OAuth2 Providers hook + * + * @since 1.0.0 + */ + if ($wp_query->get('oauth')) { + require_once(dirname(__FILE__). '/lib/classes/OAuth2_API.php'); + exit; + } + } + + /** + * Creates a JSON output + * + * @since 1.0.0 + * @uses output + * @deprecated Generic Output. Not needed or used not more. Scheduled to be removed 1.0.1 + */ + function pushoutput($message) { + $this->output($message); + } + + function output( $output ) { + header( 'Cache-Control: no-cache, must-revalidate' ); + header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' ); + + // Commented to display in browser. + // header( 'Content-type: application/json' ); + + echo json_encode( $output ); + } +} + +$OAuth2RewritesCode = new OAuth2Rewrites(); + +/** + * Does not seem to be working like it should + * + * @todo Take this activation hook out and rewrite class + * @todo This is not working and will have to see why it is not flushing the rewrites properly + */ +register_activation_hook( __file__, array($OAuth2RewritesCode, 'activate') ); + +/** + * Create all the hooks the link this all together with WordPress + */ +add_filter( 'rewrite_rules_array' , array($OAuth2RewritesCode , 'create_rewrite_rules' )); +add_filter( 'query_vars' , array($OAuth2RewritesCode , 'add_query_vars')); +add_filter( 'admin_init' , array($OAuth2RewritesCode , 'flush_rewrite_rules')); + +/** + * Add action hook to WordPress + * This was just added and seems to work pretty good + * + * @todo Look into this a little more for a more clean layout and hook + */ +add_action( 'template_redirect', array($OAuth2RewritesCode, 'template_redirect_intercept') ); +?> \ No newline at end of file From 287ff35fae8c35bc64d1cf02fb4b849c38ba3207 Mon Sep 17 00:00:00 2001 From: jgwpk Date: Wed, 3 Apr 2013 13:06:35 +0000 Subject: [PATCH 03/18] - Updated trunk files git-svn-id: http://plugins.svn.wordpress.org/oauth2-provider/trunk@691171 b8457f37-d9ea-0310-8a92-e5e31aec5664 --- lib/dashbaord.php | 254 +++++++++---------- readme.txt | 202 +++++++-------- wp_oauth2-complete.php | 556 ++++++++++++++++++++--------------------- 3 files changed, 508 insertions(+), 504 deletions(-) diff --git a/lib/dashbaord.php b/lib/dashbaord.php index a933514..7c12eca 100644 --- a/lib/dashbaord.php +++ b/lib/dashbaord.php @@ -1,127 +1,129 @@ - - * @version 1.0.0 - */ -function wp_oauth2_complete_init_dashboard() { - - require_once(plugin_dir_path( __FILE__ )."classes/admin/IOAuth2Storage.php"); // INCLUDE OAuth 2.0 STORAGE - require_once(plugin_dir_path( __FILE__ )."classes/admin/OAuthMain.php"); // INCLUDE THE OAuth ADMIN OBJECT - $oauthStorage = new IOAuth2StorageWP(); // STORAGE OBJECT - $admin = new oauthAdmin(); // ADMIN OB]JECT - - $messageType; // MESSAGE TYPE HOLDER - $messagetext; // MESSAGE TEXT HOLDER - - wp_enqueue_style('wp_oauth2_provider_stylesheet'); - - if(isset($_POST['op2action']) && $_POST['op2action'] == 'Add Client'){ - $oauthStorage->addClient($_POST['mdop_name'], $_POST['mdop_redirect_uri']); // REGISTER A CONSUMER WITH NAME TEST - } - if(isset($_GET['delete']) && $_GET['delete'] != ''){ - global $wpdb; - $wpdb->delete('oauth2_clients', array('client_id'=> $_GET['delete'])); - } -?> - -

WordPress OAuth2 Provider

-
- - - -
- -

- - -
-
-

Consumer Manager ConsumerCount(); ?> Consumers

-
- -
-
- - - - - - - - - - - - listConsumers(); ?> - -
NameKeySecretRedirect URIActions
-
- -
- -
- - -
- - -
-

Add Client

-
- -
- - - - - - - - - - - - - -
Name:Redirect URI
   
-
-
-
-
- - - -
-
- - + + * @version 1.0.1 + */ +function wp_oauth2_complete_init_dashboard() { + + require_once(plugin_dir_path( __FILE__ )."classes/admin/IOAuth2Storage.php"); // INCLUDE OAuth 2.0 STORAGE + require_once(plugin_dir_path( __FILE__ )."classes/admin/OAuthMain.php"); // INCLUDE THE OAuth ADMIN OBJECT + $oauthStorage = new IOAuth2StorageWP(); // STORAGE OBJECT + $admin = new oauthAdmin(); // ADMIN OB]JECT + + $messageType; // MESSAGE TYPE HOLDER + $messagetext; // MESSAGE TEXT HOLDER + + wp_enqueue_style('wp_oauth2_provider_stylesheet'); + + if(isset($_POST['op2action']) && $_POST['op2action'] == 'Add Client'){ + $oauthStorage->addClient($_POST['mdop_name'], $_POST['mdop_redirect_uri']); + } + if(isset($_GET['delete']) && $_GET['delete'] != ''){ + global $wpdb; + $wpdb->delete('oauth2_clients', array('client_id'=> $_GET['delete'])); + } + + // Added to be used through out the plugin backend + $adminUrl = admin_url(); +?> + +

WordPress OAuth2 Provider

+
+ + + +
+ +

+ + +
+
+

Consumer Manager ConsumerCount(); ?> Consumers

+
+ +
+
+ + + + + + + + + + + + listConsumers(); ?> + +
NameKeySecretRedirect URIActions
+
+ +
+ +
+ + +
+ + +
+

Add Client

+
+
+ + + + + + + + + + + + + +
Name:Redirect URI
   
+
+
+
+
+ + + +
+
+ + \ No newline at end of file diff --git a/readme.txt b/readme.txt index 5dedd71..5581ee2 100644 --- a/readme.txt +++ b/readme.txt @@ -1,100 +1,102 @@ -=== OAuth2 Complete For WordPress === -Contributors: jgwpk -Donate link: http://justin-greer.com/donate -Tags: oauth2, oath provider, provider, oauth, oauth client, signle sign on, sso -Requires at least: 3.4.2 -Tested up to: 3.5.1 -Stable tag: 1.0.0 -License: GPLv2 or later -License URI: http://www.gnu.org/licenses/gpl-2.0.html - -Allows WordPress to use OAuth 2 and become a SSO - Data provider. Your site will be able provided Single Sign On and also deliver authorized user data. - -== Description == - -OAuth2 Complete is a ONE OF A KIND plugin that instanly turns your WordPress webste into a valid OAuth v2 Provider. The plugin is built using OAuth2 Draft 20 standards. The backend is designed or extremly easy use for any level of experience. OAuth is a great tool but leaves most developers behind since it a bit technical. -The plugin has aleady done the hard part for you. - -Current Features Features: - -* Allows for Single Sign On Abilities -* Backend Panel for adding Apps/Clients -* 3 Methods pre built in to allow for a plug and play system - -== Installation == - -1. Upload `ouath2-complete` to the `/wp-content/plugins/` directory or use the built in plugin install by WordPress -1. Activate the plugin through the 'Plugins' menu in WordPress -1. Click 'Settings' and then 'permalinks'. Then simply click 'Save Changes' to flush the rewrite rules so that OAuth2 Provider -1. Your Ready to Rock - -== Frequently Asked Questions == - -= How do I add a APP/Client? = - -Visit the OAuth2 Complete dashboard by clicking `Provider` in the WordPress admin panel. Once you are in teh dashboard there is a form to label `Add a Client`. Give your client a name and a redirect URI. The redirect URI is the HTTP location where the user will be returned to after authinicating (Your client should provide this for you). Click `Add Client` and you will that the client has been added to your Client Manager. - -= How does a client connect to my website to use the Single Sign On? = - -Currently there is 3 Methods that OAuth2 Provider has built in: - -1. http://example.com/oauth/authorize - -1. http://example.com/oauth/request_token - -1. http://example.com/oauth/request_access - -Authorize requires only 3 parameters: - -* client_id -* response_type - Supported value's = `code` -* state -* Example call `http://example.com/oauth/authorize?client_id=the_client_id&state=anything_you_want&response_type=code` - -Request Token Requires only 4 parameters - -* code - This is auth code returned from the authorize call -* grant_type - Supported value's = `authorization_code` -* client_id -* client_secret -* Example call `http://example.com/oauth/request_token?code=the_auth_key_sent_back_from_the_authorize_call&grant_typ=authorization_code&client_id=the_client_id&client_secret=the_client_secret` - -Request Access Requires only 1 parmeter - -* access_token - This is the access_token provided from the Request Token call -* Example Call `http://example.com/oauth/request_access?access_token=the_token_from_the_request_call` - - -NOTE: All returns will be in JSON format. - -= Is there support for this plugin? Can you help me? = - -You can visit our support forum for support. Although it takes the hard part away from dealing with OAuth it will require some knowledge on your behalf. We are glad to help as much as resonibily possible but there has to be a line drawn somewhere. - -= Can you set this up for me on my current website? = - -Can we? "YES". But thats a different story. You are more than welcome to contact us with if you should ever need assistance. - -= What information does the a authorized client have access to? = - -By default OAuth2 Provider delivers ALL the information about the user that logged in. We are planning on adding a easy to use dashboard to limit data. - -= Do you have a tutorial I can follow ? = - -Yes we do. You can view a video titorial here. If you prefer readin then you may have to wait until the full documentation is complete. We are working hard to make it as easy and painless as possible for you to have a premium feature. - -= Where can I download the SDK's for OAuth2 Provider = - -You can visit our websit Here - -== Upgrade Notice == - -When Upgrading OAuth2 Provider we serioulsy recommend creating a backup of your site. We will try to create updates that will be flawless. Hopefully any future updates will not chnage to the point where it will stop working. All updates will be ran through multiple tests before being released. In the event that the a upgrade of OAuth2 Provider is realeased and you decide to update, we (balackbird Interactive) can not and will not be held responsible -for any damages done to your website, business, or part that pertains to your website. Upgrade at your OWN RISK! - -== Screenshots == - -== Changelog == - -= 1.0.0 = -*INITIAL BUILD \ No newline at end of file +=== OAuth2 Complete For WordPress === +Contributors: jgwpk +Donate link: http://justin-greer.com/donate +Tags: oauth2, OAuth provider, Provider, OAuth, OAuth client, Single Sign On, sso +Requires at least: 3.4.2 +Tested up to: 3.5.1 +Stable tag: 1.0.1 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +Your site will be able provided Single Sign On and also deliver authorized user data using the built in OAuth 2.0 API. + +== Description == + +OAuth2 Complete is a ONE OF A KIND plugin that instanly turns your WordPress webste into a valid OAuth v2 Provider. The plugin is built using OAuth2 Draft 20 standards. The backend is designed or extremly easy use for any level of experience. OAuth is a great tool but leaves most developers behind since it a bit technical. +The plugin has aleady done the hard part for you. + +Current Features Features: + +* Allows for Single Sign On Abilities +* Backend Panel for adding Apps/Clients +* 3 Methods pre built in to allow for a plug and play system + +== Installation == + +1. Upload `ouath2-complete` to the `/wp-content/plugins/` directory or use the built in plugin install by WordPress +1. Activate the plugin through the 'Plugins' menu in WordPress +1. Click 'Settings' and then 'permalinks'. Then simply click 'Save Changes' to flush the rewrite rules so that OAuth2 Provider +1. Your Ready to Rock + +== Frequently Asked Questions == + += How do I add a APP/Client? = + +Visit the OAuth2 Complete dashboard by clicking `Provider` in the WordPress admin panel. Once you are in teh dashboard there is a form to label `Add a Client`. Give your client a name and a redirect URI. The redirect URI is the HTTP location where the user will be returned to after authinicating (Your client should provide this for you). Click `Add Client` and you will that the client has been added to your Client Manager. + += How does a client connect to my website to use the Single Sign On? = + +Currently there is 3 Methods that OAuth2 Provider has built in: + +1. http://example.com/oauth/authorize + +1. http://example.com/oauth/request_token + +1. http://example.com/oauth/request_access + +Authorize requires only 3 parameters: + +* client_id +* response_type - Supported value's = `code` +* state +* Example call `http://example.com/oauth/authorize?client_id=the_client_id&state=anything_you_want&response_type=code` + +Request Token Requires only 4 parameters + +* code - This is auth code returned from the authorize call +* grant_type - Supported value's = `authorization_code` +* client_id +* client_secret +* Example call `http://example.com/oauth/request_token?code=the_auth_key_sent_back_from_the_authorize_call&grant_typ=authorization_code&client_id=the_client_id&client_secret=the_client_secret` + +Request Access Requires only 1 parmeter + +* access_token - This is the access_token provided from the Request Token call +* Example Call `http://example.com/oauth/request_access?access_token=the_token_from_the_request_call` + + +NOTE: All returns will be in JSON format. + += Is there support for this plugin? Can you help me? = + +You can visit our support forum for support. Although it takes the hard part away from dealing with OAuth it will require some knowledge on your behalf. I am glad to help as much as resonibily possible but there has to be a line drawn somewhere. + += Can you set this up for me on my current website? = + +Can I? "YES". But thats a different story. You are more than welcome to contact us with if you should ever need assistance. + += What information does the a authorized client have access to? = + +By default OAuth2 Provider delivers ALL the information about the user that logged in. We are planning on adding a easy to use dashboard to limit data. + += Do you have a tutorial I can follow ? = + +Yes I do. You can view a video titorial here. If you prefer readin then you may have to wait until the full documentation is complete. I are working hard to make it as easy and painless as possible for you to have a premium feature. + += Where can I download the SDK's for OAuth2 Provider = + +You can visit my websiet Here + +== Upgrade Notice == + +When Upgrading OAuth2 Provider I serioulsy recommend creating a backup of your site. I will try to create updates that will be flawless. Hopefully any future updates will not chnage to the point where it will stop working. All updates will be ran through multiple tests before being released. In the event that the a upgrade of OAuth2 Provider is realeased and you decide to update, I (Justin Greer) can not and will not be held responsible for any damages done to your website, business, or part that pertains to your website. Upgrade at your OWN RISK! + +== Screenshots == + +== Changelog == + += 1.0.0 = +*INITIAL BUILD += 1.0.1 = +* Re-worked Readme.txt +* Fixed absolute paths causing 404 Error when WordPress is running under a sub diractory (Useing admin_url() currently) \ No newline at end of file diff --git a/wp_oauth2-complete.php b/wp_oauth2-complete.php index 9075396..5d58b18 100644 --- a/wp_oauth2-complete.php +++ b/wp_oauth2-complete.php @@ -1,279 +1,279 @@ -insert( 'oauth2_options', array( 'version' => $wp_oath2_complete_version, 'enabled' => 1, 'draft'=> '20') ); -} - - -/** - * Run the install of the tables - */ -register_activation_hook(__FILE__,'wp_oauth2_complete_install'); // REGISTER THE CREATION OF THE TABLE -register_activation_hook(__FILE__,'wp_oauth2_complete_install_data'); // REGISTER THE INSTALLATION OF THE INTIAL DATA - - -/** - * OAuth2 Provider WordPress Rewrite rules class - * DON NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING - * - * @since 1.0.0 - */ -class OAuth2Rewrites { - - /** - * Activates the rewrite rules - * - * @todo Run this function only when the plugin is acivated - */ - function activate() { - - /** - * Rewrite Hook - * @global object $wp_rewrite WordPress hook for rewriting pretty URLs - */ - global $wp_rewrite; - - /** - * Flush the rewrites so the changes can take effect - */ - $this->flush_rewrite_rules(); - } - - /** - * Creates the rewrite rules that the plugin needs to function - * @return void - */ - function create_rewrite_rules($rules) { - global $wp_rewrite; - $newRule = array('oauth/(.+)' => 'index.php?oauth='.$wp_rewrite->preg_index(1)); - $newRules = $newRule + $rules; - return $newRules; - } - - /** - * Tell WordPress that we want to include "oauth" as something to look for in the permalinks - * @since 1.0.0 - */ - function add_query_vars($qvars) { - $qvars[] = 'oauth'; - return $qvars; - } - - /** - * Flushes the permalink rules and resets them. - * This adds any new rules into the mix - * - * @since 1.0.0 - */ - function flush_rewrite_rules() { - global $wp_rewrite; - $wp_rewrite->flush_rules(); - } - - /** - * Tells WordPress that when oauth is being called that we want stop and start using the OAuth2 Provider API hook - * - * @since 1.0.0 - */ - function template_redirect_intercept() { - - /** - * @global $wp_query Hooks into WordPress Queries - */ - global $wp_query; - - /** - * Check if "oauth" is found and is so than use OAuth2 Providers hook - * - * @since 1.0.0 - */ - if ($wp_query->get('oauth')) { - require_once(dirname(__FILE__). '/lib/classes/OAuth2_API.php'); - exit; - } - } - - /** - * Creates a JSON output - * - * @since 1.0.0 - * @uses output - * @deprecated Generic Output. Not needed or used not more. Scheduled to be removed 1.0.1 - */ - function pushoutput($message) { - $this->output($message); - } - - function output( $output ) { - header( 'Cache-Control: no-cache, must-revalidate' ); - header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' ); - - // Commented to display in browser. - // header( 'Content-type: application/json' ); - - echo json_encode( $output ); - } -} - -$OAuth2RewritesCode = new OAuth2Rewrites(); - -/** - * Does not seem to be working like it should - * - * @todo Take this activation hook out and rewrite class - * @todo This is not working and will have to see why it is not flushing the rewrites properly - */ -register_activation_hook( __file__, array($OAuth2RewritesCode, 'activate') ); - -/** - * Create all the hooks the link this all together with WordPress - */ -add_filter( 'rewrite_rules_array' , array($OAuth2RewritesCode , 'create_rewrite_rules' )); -add_filter( 'query_vars' , array($OAuth2RewritesCode , 'add_query_vars')); -add_filter( 'admin_init' , array($OAuth2RewritesCode , 'flush_rewrite_rules')); - -/** - * Add action hook to WordPress - * This was just added and seems to work pretty good - * - * @todo Look into this a little more for a more clean layout and hook - */ -add_action( 'template_redirect', array($OAuth2RewritesCode, 'template_redirect_intercept') ); +insert( 'oauth2_options', array( 'version' => $wp_oath2_complete_version, 'enabled' => 1, 'draft'=> '20') ); +} + + +/** + * Run the install of the tables + */ +register_activation_hook(__FILE__,'wp_oauth2_complete_install'); // REGISTER THE CREATION OF THE TABLE +register_activation_hook(__FILE__,'wp_oauth2_complete_install_data'); // REGISTER THE INSTALLATION OF THE INTIAL DATA + + +/** + * OAuth2 Provider WordPress Rewrite rules class + * DON NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING + * + * @since 1.0.0 + */ +class OAuth2Rewrites { + + /** + * Activates the rewrite rules + * + * @todo Run this function only when the plugin is acivated + */ + function activate() { + + /** + * Rewrite Hook + * @global object $wp_rewrite WordPress hook for rewriting pretty URLs + */ + global $wp_rewrite; + + /** + * Flush the rewrites so the changes can take effect + */ + $this->flush_rewrite_rules(); + } + + /** + * Creates the rewrite rules that the plugin needs to function + * @return void + */ + function create_rewrite_rules($rules) { + global $wp_rewrite; + $newRule = array('oauth/(.+)' => 'index.php?oauth='.$wp_rewrite->preg_index(1)); + $newRules = $newRule + $rules; + return $newRules; + } + + /** + * Tell WordPress that we want to include "oauth" as something to look for in the permalinks + * @since 1.0.0 + */ + function add_query_vars($qvars) { + $qvars[] = 'oauth'; + return $qvars; + } + + /** + * Flushes the permalink rules and resets them. + * This adds any new rules into the mix + * + * @since 1.0.0 + */ + function flush_rewrite_rules() { + global $wp_rewrite; + $wp_rewrite->flush_rules(); + } + + /** + * Tells WordPress that when oauth is being called that we want stop and start using the OAuth2 Provider API hook + * + * @since 1.0.0 + */ + function template_redirect_intercept() { + + /** + * @global $wp_query Hooks into WordPress Queries + */ + global $wp_query; + + /** + * Check if "oauth" is found and is so than use OAuth2 Providers hook + * + * @since 1.0.0 + */ + if ($wp_query->get('oauth')) { + require_once(dirname(__FILE__). '/lib/classes/OAuth2_API.php'); + exit; + } + } + + /** + * Creates a JSON output + * + * @since 1.0.0 + * @uses output + * @deprecated Generic Output. Not needed or used not more. Scheduled to be removed 1.0.1 + */ + function pushoutput($message) { + $this->output($message); + } + + function output( $output ) { + header( 'Cache-Control: no-cache, must-revalidate' ); + header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' ); + + // Commented to display in browser. + // header( 'Content-type: application/json' ); + + echo json_encode( $output ); + } +} + +$OAuth2RewritesCode = new OAuth2Rewrites(); + +/** + * Does not seem to be working like it should + * + * @todo Take this activation hook out and rewrite class + * @todo This is not working and will have to see why it is not flushing the rewrites properly + */ +register_activation_hook( __file__, array($OAuth2RewritesCode, 'activate') ); + +/** + * Create all the hooks the link this all together with WordPress + */ +add_filter( 'rewrite_rules_array' , array($OAuth2RewritesCode , 'create_rewrite_rules' )); +add_filter( 'query_vars' , array($OAuth2RewritesCode , 'add_query_vars')); +add_filter( 'admin_init' , array($OAuth2RewritesCode , 'flush_rewrite_rules')); + +/** + * Add action hook to WordPress + * This was just added and seems to work pretty good + * + * @todo Look into this a little more clean layout and hook + */ +add_action( 'template_redirect', array($OAuth2RewritesCode, 'template_redirect_intercept') ); ?> \ No newline at end of file From 6e5ac45d19278c2bd7f522dc9d2812c40303ee48 Mon Sep 17 00:00:00 2001 From: jgwpk Date: Thu, 11 Apr 2013 01:25:52 +0000 Subject: [PATCH 04/18] Fixed Login Redirect git-svn-id: http://plugins.svn.wordpress.org/oauth2-provider/trunk@695622 b8457f37-d9ea-0310-8a92-e5e31aec5664 --- lib/classes/OAuth2_API.php | 78 +++--- readme.txt | 208 +++++++------- wp_oauth2-complete.php | 556 ++++++++++++++++++------------------- 3 files changed, 423 insertions(+), 419 deletions(-) diff --git a/lib/classes/OAuth2_API.php b/lib/classes/OAuth2_API.php index f673e9d..6b95710 100644 --- a/lib/classes/OAuth2_API.php +++ b/lib/classes/OAuth2_API.php @@ -169,7 +169,45 @@ /** * Contains the HTML layout for the authorization login page */ -function oauth2LoginLayout(){?> +function oauth2LoginLayout(){ + + /** + * Form handler + */ + + // CHECK IF THE LOGIN FORM HAS BEEN SUBMITTED + if(isset($_POST['login'])) { + + $user = $_POST['user']; + $pwd = $_POST['pwd']; + $creds = array(); + $creds['user_login'] = $user; + $creds['user_password'] = $pwd; + $user = wp_signon( $creds, false ); + + if ( !is_wp_error($user) ){ + + /* + HOOK FOR SINGLE SIGN ON + + Description: + + If the sso_redirect if present then the script will look for the client_id (which has been encrypted with AES); + We need to take that encryption and decrypt it and to get the client ID. + + NOW we will wait until the user has entered the credinials that is needed to log in. If the login is good and the sso_redirect is present we will redirect then back through the OAuth process which will then authinicate and send the + user with a token to the clients site. The client can then take the token and use it to get all the users information for database (AS IF THEY ARE LOGGED IN HERE). + */ + if (isset($_GET['sso_redirect']) && $_GET['sso_redirect'] != ''){ + wp_redirect(site_url() .'/oauth/authorize/?client_id='.$_GET['sso_redirect'].'&state='.$_GET['state'].'&response_type=code'); + + }else{ + $error = ''; + } + + } + } + ?> @@ -385,44 +423,6 @@ function oauth2LoginLayout(){?> background-position: 0 -135px; color: #00aeef; } - Incorrect Information'; - } - - } - } - ?> diff --git a/readme.txt b/readme.txt index 5581ee2..287c778 100644 --- a/readme.txt +++ b/readme.txt @@ -1,102 +1,106 @@ -=== OAuth2 Complete For WordPress === -Contributors: jgwpk -Donate link: http://justin-greer.com/donate -Tags: oauth2, OAuth provider, Provider, OAuth, OAuth client, Single Sign On, sso -Requires at least: 3.4.2 -Tested up to: 3.5.1 -Stable tag: 1.0.1 -License: GPLv2 or later -License URI: http://www.gnu.org/licenses/gpl-2.0.html - -Your site will be able provided Single Sign On and also deliver authorized user data using the built in OAuth 2.0 API. - -== Description == - -OAuth2 Complete is a ONE OF A KIND plugin that instanly turns your WordPress webste into a valid OAuth v2 Provider. The plugin is built using OAuth2 Draft 20 standards. The backend is designed or extremly easy use for any level of experience. OAuth is a great tool but leaves most developers behind since it a bit technical. -The plugin has aleady done the hard part for you. - -Current Features Features: - -* Allows for Single Sign On Abilities -* Backend Panel for adding Apps/Clients -* 3 Methods pre built in to allow for a plug and play system - -== Installation == - -1. Upload `ouath2-complete` to the `/wp-content/plugins/` directory or use the built in plugin install by WordPress -1. Activate the plugin through the 'Plugins' menu in WordPress -1. Click 'Settings' and then 'permalinks'. Then simply click 'Save Changes' to flush the rewrite rules so that OAuth2 Provider -1. Your Ready to Rock - -== Frequently Asked Questions == - -= How do I add a APP/Client? = - -Visit the OAuth2 Complete dashboard by clicking `Provider` in the WordPress admin panel. Once you are in teh dashboard there is a form to label `Add a Client`. Give your client a name and a redirect URI. The redirect URI is the HTTP location where the user will be returned to after authinicating (Your client should provide this for you). Click `Add Client` and you will that the client has been added to your Client Manager. - -= How does a client connect to my website to use the Single Sign On? = - -Currently there is 3 Methods that OAuth2 Provider has built in: - -1. http://example.com/oauth/authorize - -1. http://example.com/oauth/request_token - -1. http://example.com/oauth/request_access - -Authorize requires only 3 parameters: - -* client_id -* response_type - Supported value's = `code` -* state -* Example call `http://example.com/oauth/authorize?client_id=the_client_id&state=anything_you_want&response_type=code` - -Request Token Requires only 4 parameters - -* code - This is auth code returned from the authorize call -* grant_type - Supported value's = `authorization_code` -* client_id -* client_secret -* Example call `http://example.com/oauth/request_token?code=the_auth_key_sent_back_from_the_authorize_call&grant_typ=authorization_code&client_id=the_client_id&client_secret=the_client_secret` - -Request Access Requires only 1 parmeter - -* access_token - This is the access_token provided from the Request Token call -* Example Call `http://example.com/oauth/request_access?access_token=the_token_from_the_request_call` - - -NOTE: All returns will be in JSON format. - -= Is there support for this plugin? Can you help me? = - -You can visit our support forum for support. Although it takes the hard part away from dealing with OAuth it will require some knowledge on your behalf. I am glad to help as much as resonibily possible but there has to be a line drawn somewhere. - -= Can you set this up for me on my current website? = - -Can I? "YES". But thats a different story. You are more than welcome to contact us with if you should ever need assistance. - -= What information does the a authorized client have access to? = - -By default OAuth2 Provider delivers ALL the information about the user that logged in. We are planning on adding a easy to use dashboard to limit data. - -= Do you have a tutorial I can follow ? = - -Yes I do. You can view a video titorial here. If you prefer readin then you may have to wait until the full documentation is complete. I are working hard to make it as easy and painless as possible for you to have a premium feature. - -= Where can I download the SDK's for OAuth2 Provider = - -You can visit my websiet Here - -== Upgrade Notice == - -When Upgrading OAuth2 Provider I serioulsy recommend creating a backup of your site. I will try to create updates that will be flawless. Hopefully any future updates will not chnage to the point where it will stop working. All updates will be ran through multiple tests before being released. In the event that the a upgrade of OAuth2 Provider is realeased and you decide to update, I (Justin Greer) can not and will not be held responsible for any damages done to your website, business, or part that pertains to your website. Upgrade at your OWN RISK! - -== Screenshots == - -== Changelog == - -= 1.0.0 = -*INITIAL BUILD -= 1.0.1 = -* Re-worked Readme.txt -* Fixed absolute paths causing 404 Error when WordPress is running under a sub diractory (Useing admin_url() currently) \ No newline at end of file +=== OAuth2 Complete For WordPress === +Contributors: jgwpk +Donate link: http://justin-greer.com/donate +Tags: oauth2, OAuth provider, Provider, OAuth, OAuth client, Single Sign On, sso +Requires at least: 3.4.2 +Tested up to: 3.5.2 +Stable tag: 1.0.2 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +Your site will be able provided Single Sign On and also deliver authorized user data using the built in OAuth 2.0 API. + +== Description == + +OAuth2 Complete is a ONE OF A KIND plugin that instanly turns your WordPress webste into a valid OAuth v2 Provider. The plugin is built using OAuth2 Draft 20 standards. The backend is designed or extremly easy use for any level of experience. OAuth is a great tool but leaves most developers behind since it a bit technical. +The plugin has aleady done the hard part for you. + +Current Features Features: + +* Allows for Single Sign On Abilities +* Backend Panel for adding Apps/Clients +* 3 Methods pre built in to allow for a plug and play system + +== Installation == + +1. Upload `ouath2-complete` to the `/wp-content/plugins/` directory or use the built in plugin install by WordPress +1. Activate the plugin through the 'Plugins' menu in WordPress +1. Click 'Settings' and then 'permalinks'. Then simply click 'Save Changes' to flush the rewrite rules so that OAuth2 Provider +1. Your Ready to Rock + +== Frequently Asked Questions == + += How do I add a APP/Client? = + +Visit the OAuth2 Complete dashboard by clicking `Provider` in the WordPress admin panel. Once you are in teh dashboard there is a form to label `Add a Client`. Give your client a name and a redirect URI. The redirect URI is the HTTP location where the user will be returned to after authinicating (Your client should provide this for you). Click `Add Client` and you will that the client has been added to your Client Manager. + += How does a client connect to my website to use the Single Sign On? = + +Currently there is 3 Methods that OAuth2 Provider has built in: + +1. http://example.com/oauth/authorize + +1. http://example.com/oauth/request_token + +1. http://example.com/oauth/request_access + +Authorize requires only 3 parameters: + +* client_id +* response_type - Supported value's = `code` +* state +* Example call `http://example.com/oauth/authorize?client_id=the_client_id&state=anything_you_want&response_type=code` + +Request Token Requires only 4 parameters + +* code - This is auth code returned from the authorize call +* grant_type - Supported value's = `authorization_code` +* client_id +* client_secret +* Example call `http://example.com/oauth/request_token?code=the_auth_key_sent_back_from_the_authorize_call&grant_typ=authorization_code&client_id=the_client_id&client_secret=the_client_secret` + +Request Access Requires only 1 parmeter + +* access_token - This is the access_token provided from the Request Token call +* Example Call `http://example.com/oauth/request_access?access_token=the_token_from_the_request_call` + + +NOTE: All returns will be in JSON format. + += Is there support for this plugin? Can you help me? = + +You can visit our support forum for support. Although it takes the hard part away from dealing with OAuth it will require some knowledge on your behalf. I am glad to help as much as resonibily possible but there has to be a line drawn somewhere. + += Can you set this up for me on my current website? = + +Can I? "YES". But thats a different story. You are more than welcome to contact us with if you should ever need assistance. + += What information does the a authorized client have access to? = + +By default OAuth2 Provider delivers ALL the information about the user that logged in. We are planning on adding a easy to use dashboard to limit data. + += Do you have a tutorial I can follow ? = + +Yes I do. You can view a video titorial here. If you prefer readin then you may have to wait until the full documentation is complete. I are working hard to make it as easy and painless as possible for you to have a premium feature. + += Where can I download the SDK's for OAuth2 Provider = + +You can visit my websiet Here + +== Upgrade Notice == + +When Upgrading OAuth2 Provider I serioulsy recommend creating a backup of your site. I will try to create updates that will be flawless. Hopefully any future updates will not chnage to the point where it will stop working. All updates will be ran through multiple tests before being released. In the event that the a upgrade of OAuth2 Provider is realeased and you decide to update, I (Justin Greer) can not and will not be held responsible for any damages done to your website, business, or part that pertains to your website. Upgrade at your OWN RISK! + +== Screenshots == + +== Changelog == + += 1.0.0 = +*INITIAL BUILD + += 1.0.1 = +* Re-worked Readme.txt +* Fixed absolute paths causing 404 Error when WordPress is running under a sub diractory (Useing admin_url() currently) + += 1.0.2 = +* Fixed Broken login redirect \ No newline at end of file diff --git a/wp_oauth2-complete.php b/wp_oauth2-complete.php index 5d58b18..246249e 100644 --- a/wp_oauth2-complete.php +++ b/wp_oauth2-complete.php @@ -1,279 +1,279 @@ -insert( 'oauth2_options', array( 'version' => $wp_oath2_complete_version, 'enabled' => 1, 'draft'=> '20') ); -} - - -/** - * Run the install of the tables - */ -register_activation_hook(__FILE__,'wp_oauth2_complete_install'); // REGISTER THE CREATION OF THE TABLE -register_activation_hook(__FILE__,'wp_oauth2_complete_install_data'); // REGISTER THE INSTALLATION OF THE INTIAL DATA - - -/** - * OAuth2 Provider WordPress Rewrite rules class - * DON NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING - * - * @since 1.0.0 - */ -class OAuth2Rewrites { - - /** - * Activates the rewrite rules - * - * @todo Run this function only when the plugin is acivated - */ - function activate() { - - /** - * Rewrite Hook - * @global object $wp_rewrite WordPress hook for rewriting pretty URLs - */ - global $wp_rewrite; - - /** - * Flush the rewrites so the changes can take effect - */ - $this->flush_rewrite_rules(); - } - - /** - * Creates the rewrite rules that the plugin needs to function - * @return void - */ - function create_rewrite_rules($rules) { - global $wp_rewrite; - $newRule = array('oauth/(.+)' => 'index.php?oauth='.$wp_rewrite->preg_index(1)); - $newRules = $newRule + $rules; - return $newRules; - } - - /** - * Tell WordPress that we want to include "oauth" as something to look for in the permalinks - * @since 1.0.0 - */ - function add_query_vars($qvars) { - $qvars[] = 'oauth'; - return $qvars; - } - - /** - * Flushes the permalink rules and resets them. - * This adds any new rules into the mix - * - * @since 1.0.0 - */ - function flush_rewrite_rules() { - global $wp_rewrite; - $wp_rewrite->flush_rules(); - } - - /** - * Tells WordPress that when oauth is being called that we want stop and start using the OAuth2 Provider API hook - * - * @since 1.0.0 - */ - function template_redirect_intercept() { - - /** - * @global $wp_query Hooks into WordPress Queries - */ - global $wp_query; - - /** - * Check if "oauth" is found and is so than use OAuth2 Providers hook - * - * @since 1.0.0 - */ - if ($wp_query->get('oauth')) { - require_once(dirname(__FILE__). '/lib/classes/OAuth2_API.php'); - exit; - } - } - - /** - * Creates a JSON output - * - * @since 1.0.0 - * @uses output - * @deprecated Generic Output. Not needed or used not more. Scheduled to be removed 1.0.1 - */ - function pushoutput($message) { - $this->output($message); - } - - function output( $output ) { - header( 'Cache-Control: no-cache, must-revalidate' ); - header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' ); - - // Commented to display in browser. - // header( 'Content-type: application/json' ); - - echo json_encode( $output ); - } -} - -$OAuth2RewritesCode = new OAuth2Rewrites(); - -/** - * Does not seem to be working like it should - * - * @todo Take this activation hook out and rewrite class - * @todo This is not working and will have to see why it is not flushing the rewrites properly - */ -register_activation_hook( __file__, array($OAuth2RewritesCode, 'activate') ); - -/** - * Create all the hooks the link this all together with WordPress - */ -add_filter( 'rewrite_rules_array' , array($OAuth2RewritesCode , 'create_rewrite_rules' )); -add_filter( 'query_vars' , array($OAuth2RewritesCode , 'add_query_vars')); -add_filter( 'admin_init' , array($OAuth2RewritesCode , 'flush_rewrite_rules')); - -/** - * Add action hook to WordPress - * This was just added and seems to work pretty good - * - * @todo Look into this a little more clean layout and hook - */ -add_action( 'template_redirect', array($OAuth2RewritesCode, 'template_redirect_intercept') ); +insert( 'oauth2_options', array( 'version' => $wp_oath2_complete_version, 'enabled' => 1, 'draft'=> '20') ); +} + + +/** + * Run the install of the tables + */ +register_activation_hook(__FILE__,'wp_oauth2_complete_install'); // REGISTER THE CREATION OF THE TABLE +register_activation_hook(__FILE__,'wp_oauth2_complete_install_data'); // REGISTER THE INSTALLATION OF THE INTIAL DATA + + +/** + * OAuth2 Provider WordPress Rewrite rules class + * DON NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING + * + * @since 1.0.0 + */ +class OAuth2Rewrites { + + /** + * Activates the rewrite rules + * + * @todo Run this function only when the plugin is acivated + */ + function activate() { + + /** + * Rewrite Hook + * @global object $wp_rewrite WordPress hook for rewriting pretty URLs + */ + global $wp_rewrite; + + /** + * Flush the rewrites so the changes can take effect + */ + $this->flush_rewrite_rules(); + } + + /** + * Creates the rewrite rules that the plugin needs to function + * @return void + */ + function create_rewrite_rules($rules) { + global $wp_rewrite; + $newRule = array('oauth/(.+)' => 'index.php?oauth='.$wp_rewrite->preg_index(1)); + $newRules = $newRule + $rules; + return $newRules; + } + + /** + * Tell WordPress that we want to include "oauth" as something to look for in the permalinks + * @since 1.0.0 + */ + function add_query_vars($qvars) { + $qvars[] = 'oauth'; + return $qvars; + } + + /** + * Flushes the permalink rules and resets them. + * This adds any new rules into the mix + * + * @since 1.0.0 + */ + function flush_rewrite_rules() { + global $wp_rewrite; + $wp_rewrite->flush_rules(); + } + + /** + * Tells WordPress that when oauth is being called that we want stop and start using the OAuth2 Provider API hook + * + * @since 1.0.0 + */ + function template_redirect_intercept() { + + /** + * @global $wp_query Hooks into WordPress Queries + */ + global $wp_query; + + /** + * Check if "oauth" is found and is so than use OAuth2 Providers hook + * + * @since 1.0.0 + */ + if ($wp_query->get('oauth')) { + require_once(dirname(__FILE__). '/lib/classes/OAuth2_API.php'); + exit; + } + } + + /** + * Creates a JSON output + * + * @since 1.0.0 + * @uses output + * @deprecated Generic Output. Not needed or used not more. Scheduled to be removed 1.0.1 + */ + function pushoutput($message) { + $this->output($message); + } + + function output( $output ) { + header( 'Cache-Control: no-cache, must-revalidate' ); + header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' ); + + // Commented to display in browser. + // header( 'Content-type: application/json' ); + + echo json_encode( $output ); + } +} + +$OAuth2RewritesCode = new OAuth2Rewrites(); + +/** + * Does not seem to be working like it should + * + * @todo Take this activation hook out and rewrite class + * @todo This is not working and will have to see why it is not flushing the rewrites properly + */ +register_activation_hook( __file__, array($OAuth2RewritesCode, 'activate') ); + +/** + * Create all the hooks the link this all together with WordPress + */ +add_filter( 'rewrite_rules_array' , array($OAuth2RewritesCode , 'create_rewrite_rules' )); +add_filter( 'query_vars' , array($OAuth2RewritesCode , 'add_query_vars')); +add_filter( 'admin_init' , array($OAuth2RewritesCode , 'flush_rewrite_rules')); + +/** + * Add action hook to WordPress + * This was just added and seems to work pretty good + * + * @todo Look into this a little more clean layout and hook + */ +add_action( 'template_redirect', array($OAuth2RewritesCode, 'template_redirect_intercept') ); ?> \ No newline at end of file From 73aafbe47f1afbb2c3bde7710094f96ad51649ce Mon Sep 17 00:00:00 2001 From: jgwpk Date: Tue, 16 Apr 2013 10:30:01 +0000 Subject: [PATCH 05/18] Update Admin Link for Delete git-svn-id: http://plugins.svn.wordpress.org/oauth2-provider/trunk@698425 b8457f37-d9ea-0310-8a92-e5e31aec5664 --- lib/classes/admin/OAuthMain.php | 12 ++++++------ lib/dashbaord.php | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/classes/admin/OAuthMain.php b/lib/classes/admin/OAuthMain.php index 9193a9f..4b44708 100644 --- a/lib/classes/admin/OAuthMain.php +++ b/lib/classes/admin/OAuthMain.php @@ -14,7 +14,7 @@ public function __destruct(){} /** * Consumers that are registered in the system * - * @return int Number of Consumers registered in the database + * @return int Number of Consumers Registered in the database */ public function ConsumerCount(){ global $wpdb; @@ -32,11 +32,11 @@ public function listConsumers(){ $results = $wpdb->get_results("SELECT * FROM oauth2_clients"); foreach($results as $single){ print ''; - print ''.$single->name.''; - print ''.$single->client_id.''; - print ''.$single->client_secret.''; - print ''.$single->redirect_uri.''; - print 'Delete'; + print '' . $single->name . ''; + print '' . $single->client_id . ''; + print '' . $single->client_secret . ''; + print '' . $single->redirect_uri . ''; + print 'Delete'; print ''; } diff --git a/lib/dashbaord.php b/lib/dashbaord.php index 7c12eca..b118f50 100644 --- a/lib/dashbaord.php +++ b/lib/dashbaord.php @@ -10,7 +10,7 @@ function wp_oauth2_complete_init_dashboard() { require_once(plugin_dir_path( __FILE__ )."classes/admin/IOAuth2Storage.php"); // INCLUDE OAuth 2.0 STORAGE require_once(plugin_dir_path( __FILE__ )."classes/admin/OAuthMain.php"); // INCLUDE THE OAuth ADMIN OBJECT $oauthStorage = new IOAuth2StorageWP(); // STORAGE OBJECT - $admin = new oauthAdmin(); // ADMIN OB]JECT + $admin = new oauthAdmin(); // ADMIN OBJECT $messageType; // MESSAGE TYPE HOLDER $messagetext; // MESSAGE TEXT HOLDER From edeeaba917d7bd5b39f029082f81298f604d8007 Mon Sep 17 00:00:00 2001 From: jgwpk Date: Tue, 16 Apr 2013 10:33:54 +0000 Subject: [PATCH 06/18] Changed Version fo update notifications to work git-svn-id: http://plugins.svn.wordpress.org/oauth2-provider/trunk@698427 b8457f37-d9ea-0310-8a92-e5e31aec5664 --- readme.txt | 9 ++++++--- wp_oauth2-complete.php | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/readme.txt b/readme.txt index 287c778..1738145 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: http://justin-greer.com/donate Tags: oauth2, OAuth provider, Provider, OAuth, OAuth client, Single Sign On, sso Requires at least: 3.4.2 Tested up to: 3.5.2 -Stable tag: 1.0.2 +Stable tag: 1.0.3 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -100,7 +100,10 @@ When Upgrading OAuth2 Provider I serioulsy recommend creating a backup of your s = 1.0.1 = * Re-worked Readme.txt -* Fixed absolute paths causing 404 Error when WordPress is running under a sub diractory (Useing admin_url() currently) +* Fixed absolute paths causing 404 Error when WordPress is running under a sub directory (Using admin_url() currently) = 1.0.2 = -* Fixed Broken login redirect \ No newline at end of file +* Fixed Broken login redirect + += 1.0.3 = +* Fixed Admin URL links for plugin dashboard \ No newline at end of file diff --git a/wp_oauth2-complete.php b/wp_oauth2-complete.php index 246249e..af7b073 100644 --- a/wp_oauth2-complete.php +++ b/wp_oauth2-complete.php @@ -3,7 +3,7 @@ Plugin Name: WP OAuth2 Complete Plugin URI: http://justin-greer.com/oauth2-provider-complete-wordpress-plugin Description: Allows Wordpress to use OAuth2 structure and become a provider -Version: 1.0.2 +Version: 1.0.3 Author: jgwpk Author URI: http://justin-greer.com License: GPL2 From 5dbda0fa38b382d676f7e7a4308577f266407ae5 Mon Sep 17 00:00:00 2001 From: Joel Wickard Date: Mon, 23 Dec 2013 09:44:36 -0600 Subject: [PATCH 07/18] Update README.md Update readme with contents of original. --- README.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bdf94d1..2a962ef 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,112 @@ -wordpress-oauth -=============== +# OAuth2 Complete For WordPress Plugin that enables a WordPress website to become a provider and Authenticator for Single Sign on and data transfer + +Contributors: jgwpk +Donate link: http://justin-greer.com/donate +Tags: oauth2, OAuth provider, Provider, OAuth, OAuth client, Single Sign On, sso +Requires at least: 3.4.2 +Tested up to: 3.5.2 +Stable tag: 1.0.3 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +Your site will be able provided Single Sign On and also deliver authorized user data using the built in OAuth 2.0 API. + +## Description + +OAuth2 Complete is a ONE OF A KIND plugin that instanly turns your WordPress webste into a valid OAuth v2 Provider. The plugin is built using OAuth2 Draft 20 standards. The backend is designed or extremly easy use for any level of experience. OAuth is a great tool but leaves most developers behind since it a bit technical. +The plugin has aleady done the hard part for you. + +Current Features Features: + +* Allows for Single Sign On Abilities +* Backend Panel for adding Apps/Clients +* 3 Methods pre built in to allow for a plug and play system + +## Installation + +1. Upload `ouath2-complete` to the `/wp-content/plugins/` directory or use the built in plugin install by WordPress +1. Activate the plugin through the 'Plugins' menu in WordPress +1. Click 'Settings' and then 'permalinks'. Then simply click 'Save Changes' to flush the rewrite rules so that OAuth2 Provider +1. Your Ready to Rock + +## Frequently Asked Questions + +### How do I add an APP/Client? + +Visit the OAuth2 Complete dashboard by clicking `Provider` in the WordPress admin panel. Once you are in teh dashboard there is a form to label `Add a Client`. Give your client a name and a redirect URI. The redirect URI is the HTTP location where the user will be returned to after authinicating (Your client should provide this for you). Click `Add Client` and you will that the client has been added to your Client Manager. + +### How does a client connect to my website to use the Single Sign On? + +Currently there is 3 Methods that OAuth2 Provider has built in: + +1. http://example.com/oauth/authorize + +1. http://example.com/oauth/request_token + +1. http://example.com/oauth/request_access + +Authorize requires only 3 parameters: + +* client_id +* response_type - Supported value's = `code` +* state +* Example call `http://example.com/oauth/authorize?client_id=the_client_id&state=anything_you_want&response_type=code` + +Request Token Requires only 4 parameters + +* code - This is auth code returned from the authorize call +* grant_type - Supported value's = `authorization_code` +* client_id +* client_secret +* Example call `http://example.com/oauth/request_token?code=the_auth_key_sent_back_from_the_authorize_call&grant_typ=authorization_code&client_id=the_client_id&client_secret=the_client_secret` + +Request Access Requires only 1 parmeter + +* access_token - This is the access_token provided from the Request Token call +* Example Call `http://example.com/oauth/request_access?access_token=the_token_from_the_request_call` + + +NOTE: All returns will be in JSON format. + +### Is there support for this plugin? Can you help me? + +You can visit our support forum for support. Although it takes the hard part away from dealing with OAuth it will require some knowledge on your behalf. I am glad to help as much as resonibily possible but there has to be a line drawn somewhere. + +### Can you set this up for me on my current website? + +Can I? "YES". But thats a different story. You are more than welcome to contact us with if you should ever need assistance. + +### What information does the a authorized client have access to? + +By default OAuth2 Provider delivers ALL the information about the user that logged in. We are planning on adding a easy to use dashboard to limit data. + +### Do you have a tutorial I can follow ? + +Yes I do. You can view a video titorial here. If you prefer readin then you may have to wait until the full documentation is complete. I are working hard to make it as easy and painless as possible for you to have a premium feature. + +### Where can I download the SDK's for OAuth2 Provider + +You can visit my websiet Here + +## Upgrade Notice + +When Upgrading OAuth2 Provider I serioulsy recommend creating a backup of your site. I will try to create updates that will be flawless. Hopefully any future updates will not chnage to the point where it will stop working. All updates will be ran through multiple tests before being released. In the event that the a upgrade of OAuth2 Provider is realeased and you decide to update, I (Justin Greer) can not and will not be held responsible for any damages done to your website, business, or part that pertains to your website. Upgrade at your OWN RISK! + +## Screenshots + +## Changelog + +### 1.0.0 +*INITIAL BUILD + +### 1.0.1 +* Re-worked Readme.txt +* Fixed absolute paths causing 404 Error when WordPress is running under a sub directory (Using admin_url() currently) + +### 1.0.2 +* Fixed Broken login redirect + +### 1.0.3 +* Fixed Admin URL links for plugin dashboard From ebda86ff4376d6d56dbb89fdd40e036231deeb08 Mon Sep 17 00:00:00 2001 From: Joel Wickard Date: Mon, 23 Dec 2013 09:49:35 -0600 Subject: [PATCH 08/18] Update README.md Fixed formatting. --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2a962ef..cc6fbfa 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,11 @@ Plugin that enables a WordPress website to become a provider and Authenticator for Single Sign on and data transfer -Contributors: jgwpk -Donate link: http://justin-greer.com/donate -Tags: oauth2, OAuth provider, Provider, OAuth, OAuth client, Single Sign On, sso -Requires at least: 3.4.2 -Tested up to: 3.5.2 -Stable tag: 1.0.3 -License: GPLv2 or later +Requires at least: 3.4.2 +Tested up to: 3.5.2 +Stable tag: 1.0.3 +License: GPLv2 or later + License URI: http://www.gnu.org/licenses/gpl-2.0.html Your site will be able provided Single Sign On and also deliver authorized user data using the built in OAuth 2.0 API. From 09bb6cc7ca122f0a224b22ca795bb71e061f9f92 Mon Sep 17 00:00:00 2001 From: Joel Wickard Date: Mon, 23 Dec 2013 09:50:29 -0600 Subject: [PATCH 09/18] Update readme.txt added myself to contributors. --- readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.txt b/readme.txt index 1738145..4972074 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ === OAuth2 Complete For WordPress === -Contributors: jgwpk +Contributors: jgwpk, jwickard Donate link: http://justin-greer.com/donate Tags: oauth2, OAuth provider, Provider, OAuth, OAuth client, Single Sign On, sso Requires at least: 3.4.2 @@ -106,4 +106,4 @@ When Upgrading OAuth2 Provider I serioulsy recommend creating a backup of your s * Fixed Broken login redirect = 1.0.3 = -* Fixed Admin URL links for plugin dashboard \ No newline at end of file +* Fixed Admin URL links for plugin dashboard From 12a12eeefa9701c8dc1ad85c3e1c6844caf584c8 Mon Sep 17 00:00:00 2001 From: Joel Wickard Date: Mon, 23 Dec 2013 10:15:58 -0600 Subject: [PATCH 10/18] fixed short tag in api --- lib/classes/OAuth2_API.php | 2 +- lib/classes/log.txt | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/classes/OAuth2_API.php b/lib/classes/OAuth2_API.php index 6b95710..ff20c01 100644 --- a/lib/classes/OAuth2_API.php +++ b/lib/classes/OAuth2_API.php @@ -446,6 +446,6 @@ function oauth2LoginLayout(){ - \ No newline at end of file diff --git a/lib/classes/log.txt b/lib/classes/log.txt index 5643e8f..5ed0cc9 100644 --- a/lib/classes/log.txt +++ b/lib/classes/log.txt @@ -18,3 +18,19 @@ Incomming Connection:Wed March 20 at 10:43:15 pm Method Being Called: login/style.css http://oauth2-plugin.dev/oauth/login/ ========================= +Incomming Connection:Mon December 23 at 4:14:50 pm +Method Being Called: authorize +http://localhost:3000/users/sign_in +q: /oauth/authorize/ +response_type: code +client_id: e6f7d751667badb262bae8a371c50ab5b9556e72 +redirect_uri: http://localhost:3000/users/auth/mca/callback +state: e989075f80882091a1c71619a1db4bef049daf2656abe6da +========================= +Incomming Connection:Mon December 23 at 4:14:50 pm +Method Being Called: login +http://localhost:3000/users/sign_in +q: /oauth/login/ +sso_redirect: e6f7d751667badb262bae8a371c50ab5b9556e72 +state: e989075f80882091a1c71619a1db4bef049daf2656abe6da +========================= From a7680127884d0644e623ea45cce5eebd092dfe5c Mon Sep 17 00:00:00 2001 From: Joel Wickard Date: Mon, 23 Dec 2013 10:20:02 -0600 Subject: [PATCH 11/18] remove log file --- lib/classes/log.txt | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 lib/classes/log.txt diff --git a/lib/classes/log.txt b/lib/classes/log.txt deleted file mode 100644 index 5ed0cc9..0000000 --- a/lib/classes/log.txt +++ /dev/null @@ -1,36 +0,0 @@ -Incomming Connection:Wed March 20 at 10:42:59 pm -Method Being Called: ogin - -========================= -Incomming Connection:Wed March 20 at 10:43:05 pm -Method Being Called: login - -========================= -Incomming Connection:Wed March 20 at 10:43:05 pm -Method Being Called: login/style.css -http://oauth2-plugin.dev/oauth/login/ -========================= -Incomming Connection:Wed March 20 at 10:43:15 pm -Method Being Called: login -http://oauth2-plugin.dev/oauth/login/ -========================= -Incomming Connection:Wed March 20 at 10:43:15 pm -Method Being Called: login/style.css -http://oauth2-plugin.dev/oauth/login/ -========================= -Incomming Connection:Mon December 23 at 4:14:50 pm -Method Being Called: authorize -http://localhost:3000/users/sign_in -q: /oauth/authorize/ -response_type: code -client_id: e6f7d751667badb262bae8a371c50ab5b9556e72 -redirect_uri: http://localhost:3000/users/auth/mca/callback -state: e989075f80882091a1c71619a1db4bef049daf2656abe6da -========================= -Incomming Connection:Mon December 23 at 4:14:50 pm -Method Being Called: login -http://localhost:3000/users/sign_in -q: /oauth/login/ -sso_redirect: e6f7d751667badb262bae8a371c50ab5b9556e72 -state: e989075f80882091a1c71619a1db4bef049daf2656abe6da -========================= From ef5ffc03f94feb3fcad906cbb1fa66e08313ab01 Mon Sep 17 00:00:00 2001 From: Joel Wickard Date: Tue, 24 Dec 2013 12:47:43 -0600 Subject: [PATCH 12/18] filter out password / activation key --- lib/classes/OAuth2_API.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/classes/OAuth2_API.php b/lib/classes/OAuth2_API.php index ff20c01..822a80c 100644 --- a/lib/classes/OAuth2_API.php +++ b/lib/classes/OAuth2_API.php @@ -141,6 +141,11 @@ global $wpdb; $info = $wpdb->get_row("SELECT * FROM wp_users WHERE ID = ".$user_id.""); + + //don't send sensitive info accross the wire. + unset($info->user_pass); + unset($info->user_activation_key); + header('Cache-Control: no-cache, must-revalidate'); header('Content-type: application/json'); print_r(json_encode($info)); @@ -216,7 +221,7 @@ function oauth2LoginLayout(){ <?php print bloginfo('name'); ?> - Authorization Login - +