Commit 961f5410 authored by Andreas Åkre Solberg's avatar Andreas Åkre Solberg

Major update app.

parent 57075b90
Pipeline #6843 failed with stages
in 3 minutes and 36 seconds
{
"extends": "react-app",
"parser": "babel-eslint"
"parser": "babel-eslint",
"rules": {
"strict": 0,
"jsx-a11y/href-no-hash": "off",
"jsx-a11y/anchor-is-valid": ["warn", { "aspects": ["invalidHref"] }]
}
}
......@@ -3,3 +3,4 @@ npm-debug.log
dataporten-resources
public
.env
LOCAL.md
# OAuth Play
# ORCID Proof of concept service
......
......@@ -1186,7 +1186,6 @@ define('jso',['require','exports','module','./store','./utils','./Config'],funct
}
if (callback && typeof callback === 'function') {
utils.log("About to store a callback for later with state=" + request.state, callback);
JSO.internalStates[request.state] = callback;
......
This diff is collapsed.
{
"name": "oauth-play",
"description": "OAuth Play",
"name": "orcid",
"description": "ORCID Service",
"version": "0.1.0",
"main": "",
"scripts": {
......@@ -12,17 +12,24 @@
"license": "MIT",
"devDependencies": {
"babel-core": "^6.7.2",
"babel-eslint": "^7.2.3",
"babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-2": "^6.24.1",
"css-loader": "^0.23.1",
"eslint": "^3.19.0",
"eslint-config-react-app": "^2.1.0",
"eslint-plugin-flowtype": "^2.46.1",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"file-loader": "^0.11.2",
"html-webpack-plugin": "^2.30.1",
"style-loader": "^0.13.0",
"url-loader": "^0.5.9",
"webpack": "^3.5.0",
"webpack-dev-server": "^2.7.1"
"webpack": "^3.11.0",
"webpack-dev-server": "^2.11.2"
},
"dependencies": {
"axios": "^0.16.2",
......
import { createActions} from 'redux-actions'
const actionCreators = createActions({
discovery: {
start: hostname => (hostname),
completed: payload => (payload)
authentication: {
success: payload => (payload),
logout: payload => (payload)
},
serverConfig: {
save: config => (config)
......@@ -11,9 +11,18 @@ const actionCreators = createActions({
tokens: {
save: x => (x)
},
http: {
orcids: {
start: x => (x),
completed: x => (x)
completed: x => (x),
search: x => (x),
searchResult: x => (x)
},
orcidauth: {
success: x => (x),
loggedout: x => (x)
},
embedded: {
set: x => (x)
}
});
......
......@@ -15,7 +15,7 @@ const Footer = () => (
<div className="footer-logo" >
<img src={uninettLogo} alt="Uninett logo" type="image/svg+xml" style={stylex} />
</div>
<div className="footer-uninett-department">UNINETT AS 2017</div>
<div className="footer-uninett-department">UNINETT AS 2018</div>
</div>
<div className="clearfix"></div>
</div>
......
import React, { PureComponent } from 'react'
import {DropdownButton, Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon, Jumbotron, Button} from 'react-bootstrap'
import {Row, Col, Collapse, FormControl, FormGroup, ControlLabel, HelpBlock, PanelGroup, Panel} from 'react-bootstrap'
import FontAwesome from 'react-fontawesome'
import HostnameController from '../containers/HostnameController'
import ServerConfigController from '../containers/ServerConfigController'
import WhiteBox from './WhiteBox'
import moment from 'moment'
import 'moment/locale/nb';
// import 'moment/locale/en';
moment.locale("en");
export function expiresText(expires) {
var expiresM = moment.unix(expires)
return expiresM.fromNow()
}
class Component extends PureComponent {
constructor(props) {
super(props)
this.state = {
customEndpoint: ""
}
}
updateAPIendpoint(e) {
this.setState({customEndpoint: e.target.value})
}
fetchUserinfo() {
let endpoint = this.props.serverConfig.userinfo_endpoint;
// console.error("Get userinfo from ", endpoint)
this.props.httpRequestStart(endpoint)
}
fetchCustom() {
let endpoint = this.state.customEndpoint
// console.error("Get data from ", endpoint)
this.props.httpRequestStart(endpoint)
}
preselect(key, event) {
// console.log("Key", key, "event", event)
this.setState({customEndpoint: key})
}
renderDropdownButton() {
const i = "drpdwn"
const title = "Load preset URL"
// console.log("PROPOS", this.props)
if (this.props.serverConfig && this.props.serverConfig.authorization_endpoint && this.props.serverConfig.authorization_endpoint === 'https://auth.dataporten.no/oauth/authorization') {
let links = []
links.push({
url: "https://auth.dataporten.no/userinfo",
title: "Basic userinfo endpoint "
})
const cntrl = /^gk_([^_]+)$/i
this.props.token.scopes.forEach((scope) => {
if (scope === 'groups') {
links.push({
url: "https://groups-api.dataporten.no/groups/me/groups",
title: "Dataporten groups API: My groups"
})
}
if (scope === 'openid' && this.props.serverConfig.userinfo_endpoint) {
links.push({
url: this.props.serverConfig.userinfo_endpoint,
title: "OpenID Userinfo endpoint "
})
}
let res = cntrl.exec(scope)
if (res) {
links.push({
url: "https://" + res[1] + ".dataporten-api.no/",
title: "Dataporten APIGK " + res[1]
})
// console.log(res)
}
})
let menuitems = links.map((link) => {
return (
<MenuItem key={link.url} eventKey={link.url}>{link.title}</MenuItem>
)
})
return (
<DropdownButton bsStyle="default" title={title} key={i} id={`dropdown-basic-${i}`} onSelect={this.preselect.bind(this)}>
{menuitems}
</DropdownButton>
);
}
return null
}
render() {
// console.error("Component", this.props)
if (!this.props.token) {
return null
}
let userinfo = null
// if (this.props.serverConfig.userinfo_endpoint) {
// userinfo = (
// <div>
// <p>Fetch userinfo from <code>{this.props.serverConfig.userinfo_endpoint}</code></p>
// <Button onClick={this.fetchUserinfo.bind(this)} bsStyle="default">
// <FontAwesome name='arrow-circle-o-right' /> Get userinfo
// </Button>
// </div>
// )
// }
return (
<Panel header="HTTP Client using OAuth Access Token" eventKey="1" collapsible={true} defaultExpanded={true}>
{userinfo}
<div>
<p>Fetch data from</p>
{this.renderDropdownButton()}
<FormGroup
className="gutter"
controlId="formBasicText"
>
<FormControl
type="text"
bsSize="large"
placeholder="http://httpjs.net/foo"
value={this.state.customEndpoint}
onChange={this.updateAPIendpoint.bind(this)}
/>
<HelpBlock>Fill out the hostname of your OAuth server. Will only work if your OAuth server support OpenID Connect Discovery. If not, please enter configuration manually.</HelpBlock>
</FormGroup>
<Button onClick={this.fetchCustom.bind(this)} bsStyle="default">
<FontAwesome name='arrow-circle-o-right' /> Perform HTTP call
</Button>
</div>
</Panel>
)
}
}
export default Component
import React, { PureComponent } from 'react'
import {Table, Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon, Jumbotron, Button} from 'react-bootstrap'
import {Alert, Row, Col, Collapse, FormControl, FormGroup, ControlLabel, HelpBlock, PanelGroup, Panel} from 'react-bootstrap'
import FontAwesome from 'react-fontawesome'
import HostnameController from '../containers/HostnameController'
import ServerConfigController from '../containers/ServerConfigController'
import WhiteBox from './WhiteBox'
import LoadingIndicator from '../components/LoadingIndicator'
import moment from 'moment'
import 'moment/locale/nb';
// import 'moment/locale/en';
moment.locale("en");
export function expiresText(expires) {
var expiresM = moment.unix(expires)
return expiresM.fromNow()
}
class Component extends PureComponent {
updateAPIendpoint() {
}
fetchUserinfo() {
let endpoint = this.props.serverConfig.userinfo_endpoint;
// console.error("Get userinfo from ", endpoint)
this.props.httpRequestStart(endpoint)
}
fetchCustom() {
}
handleAlertDismiss() {
}
getErrorMessage() {
if (this.props.http.error) {
return (
<Alert bsStyle="danger" onDismiss={this.handleAlertDismiss}>
<h4>Error performing HTTP request</h4>
<p>{this.props.http.error}</p>
</Alert>
)
}
return null
}
render() {
// console.error("Component", this.props)
if (!this.props.http) {
return (
<p></p>
)
}
let headers = []
if (this.props.http.response && this.props.http.response.headers) {
this.props.http.response.headers.forEach((item) => {
headers.push((
<tr key={item.key}>
<td>{item.key}</td>
<td>{item.value}</td>
</tr>
))
})
}
let bodystr = this.props.http.response ? JSON.stringify(this.props.http.response.body, undefined, 2) : ''
let loading = null
if (this.props.http.isLoading) {
loading = (
<LoadingIndicator />
)
}
return (
<Panel header="HTTP Response" eventKey="1" collapsible={true} defaultExpanded={true}>
<div>
<p><code>GET</code> request to <code>{this.props.http.url}</code></p>
{ loading }
{this.getErrorMessage()}
<Table striped bordered condensed hover>
<tbody>
{headers}
</tbody>
</Table>
<pre>{ bodystr }</pre>
</div>
</Panel>
)
}
}
export default Component
......@@ -9,7 +9,7 @@ var FontAwesome = require('react-fontawesome')
const navStyle = {
marginTop: "17px"
marginTop: "13px"
}
// const checkActive = (currentLocation, check) => {
......@@ -27,7 +27,7 @@ const Header = ({}) => (
</Navbar.Brand>
</Navbar.Header>
<ul style={navStyle} className="nav nav-pills">
<li>OAuth Play <FontAwesome name='play-circle-o' /></li>
<li>Dataporten ORCID <FontAwesome name='flask' /></li>
</ul>
</Navbar>
)
......
......@@ -2,11 +2,8 @@ import React from 'react'
import {Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon, Jumbotron, Button} from 'react-bootstrap'
import {Row, Col, Collapse, FormControl, FormGroup, ControlLabel, HelpBlock, PanelGroup, Panel} from 'react-bootstrap'
import HostnameController from '../containers/HostnameController'
import ServerConfigController from '../containers/ServerConfigController'
import TokenViewContainer from '../containers/TokenViewContainer'
import HTTPFetchContainer from '../containers/HTTPFetchContainer'
import HTTPResponseContainer from '../containers/HTTPResponseContainer'
import ORCIDController from '../containers/ORCIDController'
import SearchController from '../containers/SearchController'
import WhiteBox from './WhiteBox'
......@@ -14,29 +11,11 @@ import WhiteBox from './WhiteBox'
const MainContent = () => (
<div>
<div className="container">
<Jumbotron className="uninett-color-lightBlue">
<h1>Play with OAuth 2.0</h1>
<p>Learn more about how OAuth 2.0 works, and explore OAuth protected APIs without entering any code.</p>
</Jumbotron>
<WhiteBox>
<HostnameController />
<ServerConfigController />
<TokenViewContainer />
<HTTPFetchContainer />
<HTTPResponseContainer />
</WhiteBox>
{/* <WhiteBox> */}
<ORCIDController />
<SearchController />
{/* </WhiteBox> */}
</div>
</div>
)
......
import React, { PureComponent } from 'react'
import {Alert, InputGroup, Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon, Jumbotron, Button} from 'react-bootstrap'
import {Panel, Row, Col, Collapse, FormControl, FormGroup, ControlLabel, HelpBlock} from 'react-bootstrap'
import FontAwesome from 'react-fontawesome'
import WhiteBox from './WhiteBox'
class Component extends PureComponent {
constructor(props) {
super(props)
// console.log("--- props", this.props)
// With these Location object properties you can access all of these URL components
//
// hash - Sets or returns the anchor portion of a URL.
// host - Sets or returns the hostname and port of a URL.
// hostname - Sets or returns the hostname of a URL.
// href - Sets or returns the entire URL.
// pathname - Sets or returns the path name of a URL.
// port - Sets or returns the port number the server uses for a URL.
// protocol - Sets or returns the protocol of a URL.
// search - Sets or returns the query portion of a URL
var currentURL = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : '' ) + '/callback'
// console.log("currentURL", currentURL)
this.state = {
client_id: "6233aedf-f08a-4112-9a1b-f33c3cd9b396",
// clientSecret: "",
redirectURL: currentURL,
// scopes: "",
// response_type: "token"
}
}
authenticateStart() {
let config = Object.assign({}, this.props.serverConfig, this.state)
console.error("Authenticaiton start with config", config)
this.props.authenticateStart(config)
}
logoutStart() {
this.props.logoutStart()
}
updateFieldHandler(field) {
return (e) => {
const upd = {
[field]: e.target.value
}
this.props.updateConfig(upd)
// this.setState(upd)
}
}
render() {
// console.log("X This props", this.props)
// console.log("X This state", this.state)
let authorizationEndpoint = ''
if (this.props.serverConfig) {
authorizationEndpoint = this.props.serverConfig.authorization_endpoint
}
let tokenEndpoint = ''
if (this.props.serverConfig) {
tokenEndpoint = this.props.serverConfig.token_endpoint
}
let userinfoEndpoint = ''
if (this.props.serverConfig) {
userinfoEndpoint = this.props.serverConfig.userinfo_endpoint
}
let clientId = ''
if (this.props.serverConfig && typeof this.props.serverConfig.client_id !== 'undefined') {
clientId = this.props.serverConfig.client_id
this.state.client_id = this.props.serverConfig.client_id
console.error("Setting clientid to serverconfig", clientId)
} else if (this.state && this.state.client_id) {
clientId = this.state.client_id
}
let clientSecret = ''
let scopes = ''
if (this.props.serverConfig) {
scopes = this.props.serverConfig.scopes
}
let expanded = this.props.expanded && !!this.props.serverConfig
return (
<Panel header="OAuth Server Configuration" eventKey="2" collapsible={true} defaultExpanded={expanded}>
<form>
<h4>OAuth Authorization endpoint</h4>
<FormGroup>
<FormControl id="inputAuthorization" type="text" value={authorizationEndpoint} onChange={this.updateFieldHandler.bind(this)("authorization_endpoint")} />
</FormGroup>
<h4>OAuth Token endpoint</h4>
<FormGroup>
<FormControl id="inputToken" type="text" value={tokenEndpoint} onChange={this.updateFieldHandler.bind(this)("token_endpoint")} />
</FormGroup>
<h4>Userinfo Endpoint</h4>
<FormGroup>
<FormControl id="inputUserinfo" type="text" value={userinfoEndpoint} onChange={this.updateFieldHandler.bind(this)("userinfo_endpoint")} />
</FormGroup>
<h4>Client ID</h4>
<FormGroup>
<FormControl id="inputClientid" type="text" bsSize="large" value={clientId} onChange={this.updateFieldHandler.bind(this)("client_id")} />
</FormGroup>
{/*
<h4>Client secret</h4>
<FormGroup>
<FormControl type="text" bsSize="large" value={clientSecret} onChange={this.updateFieldHandler.bind(this)("client_secret")} />
</FormGroup>
*/}
<h4>Scopes</h4>
<FormGroup>
<FormControl type="text" bsSize="large" value={scopes} onChange={this.updateFieldHandler.bind(this)("scopes")} />
</FormGroup>
<FormGroup controlId="response_type">
<ControlLabel>Response Type</ControlLabel>
<FormControl componentClass="select" placeholder="select" value={this.state.response_type} onChange={this.updateFieldHandler.bind(this)("response_type")}>
<option value="token">OAuth Implicit Grant (token)</option>
<option value="id_token token">OpenID Connect Implicit Flow (id_token token)</option>
</FormControl>
</FormGroup>
<h4>Redirect URI</h4>
<p>When you configure your client, please register the following redirect uri:</p>
<p><code>{this.state.redirectURL}</code></p>
<div>
<Button id="btnAuthenticate" onClick={this.authenticateStart.bind(this)} bsStyle="primary">
<FontAwesome name='sign-in' /> Authenticate
</Button>
&nbsp;
<Button id="btnWipe" onClick={this.logoutStart.bind(this)} bsStyle="danger">
<FontAwesome name='sign-out' /> Wipe token
</Button>
</div>
</form>
</Panel>
)
}
}
export default Component
import React, { PureComponent } from 'react'
import {Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon, Jumbotron, Button} from 'react-bootstrap'
import {Row, Col, Collapse, FormControl, FormGroup, ControlLabel, HelpBlock, PanelGroup, Panel} from 'react-bootstrap'
import HostnameController from '../containers/HostnameController'
import ServerConfigController from '../containers/ServerConfigController'
import WhiteBox from './WhiteBox'
import jwtDecode from 'jwt-decode'
import moment from 'moment'
import 'moment/locale/nb';
// import 'moment/locale/en';
moment.locale("en");
export function expiresText(expires) {
var expiresM = moment.unix(expires)
return expiresM.fromNow()
}
class Component extends PureComponent {
getIDTokenView(idtoken) {
let str = JSON.stringify(jwtDecode(idtoken), undefined, 4)
return (
<div>
<h4>ID Token decoded</h4>
<pre>{str}</pre>
</div>
)
}
render() {
// console.error("Component", this.props)
if (!this.props.token) {
return (
<p>No token</p>
)
}
let idtokenView = null
let extxt = expiresText(this.props.token.expires)
if (this.props.token.id_token) {
idtokenView = this.getIDTokenView(this.props.token.id_token)
}
return (
<Panel header="Access Token" eventKey="1" collapsible={true} defaultExpanded={true}>
<p>This access token expires {extxt}</p>
<pre>{this.props.tokenstring}</pre>
{idtokenView}
</Panel>
)
}
}
export default Component
import {Grid, Well,Row, Col, Button, NavItem, Media} from 'react-bootstrap'
import { connect } from 'react-redux'
import React from 'react'
import API from '../utils/API'
import FontAwesome from 'react-fontawesome'
const getPhoto = (props) => {
return 'https://api.dataporten.no/userinfo/v1/user/media/' + props.authentication.user.profilephoto
}
class PureComponent extends React.PureComponent {
getLoginInProgress() {
return (
<div><FontAwesome name='circle-o-notch' spin /> Logging in…</div>
)
}
getLoginButton() {
return (
<NavItem eventKey={1} onClick={this.props.actLogin}>Login</NavItem>
)
}
getAuthComponent() {
var xstyle = {
border: "1px solid #aaa",
'borderRadius': '5px',
'backgroundColor': '#fff',
padding: '3px'
}
var ystyle = {
'paddingTop': '5px'
}
var zstyle= {
'margin': '0px', 'padding': '0px'
}